[安卓]手机管家(十九)软件管理之软件锁

要对已经安装的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);


至此核心功能都已经实现,还可以继续做的主要就是密码的问题,保存啊,是否设置同一个密码啊等等



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值