導讀:本文介紹如何實現對應用加鎖的功能,無須root權限
某些人有時候會有這樣一種需求,小A下載了個軟件,只是軟件中的美女過於誘惑與暴露,所以他不想讓別人知道這是個什麼軟件,起碼不想讓別人打開瀏覽。而這款軟件又沒有鎖,任何人都可以打開,腫麼辦呢?如果打開它的時候需要輸入密碼,那該多好阿!於是,程序鎖這種應用就產生了
程序鎖不是最近纔有的,很久之前android就有這種apk了
這一期我們來苛刻如何實現程序加鎖功能
首先,我們先明確一下我們要做的程序具有什麼功能
1可以選擇需要加鎖的程序
2可以設置密碼
3可以關閉程序鎖
這裏作爲演示,我們就儘量簡化代碼
我們先說最關鍵的部分
最關鍵的地方在於:當用戶打開一個應用的時候,怎麼彈出密碼頁面?
這裏沒有什麼太好的辦法,需要掃描task中的topActivity
首先,我們先獲得運行的task
- mActivityManager = (ActivityManager) context.getSystemService("activity");
- //mActivityManager.getRunningTasks(1);//List<RunningTaskInfo>
mActivityManager = (ActivityManager) context.getSystemService("activity");
//mActivityManager.getRunningTasks(1);//List<RunningTaskInfo>
getRunningTasks方法返回一個List,我們來看看這個List是什麼
……
返回的List是有序的,第一個是最近的,所以我們取出第一個即可,然後得到此task中的最上層的Activity
- ComponentName topActivity = mActivityManager.getRunningTasks(1).get(0).topActivity;
ComponentName topActivity = mActivityManager.getRunningTasks(1).get(0).topActivity;
topActivity居然是ComponentName類型,下面的事情就好辦了,獲得包名和類名
- ComponentName topActivity = mActivityManager.getRunningTasks(1).get(0).topActivity;
- String packageName = topActivity.getPackageName();
- String className = topActivity.getClassName();
- Log.v(TAG, "packageName" + packageName);
- Log.v(TAG, "className" + className);
- if (testPackageName.equals(packageName)
- && testClassName.equals(className)) {
- Intent intent = new Intent();
- intent.setClassName("com.example.locktest", "com.example.locktest.PasswordActivity");
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- mContext.startActivity(intent);
- }
ComponentName topActivity = mActivityManager.getRunningTasks(1).get(0).topActivity;
String packageName = topActivity.getPackageName();
String className = topActivity.getClassName();
Log.v(TAG, "packageName" + packageName);
Log.v(TAG, "className" + className);
if (testPackageName.equals(packageName)
&& testClassName.equals(className)) {
Intent intent = new Intent();
intent.setClassName("com.example.locktest", "com.example.locktest.PasswordActivity");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
}
由於我沒有選擇程序這一步,所以我就固定一個應用做測試,這裏選擇的是htc的note應用
- String testPackageName = "com.htc.notes";
- String testClassName = "com.htc.notes.collection.NotesGridViewActivity";
String testPackageName = "com.htc.notes";
String testClassName = "com.htc.notes.collection.NotesGridViewActivity";
下面我們該想,這段代碼何時執行了
打開一個應用程序,系統不會發送廣播,我們無法直接監聽,所以這裏我們採取定時掃描的策略
這裏只是一個簡單的實現,之後我們再討論優化
我們採取每秒中檢查一次task的方式,這裏使用Timer吧,用Handler也一樣可以實現
- private Timer mTimer;
- private void startTimer() {
- if (mTimer == null) {
- mTimer = new Timer();
- LockTask lockTask = new LockTask(this);
- mTimer.schedule(lockTask, 0L, 1000L);
- }
- }
private Timer mTimer;
private void startTimer() {
if (mTimer == null) {
mTimer = new Timer();
LockTask lockTask = new LockTask(this);
mTimer.schedule(lockTask, 0L, 1000L);
}
}
到這裏,其實我們的關鍵代碼就已經完成了
下面貼出完整帶代碼,注意:我們只關注彈出鎖界面這部分,其他部分自行實現(比如文章末尾提到的)
Task,負責檢查task,並在適當的時候彈出密碼頁面
- public class LockTask extends TimerTask {
- public static final String TAG = "LockTask";
- private Context mContext;
- String testPackageName = "com.htc.notes";
- String testClassName = "com.htc.notes.collection.NotesGridViewActivity";
- private ActivityManager mActivityManager;
- public LockTask(Context context) {
- mContext = context;
- mActivityManager = (ActivityManager) context.getSystemService("activity");
- }
- @Override
- public void run() {
- ComponentName topActivity = mActivityManager.getRunningTasks(1).get(0).topActivity;
- String packageName = topActivity.getPackageName();
- String className = topActivity.getClassName();
- Log.v(TAG, "packageName" + packageName);
- Log.v(TAG, "className" + className);
- if (testPackageName.equals(packageName)
- && testClassName.equals(className)) {
- Intent intent = new Intent();
- intent.setClassName("com.example.locktest", "com.example.locktest.PasswordActivity");
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- mContext.startActivity(intent);
- }
- }
- }
public class LockTask extends TimerTask {
public static final String TAG = "LockTask";
private Context mContext;
String testPackageName = "com.htc.notes";
String testClassName = "com.htc.notes.collection.NotesGridViewActivity";
private ActivityManager mActivityManager;
public LockTask(Context context) {
mContext = context;
mActivityManager = (ActivityManager) context.getSystemService("activity");
}
@Override
public void run() {
ComponentName topActivity = mActivityManager.getRunningTasks(1).get(0).topActivity;
String packageName = topActivity.getPackageName();
String className = topActivity.getClassName();
Log.v(TAG, "packageName" + packageName);
Log.v(TAG, "className" + className);
if (testPackageName.equals(packageName)
&& testClassName.equals(className)) {
Intent intent = new Intent();
intent.setClassName("com.example.locktest", "com.example.locktest.PasswordActivity");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
}
}
}
LockService,負責執行定時任務,取消任務等
- public class LockService extends Service {
- private Timer mTimer;
- public static final int FOREGROUND_ID = 0;
- private void startTimer() {
- if (mTimer == null) {
- mTimer = new Timer();
- LockTask lockTask = new LockTask(this);
- mTimer.schedule(lockTask, 0L, 1000L);
- }
- }
- public IBinder onBind(Intent intent) {
- return null;
- }
- public void onCreate() {
- super.onCreate();
- startForeground(FOREGROUND_ID, new Notification());
- }
- public int onStartCommand(Intent intent, int flags, int startId) {
- startTimer();
- return super.onStartCommand(intent, flags, startId);
- }
- public void onDestroy() {
- stopForeground(true);
- mTimer.cancel();
- mTimer.purge();
- mTimer = null;
- super.onDestroy();
- }
- }
public class LockService extends Service {
private Timer mTimer;
public static final int FOREGROUND_ID = 0;
private void startTimer() {
if (mTimer == null) {
mTimer = new Timer();
LockTask lockTask = new LockTask(this);
mTimer.schedule(lockTask, 0L, 1000L);
}
}
public IBinder onBind(Intent intent) {
return null;
}
public void onCreate() {
super.onCreate();
startForeground(FOREGROUND_ID, new Notification());
}
public int onStartCommand(Intent intent, int flags, int startId) {
startTimer();
return super.onStartCommand(intent, flags, startId);
}
public void onDestroy() {
stopForeground(true);
mTimer.cancel();
mTimer.purge();
mTimer = null;
super.onDestroy();
}
}
MainActivity,測試用,作爲應用入口,啓動service(產品中,我們可以在receiver中啓動service)。
- public class MainActivity extends Activity {
- public void onCreate(Bundle savedInstanceState){
- super.onCreate(savedInstanceState);
- startService(new Intent(this, LockService.class));
- }
- }
public class MainActivity extends Activity {
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
startService(new Intent(this, LockService.class));
}
}
PasswordActivity,密碼頁面,很粗糙,沒有核對密碼邏輯,自行實現
記得重寫onBackPressed函數,不然按返回鍵的時候……你懂的
- public class PasswordActivity extends Activity {
- private static final String TAG = "PasswordActivity";
- Button okButton;
- EditText passwordEditText;
- private boolean mFinish = false;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.password);
- passwordEditText = (EditText) findViewById(R.id.password);
- okButton = (Button) findViewById(R.id.ok);
- okButton.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- String password = passwordEditText.getText().toString();
- Log.v(TAG, "password" + password);
- mFinish = true;
- finish();
- }
- });
- }
- public void onBackPressed(){}
- public void onPause(){
- super.onPause();
- if(!mFinish){
- finish();
- }
- }
- }
public class PasswordActivity extends Activity {
private static final String TAG = "PasswordActivity";
Button okButton;
EditText passwordEditText;
private boolean mFinish = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.password);
passwordEditText = (EditText) findViewById(R.id.password);
okButton = (Button) findViewById(R.id.ok);
okButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
String password = passwordEditText.getText().toString();
Log.v(TAG, "password" + password);
mFinish = true;
finish();
}
});
}
public void onBackPressed(){}
public void onPause(){
super.onPause();
if(!mFinish){
finish();
}
}
}
xml這裏就不貼了,記得添加權限
- <uses-permission android:name="android.permission.GET_TASKS"/>
關於程序的其他部分,這裏只做簡要說明
選擇應用對其進行加鎖部分
1列出系統中所有程序(你也可以自由發揮,比如過濾掉原始應用)
2選擇,然後存入數據庫(當然,最好也有取消功能,記得從數據庫中刪除數據)
程序鎖總開關
可以使用sharedPreference,設置一個boolean開關
現在,當我想要打開htc的note應用的時候,就會彈出密碼頁面當我解鎖,按home會回到桌面,長按home,點擊note,還是會彈出密碼框
因爲是每秒檢查一次,所以可能會有一點點延遲,你可以設置爲500毫秒,但是越頻繁,佔用資源就越多
上面的代碼我取得topActivity後檢查了其包名行和類名,所以只有當打開指定的頁面的時候,纔會彈出密碼鎖
比如我對Gallery應用加密了,但是用戶正在編輯短信,這時候它想發彩信,於是他通過短信進入到了Gallery……
對於某些用戶的某些需求來說,這是不能容忍的,這時,我們只需簡單修改下判斷邏輯即可:只檢查包名,包名一致就彈出密碼鎖,這樣就完美了
程序鎖我就分析到這裏
最後一句
當使用程序鎖的時候,你長按home,發現程序鎖也出現在“最近的任務”中,腫麼辦……給此activity設置android:excludeFromRecents="true"即可