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

来源:互联网 发布:七龙珠战斗力官方数据 编辑:程序博客网 时间:2024/06/02 11:38

要对已经安装的APP加锁,也就说不能改动这个APP

而要实现这个功能,可以偷巧,在要启动的APP即将启动之前,进入加锁的activity

经典的功能watchdog,看看用户触动了哪个功能,很多软件里都有,尤其是安全软件


不能写在activity里,生命周期的问题,需要启动一个service,在后台监听

manifest注册

<service android:name="com.example.watchdogdemo.WatchDogService"></service>


public class WatchDogService extends Service {@Overridepublic IBinder onBind(Intent intent) {// TODO Auto-generated method stubreturn null;}}

在main里启用

public class MainActivity extends Activity {@Overrideprotected 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

@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {// TODO Auto-generated method stubnew Thread(new Runnable(){@Overridepublic void run(){// TODO Auto-generated method stubwhile(true){//获取当前程序,若是加锁的,则启动另一个activity//给CPU一个时间去执行别的程序try {Thread.sleep(1);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}});return super.onStartCommand(intent, flags, startId);}@Overridepublic void onDestroy() {// TODO Auto-generated method stubsuper.onDestroy();}

由于要动态监测系统的服务,这里需要权限


这里要获得当前被启动的APP,这就需要任务栈,要任务栈就需要activity管理器

任务栈里面按照执行顺序存放着activity,当你拿一个时,就是当前的activity

@Overridepublic 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(){@Overridepublic void run(){// TODO Auto-generated method stubwhile(true){//获取当前程序,若是加锁的,则启动另一个activity//task任务栈,里面放的是activityList<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 blocke.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

@Overridepublic 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 stubsettingItem_watchdog.setCheck(ServiceUtils.isRunning(this, "com.rjl.mobilephonemanager.service.WatchDogService"));settingItem_watchdog.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubif (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); }}});}

调用

@Overrideprotected void onResume() {// TODO Auto-generated method stubinitAutoUpdateItem( );    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() {@Overridepublic boolean onItemLongClick(AdapterView<?> parent, View view,int position, long id) {// TODO Auto-generated method stubreturn 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}@Overridepublic void onCreate(SQLiteDatabase db) {// TODO Auto-generated method stubString sql = "create table watchdoginfo (_id integer primary key autoincrement, packagename varchar(50) )";db.execSQL(sql);}@Overridepublic 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() {@Overridepublic 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 {@Overrideprotected void onCreate(Bundle savedInstanceState) {// TODO Auto-generated method stubsuper.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 blocke.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() {@Overridepublic 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 {@Overridepublic void onReceive(Context context, Intent intent) {// TODO Auto-generated method stub}  }
动态注册

public int onStartCommand(Intent intent, int flags, int startId) {//通过activityManager拿到系统的serviceams = (ActivityManager) getSystemService(ACTIVITY_SERVICE);dao = new WatchDogDao(this); IntentFilter filter = new IntentFilter("com.rjl.mobilephonemanager.unlock");registerReceiver(new MyUnlockReceiver(), filter);

这时候一解锁就发消息,此时就可以判断

先接收

lass MyUnlockReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {// TODO Auto-generated method stubunlockPackage = 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

@Overrideprotected 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">@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {//通过activityManager拿到系统的serviceams = (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}@Overridepublic void onChange(boolean selfChange) {// TODO Auto-generated method stubsuper.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);


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



0 0
原创粉丝点击