要对已经安装的APP加锁,也就说不能改动这个APP
而要实现这个功能,可以偷巧,在要启动的APP即将启动之前,进入加锁的activity
经典的功能watchdog,看看用户触动了哪个功能,很多软件里都有,尤其是安全软件
不能写在activity里,生命周期的问题,需要启动一个service,在后台监听
manifest注册
<service android:name="com.example.watchdogdemo.WatchDogService"></service>
public class WatchDogService extends Service {
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
}
在main里启用
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent service = new Intent(this,WatchDogService.class);
startService(service);
}
启动后时时刻监视哪个程序被启用
service里一般不能用while(true)来判断,这样很容易阻塞,应该在外面加一个thread
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// TODO Auto-generated method stub
new Thread(new Runnable(){
@Override
public void run(){
// TODO Auto-generated method stub
while(true){
//获取当前程序,若是加锁的,则启动另一个activity
//给CPU一个时间去执行别的程序
try {
Thread.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
}
由于要动态监测系统的服务,这里需要权限
这里要获得当前被启动的APP,这就需要任务栈,要任务栈就需要activity管理器
任务栈里面按照执行顺序存放着activity,当你拿一个时,就是当前的activity
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//通过activityManager拿到系统的service
<span style="background-color: rgb(255, 102, 102);">ams = (ActivityManager) getSystemService(ACTIVITY_SERVICE);</span>
new Thread(new Runnable(){
@Override
public void run(){
// TODO Auto-generated method stub
while(true){
//获取当前程序,若是加锁的,则启动另一个activity
//task任务栈,里面放的是activity
List<RunningTaskInfo> runningTasks = ams.getRunningTasks(3);
for(RunningTaskInfo runningTaskInfo : runningTasks){
String packagename = runningTaskInfo.baseActivity.getPackageName();
//每隔五秒打印一次
System.out.println("WatchDogService.onStartCommand()"+packagename);
}
//给CPU一个时间去执行别的程序
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
如何知道哪一个被我们加锁的的程序被启动呢?以短信为例
判断一下
//每隔五秒打印一次
System.out.println("WatchDogService.onStartCommand()"+packagename);
if("com.android.mms".equals(packagename)){
//启动我们的watchdog的mainActivity
}
}
//给CPU一个时间去执行别的程序
注意,由于是service跳转到activity,需要设置一个flag,activity间的跳转不需要
if("com.android.mms".equals(packagename)){
//启动我们的watchdog的mainActivity
Intent intent = new Intent(WatchDogService.this,MainActivity.class);
//由于是由service跳转到activity,service没有任务栈,他需要一个flag
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
跳转成功后,在watchdog的activity上可以加上一些东西,输入密码解锁
现在有个问题,跳到watchdog后,退回,又会跳回来
需要在main里加一个onBackPressed
@Override
public void onBackPressed() {
// 跳回到Home界面
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
startActivity(intent);
super.onBackPressed();
}
让用户选择,在setting里加入一条自定义控件
<com.rjl.mobilephonemanager.ui.SettingItem
android:id="@+id/settingitem_watchdog"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
rjl:itemtitle="软件锁"
rjl:desc_checkbox_on="开启软件锁"
rjl:desc_checkbox_off="关闭软件锁"/>
声明
private SettingItem settingItem_watchdog;
找到
settingItem_watchdog = (SettingItem) findViewById(R.id.settingitem_watchdog);
初始化
private void initWatchDog() {
// TODO Auto-generated method stub
settingItem_watchdog.setCheck(ServiceUtils.isRunning(this, "com.rjl.mobilephonemanager.service.WatchDogService"));
settingItem_watchdog.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
if (settingItem_watchdog.getChecked()) {
settingItem_watchdog.setCheck(false);
settingItem_watchdog.setdescriptionoff();
Intent intent = new Intent (SettingActivity.this, WatchDogService.class);
stopService(intent);
}
else {
settingItem_watchdog.setCheck(true);
settingItem_watchdog.setdescriptionon();
Intent intent = new Intent (SettingActivity.this,WatchDogService.class);
startService(intent);
}
}
});
}
调用
@Override
protected void onResume() {
// TODO Auto-generated method stub
initAutoUpdateItem( );
initShowAddressItem();
initSetToastBgItem();
initSetToastPostionItem();
intiSetBlackNumItem();
initWatchDog();
System.out.println("SettingActivity.onResume()");
super.onResume();
}
这个过程中,manifest里加上了权限、activity、service
现在来完善上锁 判断的时候应该获取包名,看他是不是加锁了,没加锁就锁上,锁了就不动
需要在软件管理的list里面操作下
长按加锁、解锁
item_appmanager_appinfo里加一个imageview
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:id="@+id/iv_watchdog_lockstatus"
android:src="@drawable/unlock"
android:layout_alignParentRight="true"
android:layout_marginTop="10dp"/>
APPmanageractivity里加上OnItemLongClickListener
lv_appmanage_appinfolist.setOnItemLongClickListener(new OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view,
int position, long id) {
// TODO Auto-generated method stub
return false;
}
});
refreshData();
也是通过position获得APPinformation
现在实现的功能是长按换图标
private ImageView iv_watchdog_lockstatus;
我们要把 应用 上锁状态改变的情况放到数据库,应用名,上锁情况
openhelper
public class WatchDogOpenHelper extends SQLiteOpenHelper {
public WatchDogOpenHelper(Context context, String name,
CursorFactory factory, int version) {
super(context, name, factory, version);
// TODO Auto-generated constructor stub
}
@Override
public void onCreate(SQLiteDatabase db) {
// TODO Auto-generated method stub
String sql = "create table watchdoginfo (_id integer primary key autoincrement, packagename varchar(50) )";
db.execSQL(sql);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
}
}
dao接口
</pre><pre name="code" class="java">public class WatchDogDao {
//增删改查
SQLiteDatabase db ;
WatchDogOpenHelper helper ;
Context ctx ;
public WatchDogDao(Context ctx) {
this.ctx =ctx;
helper = new WatchDogOpenHelper(ctx, "WatchDog.db", null, 1);
this.db = helper.getReadableDatabase();
}
//add
public void addPackageName(String name ){
//db.execSQL(sql);
ContentValues values = new ContentValues();
values.put("packagename", name);
db.insert("watchdoginfo", null, values);
}
//delete
public int deletepackagename(String packagename){
int i = db.delete("watchdoginfo", "packagename = ? ", new String[]{packagename});
return i;
}
//query
public boolean queryLockStatus(String packagename){
boolean flag = false;
Cursor cursor= db.query("watchdoginfo", null, "packagename = ?", new String[]{packagename}, null, null, null);
if (cursor.moveToNext()) {
flag=true;
}
return flag;
}
}
APPmanageractivity里初始化dao
dao = new WatchDogDao(this);
注意不能给自己加锁
<pre name="code" class="java">//软件锁换图标
lv_appmanage_appinfolist.setOnItemLongClickListener(new OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view,
int position, long id) {
// TODO Auto-generated method stub
AppInfo appinfo_longclick;
if (position< user_appinfolist.size()+1) {
appinfo_longclick = user_appinfolist.get(position-1);
}else {
appinfo_longclick = sys_appinfolist.get(position-user_appinfolist.size()-2);
}
iv_watchdog_lockstatus = (ImageView) view.findViewById(R.id.iv_watchdog_lockstatus);
if (dao.queryLockStatus(appinfo_longclick.getPackagename())) {
iv_watchdog_lockstatus.setImageResource(R.drawable.unlock);
dao.deletepackagename(appinfo_longclick.getPackagename());
} else {
if (!appinfo_longclick.getPackagename().equals(getPackageName())) {
iv_watchdog_lockstatus.setImageResource(R.drawable.lock);
dao.addPackageName(appinfo_longclick.getPackagename());
}else {
Toast.makeText(AppManagementActivity.this, "不能给手机管家加锁!", 1).show();
}
}
return true;
}
});
回显问题首先 holder复用
<pre name="code" class="java">holder.iv_watchdog_lockstatus = (ImageView) item_layout.findViewById(R.id.iv_watchdog_lockstatus);
//3.添加进view
item_layout.setTag(holder);
}
holder.iv_appmanage_icon.setImageDrawable(appInfo.getIcon());
holder.tv_appmanage_appname.setText(appInfo.getAppname());
if(appInfo.isIs_sdcard()){
holder.tv_appmanage_applocation.setText("SD卡存储");
}else{
holder.tv_appmanage_applocation.setText("手机内存");
}
return item_layout;
}
//1.Holder类搞定
class Holder {
ImageView iv_appmanage_icon ;
TextView tv_appmanage_appname;
TextView tv_appmanage_applocation;
ImageView iv_watchdog_lockstatus ;
}
}
其次 默认未上锁,要查询是否上锁
//查询软件是否上锁
if (dao.queryLockStatus(appInfo.getPackagename())) {
holder.iv_watchdog_lockstatus.setImageResource(R.drawable.lock);
}else {
holder.iv_watchdog_lockstatus.setImageResource(R.drawable.unlock);
}
来完成跳转后的判断上锁问题
要根据数据库中的状态来判断是否要上锁
private WatchDogDao dao;
dao = new WatchDogDao(this);
if (dao.queryLockStatus(packagename)){
//启动我们的watchdog的mainActivity
Intent intent = new Intent(WatchDogService.this,WatchDogActivity.class);
//由于是由service跳转到activity,service没有任务栈,他需要一个flag
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
完善用户体验
跳转后显示之前的软件名和图标
watchdog的activity里要获取到包名,由于是从service里跳过来的,要在service里塞一个
if (dao.queryLockStatus(packagename)){
//启动我们的watchdog的mainActivity
Intent intent = new Intent(WatchDogService.this,WatchDogActivity.class);
//带上包名,方便在watchdog里接收显示
intent.putExtra("packageName", packagename);
//由于是由service跳转到activity,service没有任务栈,他需要一个flag
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
<pre name="code" class="java">I
public class WatchDogActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_watchdog);
ImageView iv_watchdog_icon = (ImageView) findViewById(R.id.iv_watchdog_icon);
TextView tv_watchdog_name = (TextView) findViewById(R.id.tv_watchdog_name);
//获取某个一应用的icon和name 实际上只需要我们拿到该应用的包名就可以
Intent intent = getIntent();
final String packageName = intent.getStringExtra("packageName");
System.out.println("WatchDogActivity.onCreate()"+packageName);
if (packageName!=null) {
//获取当前锁定的应用的图标和 名字
PackageManager pm = getPackageManager();
try {
ApplicationInfo applicationInfo = pm.getApplicationInfo(packageName, 0);
String name = (String) applicationInfo.loadLabel(pm);
Drawable icon= applicationInfo.loadIcon(pm);
iv_watchdog_icon.setImageDrawable(icon);
tv_watchdog_name.setText(name);
} catch (NameNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
button响应
Button bt_watchdog_confirm = (Button) findViewById(R.id.bt_watchdog_confirm);
final EditText et = (EditText) findViewById(R.id.et_watchdog_password);
<pre name="code" class="java">bt_watchdog_confirm.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//判断用户输入的密码 跟我们预设的是否一致,如果是一致的话,放行,否则吐司
String password = et.getText().toString();
if (!password.isEmpty()&&"123".equals(password)) {
finish();
}else {
Toast.makeText(getApplicationContext(), "密码不正确", 0).show();
}
}
});
OK,有几个bug
进入MobilphoneManager的软件管理,然后点击home
这时候他去了后台,没有清空,这时候任务栈里有两个activity,下面是home,上面是软件管理
现在点一个APP,这时应该跳转到看门狗,也就是在之前的任务栈里又加了一个,这时候填密码点击,等于是把自己,看门狗销毁了
这时候回显的是之前的两个activity,点击两次退回才能看到被点击的APP
要解决这个问题,我们最好能单独给看门狗起一个任务栈
在manifest里给看门狗声明的部分加一个launchmode:singleInstance
<activity android:name=".WatchDogActivity"
android:launchMode="singleInstance">
</activity>
<!--
standard : 每次启动Activity的时候,都新建一个放到任务栈
singleTop: 如果该Activity在栈顶,就不会创建新的Activity
singleTask: 如果当前的Activity已经在任务栈里面 ,就将其上面的Activity都干掉,让其在栈顶显示
singleInstance: 开启一个新的任务栈, 将Activity放入
-->
另一个问题,解锁后循环加锁解锁
这时候解完锁要设置一个标记,而不是直接销毁
C++ 什么的设置一个全局变量就好
这里考虑activity发一个广播 service里来一个接收者
if (!password.isEmpty()&&"123".equals(password)) {
//不能直接销毁,而是给一个广播,否则不断解锁加锁死循环,自定义的内容
Intent intent = new Intent("com.rjl.mobilemanager.unlock");
intent.putExtra("packageName", packageName);
sendBroadcast(intent);
finish();
}else {
class MyUnlockReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
}
}
动态注册
public int onStartCommand(Intent intent, int flags, int startId) {
//通过activityManager拿到系统的service
ams = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
dao = new WatchDogDao(this);
IntentFilter filter = new IntentFilter("com.rjl.mobilephonemanager.unlock");
registerReceiver(new MyUnlockReceiver(), filter);
这时候一解锁就发消息,此时就可以判断
先接收
lass MyUnlockReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
unlockPackage = intent.getStringExtra("packageName");
}
}
判断之
if (!packagename.equals(unlockPackage) && dao.queryLockStatus(packagename)){
另一个bug
对某APP加锁密码输入不对,直接按home键出来了,然后再进一个app,发现图标和名称不对,显示的是前一个APP
还是任务栈和生命周期的问题,按home键后,先onpause,然后onstop,看不见了,然后进去,直接onrestart,onresume,没有oncreate,图标不会更新
把相关代码放到onresume里,或者在onstop里干掉自己,一下次就会oncreate
watchdogactivity
@Override
protected void onStop() {
finish();
super.onStop();
}
优化,查询数据库很耗时,将查询结果放在内存录,弄一个list的数据结构,list放在内存里,查了一次后就放进去了
public class WatchDogService extends Service {
private ActivityManager ams;
private WatchDogDao dao;
private String unlockPackage;
//将查询操作转换为 查询内存里面的数据结构的操作,这样可以大大的节省时间 提高效率
List<String> lockpackageList ;
要去dao里做添加一个查询list
public List<String> queryallPackageName(){
List<String> list = new ArrayList<String>();
Cursor cursor= db.query("watchdoginfo", null, null, null, null, null, null);
while (cursor.moveToNext()) {
String packagename = cursor.getString(1);
list.add(packagename);
}
return list;
}
去service初始化
<pre name="code" class="java">@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//通过activityManager拿到系统的service
ams = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
dao = new WatchDogDao(this);
<span style="color:#ff0000;">lockpackageList = dao.queryallPackageName();</span>
for (String string : lockpackageList) {
System.out.println("WatchDogService.onStartCommand()"+string);
}
节省了时间,有可能会内存不够用,其实还可以优化什么比特。。
然后在判断的时候,进这个list
if (!packagename.equals(unlockPackage) && lockpackageList.contains(packagename)){
//启动我们的watchdog的mainActivity
Intent intent = new Intent(WatchDogService.this,WatchDogActivity.class);
OK,现在又出现一个bug
给几个APP加锁后退出,再随便点击别的某个APP,发现不会跳转
因为list被固定了,和数据库不同步
需要数据库的一个内容观察者,在数据库发生变化时通知下
需要一个getcontext
public class WatchDogDao {
//增删改查
SQLiteDatabase db ;
WatchDogOpenHelper helper ;
<span style="color:#ff0000;">Context ctx</span> ;
public WatchDogDao(Context ctx) {
<span style="color:#ff0000;">this.ctx =ctx; </span>
<pre name="code" class="java">public void addPackageName(String name ){
//db.execSQL(sql);
ContentValues values = new ContentValues();
values.put("packagename", name);
db.insert("watchdoginfo", null, values);
//数据库发生变化时做通知,自己定义的uri,用来被人监听到
ctx.getContentResolver().notifyChange(Uri.parse("content://com.rjl.watchdogdb"), null);
}
//delete
public int deletepackagename(String packagename){
int i = db.delete("watchdoginfo", "packagename = ? ", new String[]{packagename});
ctx.getContentResolver().notifyChange(Uri.parse("content://com.rjl.watchdogdb"), null);
return i;
}
service里需要一个观察者,发生变化时则重新查一下数据库
//监听数据库变化
class Mycontentobeserver extends ContentObserver {
public Mycontentobeserver(Handler handler) {
super(handler);
// TODO Auto-generated constructor stub
}
@Override
public void onChange(boolean selfChange) {
// TODO Auto-generated method stub
super.onChange(selfChange);
lockpackageList = dao.queryallPackageName();
Log.i("onChange", "watchdogdb changed!");
}
}
要注册下,发生变化,则调用上面的onchange重新查
//注册观察者
Mycontentobeserver myobeserver = new Mycontentobeserver(new Handler());
getContentResolver().registerContentObserver(Uri.parse("content://com.rjl.watchdogdb"), true, myobeserver);
至此核心功能都已经实现,还可以继续做的主要就是密码的问题,保存啊,是否设置同一个密码啊等等