Android程序鎖的實(shí)現(xiàn)以及邏輯
本項(xiàng)目是一個(gè)比較有趣的項(xiàng)目源碼,可以給其他項(xiàng)目加鎖,程序鎖的原理是一個(gè)“看門(mén)狗”的服務(wù)定時(shí)監(jiān)視頂層activity,如果activity對(duì)應(yīng)的包名是之前上鎖的應(yīng)用程序的,則彈出一個(gè)頁(yè)面要求輸入解鎖密碼。
效果如下:
1.基本思路
①.創(chuàng)建已加鎖應(yīng)用的數(shù)據(jù)庫(kù)(字段:_id,packagename),如果應(yīng)用已加鎖,將加鎖應(yīng)用的包名維護(hù)到數(shù)據(jù)庫(kù)中
②.已加鎖+未加鎖 == 手機(jī)中所有應(yīng)用(AppInfoProvider)
2.已加鎖和未加鎖的數(shù)據(jù)適配器
class MyAdapter extends BaseAdapter{ private boolean isLock; /** * @param isLock 用于區(qū)分已加鎖和未加鎖應(yīng)用的標(biāo)示 true已加鎖數(shù)據(jù)適配器 false未加鎖數(shù)據(jù)適配器 */ public MyAdapter(boolean isLock) { this.isLock = isLock; } @Override public int getCount() { if(isLock){ tv_lock.setText("已加鎖應(yīng)用:"+mLockList.size()); return mLockList.size(); }else{ tv_unlock.setText("未加鎖應(yīng)用:"+mUnLockList.size()); return mUnLockList.size(); } } @Override public AppInfo getItem(int position) { if(isLock){ return mLockList.get(position); }else{ return mUnLockList.get(position); } } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = null; if(convertView == null){ convertView = View.inflate(getApplicationContext(), R.layout.listview_islock_item, null); holder = new ViewHolder(); holder.iv_icon = (ImageView) convertView.findViewById(R.id.iv_icon); holder.tv_name = (TextView) convertView.findViewById(R.id.tv_name); holder.iv_lock = (ImageView) convertView.findViewById(R.id.iv_lock); convertView.setTag(holder); }else{ holder = (ViewHolder) convertView.getTag(); } final AppInfo appInfo = getItem(position); final View animationView = convertView; holder.iv_icon.setBackgroundDrawable(appInfo.icon); holder.tv_name.setText(appInfo.name); if(isLock){ holder.iv_lock.setBackgroundResource(R.drawable.lock); }else{ holder.iv_lock.setBackgroundResource(R.drawable.unlock); } holder.iv_lock.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { //添加動(dòng)畫(huà)效果,動(dòng)畫(huà)默認(rèn)是非阻塞的,所以執(zhí)行動(dòng)畫(huà)的同時(shí),動(dòng)畫(huà)以下的代碼也會(huì)執(zhí)行 animationView.startAnimation(mTranslateAnimation);//500毫秒 //對(duì)動(dòng)畫(huà)執(zhí)行過(guò)程做事件監(jiān)聽(tīng),監(jiān)聽(tīng)到動(dòng)畫(huà)執(zhí)行完成后,再去移除集合中的數(shù)據(jù),操作數(shù)據(jù)庫(kù),刷新界面 mTranslateAnimation.setAnimationListener(new AnimationListener() { @Override public void onAnimationStart(Animation animation) { //動(dòng)畫(huà)開(kāi)始的是調(diào)用方法 } @Override public void onAnimationRepeat(Animation animation) { //動(dòng)畫(huà)重復(fù)時(shí)候調(diào)用方法 } //動(dòng)畫(huà)執(zhí)行結(jié)束后調(diào)用方法 @Override public void onAnimationEnd(Animation animation) { if(isLock){ //已加鎖------>未加鎖過(guò)程 //1.已加鎖集合刪除一個(gè),未加鎖集合添加一個(gè),對(duì)象就是getItem方法獲取的對(duì)象 mLockList.remove(appInfo); mUnLockList.add(appInfo); //2.從已加鎖的數(shù)據(jù)庫(kù)中刪除一條數(shù)據(jù) mDao.delete(appInfo.packageName); //3.刷新數(shù)據(jù)適配器 mLockAdapter.notifyDataSetChanged(); }else{ //未加鎖------>已加鎖過(guò)程 //1.已加鎖集合添加一個(gè),未加鎖集合移除一個(gè),對(duì)象就是getItem方法獲取的對(duì)象 mLockList.add(appInfo); mUnLockList.remove(appInfo); //2.從已加鎖的數(shù)據(jù)庫(kù)中插入一條數(shù)據(jù) mDao.insert(appInfo.packageName); //3.刷新數(shù)據(jù)適配器 mUnLockAdapter.notifyDataSetChanged(); } } }); } }); return convertView; } }
mLockAdapter = new MyAdapter(true); lv_lock.setAdapter(mLockAdapter); mUnLockAdapter = new MyAdapter(false); lv_unlock.setAdapter(mUnLockAdapter);
3.已加鎖和未加鎖條目點(diǎn)擊事件處理
holder.iv_lock.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { //添加動(dòng)畫(huà)效果,動(dòng)畫(huà)默認(rèn)是非阻塞的,所以執(zhí)行動(dòng)畫(huà)的同時(shí),動(dòng)畫(huà)以下的代碼也會(huì)執(zhí)行 animationView.startAnimation(mTranslateAnimation);//500毫秒 //對(duì)動(dòng)畫(huà)執(zhí)行過(guò)程做事件監(jiān)聽(tīng),監(jiān)聽(tīng)到動(dòng)畫(huà)執(zhí)行完成后,再去移除集合中的數(shù)據(jù),操作數(shù)據(jù)庫(kù),刷新界面 mTranslateAnimation.setAnimationListener(new AnimationListener() { @Override public void onAnimationStart(Animation animation) { //動(dòng)畫(huà)開(kāi)始的是調(diào)用方法 } @Override public void onAnimationRepeat(Animation animation) { //動(dòng)畫(huà)重復(fù)時(shí)候調(diào)用方法 } //動(dòng)畫(huà)執(zhí)行結(jié)束后調(diào)用方法 @Override public void onAnimationEnd(Animation animation) { if(isLock){ //已加鎖------>未加鎖過(guò)程 //1.已加鎖集合刪除一個(gè),未加鎖集合添加一個(gè),對(duì)象就是getItem方法獲取的對(duì)象 mLockList.remove(appInfo); mUnLockList.add(appInfo); //2.從已加鎖的數(shù)據(jù)庫(kù)中刪除一條數(shù)據(jù) mDao.delete(appInfo.packageName); //3.刷新數(shù)據(jù)適配器 mLockAdapter.notifyDataSetChanged(); }else{ //未加鎖------>已加鎖過(guò)程 //1.已加鎖集合添加一個(gè),未加鎖集合移除一個(gè),對(duì)象就是getItem方法獲取的對(duì)象 mLockList.add(appInfo); mUnLockList.remove(appInfo); //2.從已加鎖的數(shù)據(jù)庫(kù)中插入一條數(shù)據(jù) mDao.insert(appInfo.packageName); //3.刷新數(shù)據(jù)適配器 mUnLockAdapter.notifyDataSetChanged(); } } }); } });
4.程序鎖必須在服務(wù)中去維護(hù)
①基本思路
- 判斷當(dāng)前開(kāi)啟的應(yīng)用(現(xiàn)在手機(jī)可見(jiàn)任務(wù)棧)
- 如果開(kāi)啟的應(yīng)用在已加鎖的列表中,彈出攔截界面
- 看門(mén)狗服務(wù),一直(死循環(huán)(子線(xiàn)程,可控))對(duì)開(kāi)啟的應(yīng)用做監(jiān)聽(tīng)
public class WatchDogService extends Service { private boolean isWatch; private AppLockDao mDao; private List<String> mPacknameList; private InnerReceiver mInnerReceiver; private String mSkipPackagename; private MyContentObserver mContentObserver; @Override public void onCreate() { //維護(hù)一個(gè)看門(mén)狗的死循環(huán),讓其時(shí)刻監(jiān)測(cè)現(xiàn)在開(kāi)啟的應(yīng)用,是否為程序鎖中要去攔截的應(yīng)用 mDao = AppLockDao.getInstance(this); isWatch = true; watch(); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction("android.intent.action.SKIP"); mInnerReceiver = new InnerReceiver(); registerReceiver(mInnerReceiver, intentFilter); //注冊(cè)一個(gè)內(nèi)容觀察者,觀察數(shù)據(jù)庫(kù)的變化,一旦數(shù)據(jù)有刪除或者添加,則需要讓mPacknameList重新獲取一次數(shù)據(jù) mContentObserver = new MyContentObserver(new Handler()); getContentResolver().registerContentObserver( Uri.parse("content://applock/change"), true, mContentObserver); super.onCreate(); } class MyContentObserver extends ContentObserver{ public MyContentObserver(Handler handler) { super(handler); } //一旦數(shù)據(jù)庫(kù)發(fā)生改變時(shí)候調(diào)用方法,重新獲取包名所在集合的數(shù)據(jù) @Override public void onChange(boolean selfChange) { new Thread(){ public void run() { mPacknameList = mDao.findAll(); }; }.start(); super.onChange(selfChange); } } class InnerReceiver extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { //獲取發(fā)送廣播過(guò)程中傳遞過(guò)來(lái)的包名,跳過(guò)次包名檢測(cè)過(guò)程 mSkipPackagename = intent.getStringExtra("packagename"); } } private void watch() { //1,子線(xiàn)程中,開(kāi)啟一個(gè)可控死循環(huán) new Thread(){ public void run() { mPacknameList = mDao.findAll(); while(isWatch){ //2.監(jiān)測(cè)現(xiàn)在正在開(kāi)啟的應(yīng)用,任務(wù)棧 //3.獲取activity管理者對(duì)象 ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); //4.獲取正在開(kāi)啟應(yīng)用的任務(wù)棧 List<RunningTaskInfo> runningTasks = am.getRunningTasks(1); RunningTaskInfo runningTaskInfo = runningTasks.get(0); //5.獲取棧頂?shù)腶ctivity,然后在獲取此activity所在應(yīng)用的包名 String packagename = runningTaskInfo.topActivity.getPackageName(); //如果任務(wù)棧指向應(yīng)用有切換,將mSkipPackagename空字符串 //6.拿此包名在已加鎖的包名集合中去做比對(duì),如果包含次包名,則需要彈出攔截界面 if(mPacknameList.contains(packagename)){ //如果現(xiàn)在檢測(cè)的程序,以及解鎖了,則不需要去彈出攔截界面 if(!packagename.equals(mSkipPackagename)){ //7,彈出攔截界面 Intent intent = new Intent(getApplicationContext(),EnterPsdActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.putExtra("packagename", packagename); startActivity(intent); } } //睡眠一下,時(shí)間片輪轉(zhuǎn) try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } }; }.start(); } @Override public IBinder onBind(Intent arg0) { return null; } @Override public void onDestroy() { //停止看門(mén)狗循環(huán) isWatch = false; //注銷(xiāo)廣播接受者 if(mInnerReceiver!=null){ unregisterReceiver(mInnerReceiver); } //注銷(xiāo)內(nèi)容觀察者 if(mContentObserver!=null){ getContentResolver().unregisterContentObserver(mContentObserver); } super.onDestroy(); } }
public class EnterPsdActivity extends Activity { private String packagename; private TextView tv_app_name; private ImageView iv_app_icon; private EditText et_psd; private Button bt_submit; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //獲取包名 packagename = getIntent().getStringExtra("packagename"); setContentView(R.layout.activity_enter_psd); initUI(); initData(); } private void initData() { //通過(guò)傳遞過(guò)來(lái)的包名獲取攔截應(yīng)用的圖標(biāo)以及名稱(chēng) PackageManager pm = getPackageManager(); try { ApplicationInfo applicationInfo = pm.getApplicationInfo(packagename,0); Drawable icon = applicationInfo.loadIcon(pm); iv_app_icon.setBackgroundDrawable(icon); tv_app_name.setText(applicationInfo.loadLabel(pm).toString()); } catch (NameNotFoundException e) { e.printStackTrace(); } bt_submit.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { String psd = et_psd.getText().toString(); if(!TextUtils.isEmpty(psd)){ if(psd.equals("123")){ //解鎖,進(jìn)入應(yīng)用,告知看門(mén)口不要再去監(jiān)聽(tīng)以及解鎖的應(yīng)用,發(fā)送廣播 Intent intent = new Intent("android.intent.action.SKIP"); intent.putExtra("packagename",packagename); sendBroadcast(intent); finish(); }else{ ToastUtil.show(getApplicationContext(), "密碼錯(cuò)誤"); } }else{ ToastUtil.show(getApplicationContext(), "請(qǐng)輸入密碼"); } } }); } private void initUI() { tv_app_name = (TextView) findViewById(R.id.tv_app_name); iv_app_icon = (ImageView) findViewById(R.id.iv_app_icon); et_psd = (EditText) findViewById(R.id.et_psd); bt_submit = (Button) findViewById(R.id.bt_submit); } @Override public void onBackPressed() { //通過(guò)隱式意圖,跳轉(zhuǎn)到桌面 Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_HOME); startActivity(intent); super.onBackPressed(); } }
5.隱藏最近打開(kāi)的activity
<activity android:excludeFromRecents="true" android:name="com.itheima.mobilesafe.EnterPwdActivity" android:launchMode="singleInstance" />
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android實(shí)現(xiàn)底部彈出PopupWindow背景逐漸變暗效果
- android項(xiàng)目手機(jī)衛(wèi)士來(lái)電顯示號(hào)碼歸屬地
- Android自定義滑動(dòng)接聽(tīng)電話(huà)控件組實(shí)例
- android中對(duì)文件加密解密的實(shí)現(xiàn)
- Android實(shí)現(xiàn)屏幕鎖定源碼詳解
- Android開(kāi)發(fā)之自定義View(視圖)用法詳解
- Android實(shí)現(xiàn)顯示電量的控件代碼
- Android滑動(dòng)動(dòng)態(tài)分頁(yè)實(shí)現(xiàn)方法
- Android 自定義手勢(shì)--輸入法手勢(shì)技術(shù)
相關(guān)文章
android仿知乎標(biāo)題欄隨ScrollView滾動(dòng)變色
這篇文章主要為大家詳細(xì)介紹了android仿知乎標(biāo)題欄隨ScrollView滾動(dòng)變色,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-06-06Android編程實(shí)現(xiàn)應(yīng)用強(qiáng)制安裝到手機(jī)內(nèi)存的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)應(yīng)用強(qiáng)制安裝到手機(jī)內(nèi)存的方法,涉及Android中屬性設(shè)置的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-11-11Android手冊(cè)之Toolbar搜索聯(lián)動(dòng)及監(jiān)聽(tīng)小技巧
這篇文章主要為大家介紹了Android手冊(cè)之Toolbar搜索聯(lián)動(dòng)及監(jiān)聽(tīng)小技巧示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09Android自定義view實(shí)現(xiàn)滑動(dòng)解鎖效果
這篇文章主要為大家詳細(xì)介紹了Android自定義view實(shí)現(xiàn)滑動(dòng)解鎖效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-05-05Android RecyclerView 滾動(dòng)到中間位置的方法示例
這篇文章主要介紹了Android RecyclerView 滾動(dòng)到中間位置的方法示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-03-03Android安卓中循環(huán)錄像并檢測(cè)內(nèi)存卡容量
這篇文章主要介紹了Android安卓中循環(huán)錄像并檢測(cè)內(nèi)存卡容量,當(dāng)內(nèi)存卡空間已滿(mǎn)時(shí),本文還實(shí)現(xiàn)自動(dòng)刪除視頻列表里面的第一個(gè)文件,需要的朋友可以參考下2015-06-06Android基礎(chǔ)之使用Fragment控制切換多個(gè)頁(yè)面
Android官方已經(jīng)提供了Fragment的各種使用的Demo例子,在我們SDK下面的API Demo里面就包含了Fragment的各種使用例子,需要看Demo的朋友,直接看API Demo那個(gè)程序就可以了,不用到處去找。里面分開(kāi)不同功能,實(shí)現(xiàn)了不同的類(lèi)2013-07-07