程序锁细节
步骤:1、在需要验证密码的时候带上应用程序名称和应用程序图标 2、需要验证密码才能进入应用程序 3、如果成功,那么进入,并加入到临时不受保护的集合中,通知看门狗不保护 4、如果不成功或为null,提示密码错误 5、用户按后退键,应该回退到桌面 |
核心代码: a) EnterPasswordActivity.java package cn.zengfansheng.mobilesafe;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import cn.zengfansheng.mobilesafe.utils.ToastUtils;
/**
* 19、程序管理-输入密码的Activity,输入正确才让进去
*
* @author hacket
*/
public class EnterPasswordActivity extends Activity {
private TextView tv_protect_name_icon;// 要被保护的icon和name
private EditText et_enter_password;// 密码输入框
private Button bt_enter;// 进入按钮
private String packagename;// 要被保护的应用程序的包名
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.activity_app_lock_enter_pwd);
tv_protect_name_icon = (TextView) this.findViewById(R.id.tv_protect_name_icon);
et_enter_password = (EditText) this.findViewById(R.id.et_enter_password);
bt_enter = (Button) this.findViewById(R.id.bt_enter);
// 1、设置EnterPasswordActivity中界面的布局
setEnterPassActivity();
// 2、为按钮设置点击事件
bt_enter.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
String et_pass = et_enter_password.getText().toString().trim();
if (TextUtils.isEmpty(et_pass)) {// 输入的密码为空,TODO 可以设置抖动的动画
ToastUtils.showToastInThread(EnterPasswordActivity.this, "密码不能为空~~~");
return;
}
if ("123".equals(et_pass)) {//假设密码是123, TODO 这里可以从数据库或者SharedPreferences中获取,看你程序锁密码保存在哪
// 通知看门狗服务,不要再监视这个应用,暂时取消保护,也就是Activity和Service通信
// 解决:1、可以通过service和activity绑定进行 2、发送自定义广播
Intent intent = new Intent();
intent.setAction("cn.zengfansheng.cancel");
intent.putExtra("packagename", packagename);
sendBroadcast(intent);// 发送自定义广播
finish();// 销毁当前的Activity
} else {
ToastUtils.showToastInThread(EnterPasswordActivity.this, "密码不对~~~");
return;
}
}
});
}
// 2、屏蔽掉用户按物理后退键
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// 如果用户按了物理后退键,返回到桌面,可以系统定义,也可以看LogCat
if (keyCode == KeyEvent.KEYCODE_BACK) {
/*<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.MONKEY"/>
</intent-filter>*/
Intent intent = new Intent();
intent.setAction(Intent.ACTION_MAIN);
// 防止不同的桌面,3个CATEGORY都设置
intent.addCategory(Intent.CATEGORY_HOME);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.addCategory(Intent.CATEGORY_MONKEY);
startActivity(intent);
return true;// 返回true,消耗掉该事件
}
return super.onKeyDown(keyCode, event);
}
/**
* 1、设置EnterPasswordActivity中界面的布局
*/
private void setEnterPassActivity() {
// 1、返回激活该Activity的Intent
Intent intent = getIntent();//Return the intent that started this activity.
// 2、得到要保护的应用程序的包名
packagename = intent.getStringExtra("packagename");
// 3、得到包管理者
PackageManager packageManager = getPackageManager();
// 4、获取该应用的包名,获取该应用程序的名称和图标
try {
ApplicationInfo applicationInfo = packageManager.getApplicationInfo(packagename, 0);
String appName = applicationInfo.loadLabel(packageManager).toString();
Drawable appIcon = applicationInfo.loadIcon(packageManager);
appIcon.setBounds(0, 0, 70, 83);
// 5、设置到要输入密码的Activity上
tv_protect_name_icon.setCompoundDrawables(null, appIcon, null, null);
tv_protect_name_icon.setText(appName);
} catch (NameNotFoundException e) {
e.printStackTrace();
}
}
} b) WatchDogService.java package cn.zengfansheng.mobilesafe.service;
import java.util.ArrayList;
import java.util.List;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;
import android.util.Log;
import cn.zengfansheng.mobilesafe.EnterPasswordActivity;
import cn.zengfansheng.mobilesafe.db.dao.AppLockDao;
/**
* 4、看门狗Service——监听应用程序的启动
* @author hacket
*/
public class WatchDogService extends Service { protected static final String TAG = "WatchDogService";
private ActivityManager activityManager;// Activity管理者
private boolean flag;// 服务运行的标记,true表示运行,false表示停止
private AppLockDao appLockDao;// 程序锁的dao
private Intent intent;// 进入输入密码Activity界面的Intent
private CancelProtectApplication noProtectReceiver;// 暂时不保护的广播接收者
private List<String> tempNoProtectAppPackageName;// 临时不受保护的应用的包名集合
private BroadcastReceiver screenOffReceiver;// 屏幕锁屏的广播接收者
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
tempNoProtectAppPackageName = new ArrayList<String>();
// 3、注册屏幕锁屏时的广播接收者
screenOffReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// 屏幕锁屏时,将临时不受保护的List集合给清空
if (tempNoProtectAppPackageName != null) {
tempNoProtectAppPackageName.clear();
}
}
};
IntentFilter screenFilter = new IntentFilter();
screenFilter.addAction(Intent.ACTION_SCREEN_OFF);// 屏幕锁屏广播
registerReceiver(screenOffReceiver, screenFilter);
// 2、注册接收自定义的广播事件
noProtectReceiver = new CancelProtectApplication();
IntentFilter filter = new IntentFilter();
filter.addAction("cn.zengfansheng.cancel");
registerReceiver(noProtectReceiver, filter);
// 1、获取ActivityManager管理者,进行开启看门狗服务
activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
appLockDao = new AppLockDao(getApplicationContext());
intent = new Intent(getApplicationContext(),EnterPasswordActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);// 外部开启Activity,一定要加上
// 看门狗需要不停的监视后台正在运行的应用程序.
new Thread() {
public void run() {
flag = true;
while (flag) {
// 2、获取最近运行的3个Task
List<RunningTaskInfo> runningTasks = activityManager.getRunningTasks(3);
// 3、新打开的Activity在第一个task里面
RunningTaskInfo runningTaskInfo = runningTasks.get(0);
// 4、然后取栈顶,就是刚打开的Activity
ComponentName topActivity = runningTaskInfo.topActivity;
String packageName = topActivity.getPackageName();
boolean isLock = appLockDao.find(packageName);
if (isLock) {// 如果锁上了,进入输入密码Activity界面
if (!tempNoProtectAppPackageName.contains(packageName)) {// 如果临时不保护的集合不包含当前应用的包名,那么就要保护
Log.i(TAG, packageName + "锁上了~");
// 将要保护的packagename传递过去
intent.putExtra("packagename", packageName);
startActivity(intent);
}
} else {// 没有锁上
Log.i(TAG, packageName + "没有锁上~");
}
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(packageName);
}
}
}.start();
}
@Override
public void onDestroy() {
super.onDestroy();
// 1、销毁时,服务不在运行
flag = false;
// 2、服务销毁时,取消自定义广播的事件-取消应用保护的注册
unregisterReceiver(noProtectReceiver);
noProtectReceiver = null;
// 3、服务销毁时,取消屏幕广播事件的注册
unregisterReceiver(screenOffReceiver);
screenOffReceiver = null;
}
/**
* 1、临时取消保护应用的广播接收者
* @author hacket
*/
private class CancelProtectApplication extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
// 1、 得到临时要取消保护的应用程序的包名
String packagename = intent.getStringExtra("packagename");
// 2、然后将其加入临时不受保护的List集合中去
tempNoProtectAppPackageName.add(packagename);
}
}
} |
问题1:用户输入正确后,进入应用,但看门狗,还是继续监视,又会弹出输入密码框输入,这个体验不好 解决:临时取消掉 定义一个临时集合容器, 输入正确时,不在监听该应用 |
问题2:当用户输入正确时,可以进去,当锁屏后再次输入不需要, 这个体验不好,最好是在锁屏的时候,将该临时保护的集合列表给清除,再下次进入时,需要输入密码 解决: 自定义发送广播事件 当用户输入正确后,临时取消保护 将该应用加入到一个临时不受保护的集合,在锁屏的时候,将该集合清除, 再下次屏幕开始时,又要输入密码 // 3、注册屏幕锁屏时的广播接收者
screenOffReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// 屏幕锁屏时,将临时不受保护的List集合给清空
if (tempNoProtectAppPackageName != null) {
tempNoProtectAppPackageName.clear();
}
}
};
IntentFilter screenFilter = new IntentFilter();
screenFilter.addAction(Intent.ACTION_SCREEN_OFF);// 屏幕锁屏广播
registerReceiver(screenOffReceiver, screenFilter); Service和Activity进行通信 通过自定义广播消息,发送给Service,Service接收到广播事件做相应的处理 |
问题3:如果用户在输入密码时,按后退键,会回到该应用,但又会被看门狗拦截,又会弹出密码框输入, 这个体验也不好 解决:如果用户按了物理后退键,回到桌面,设置按钮监听事件,监听后退键按下 桌面应用: 1)看logcat 2)看系统源码:桌面应用是如何定义的 <activity
android:name="com.android.launcher2.Launcher"
android:launchMode="singleTask"
android:clearTaskOnLaunch="true"
android:stateNotNeeded="true"
android:theme="@style/Theme"
android:windowSoftInputMode="adjustPan"
android:screenOrientation="nosensor">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.MONKEY"/>
</intent-filter>
</activity> 3)解决, // 2、屏蔽掉用户按物理后退键
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// 如果用户按了物理后退键,返回到桌面,可以系统定义,也可以看LogCat
if (keyCode == KeyEvent.KEYCODE_BACK) {
/*<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.MONKEY"/>
</intent-filter>*/
Intent intent = new Intent();
intent.setAction(Intent.ACTION_MAIN);
// 防止不同的桌面,3个CATEGORY都设置
intent.addCategory(Intent.CATEGORY_HOME);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.addCategory(Intent.CATEGORY_MONKEY);
startActivity(intent);
return true;// 返回true,消耗掉该事件
}
return super.onKeyDown(keyCode, event);
} |
|