想做個鎖屏界面很久了,最近一周,歷經千辛萬苦,越過種種挫折,終於完美實現了這一要求,在此將鎖屏思路分享出來。
注意:這不是什麼一鍵鎖屏,是類似“go鎖屏”那樣的鎖屏界面。
准備:本程序共需要
兩個activity:home、main。
一個service:myService
一個receiver:bootReceiver
一個layout:layout
其中home作為屏幕home鍵專用的activity,main則是主要的展示鎖屏界面的activity。
service用於接收鎖屏/解鎖廣播,layout則是main所需要展示的界面。
思路:
!注意:以下代碼沒有順序聯系,具體請參考源碼!
1,給程序添加服務,當此服務接收到 鎖屏/解鎖廣播 時,關閉系統鎖屏界面,打開自己的鎖屏界面。
關鍵代碼:
/onReceive中:
keyguardManager = (KeyguardManager)context.getSystemService(context.KEYGUARD_SERVICE);
keyguardLock = keyguardManager.newKeyguardLock("");
keyguardLock.disableKeyguard();//解鎖系統鎖屏
startActivity(toMainIntent);//跳轉到主界面
注意,上面的代碼需要注冊權限:
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
另外,為了防止主界面被重復調用,我們在設置intent時還要加上一些filter:
//設置myservice中intent的filter
toMainIntent = new Intent(myService.this, Main.class);
toMainIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);//這個flags表示如果已經有這個activity,則將已有的提到棧頂,否則新建一個activity。
復制代碼
//在manifest中講主界面的啟動模式更改為:singleTask,功能和上面的“Intent.FLAG_ACTIVITY_NEW_TASK”類似
<activity
android:name="com.example.screenlocker.Main"
android:label="@string/app_name"
android:launchMode="singleTask" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
復制代碼
當然,我們還可以在onDestroy中設置服務重啟,以保證此服務一直在後台運行
復制代碼
@Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(screenReceiver);
//重啟此服務
startActivity(new Intent(myService.this,myService.class));
}
復制代碼
將一些其他的事項,如注冊服務、在主界面中啟動服務等設置完成後,我們可以run一遍了~
如果沒出錯,那麼解鎖後首先打開的將是我們的鎖屏界面。
2,實現了鎖屏,但是還有一個問題,當按返回鍵或者home鍵的時候,我們的界面就輕易被KO了。別擔心,咱們一一屏蔽他們。
首先,拿返回鍵開刀:只需要在主界面中添加如下代碼即可:
復制代碼
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_BACK:
return true;
}
return super.onKeyDown(keyCode, event);
}
復制代碼
可是,home鍵並不能通過此方法屏蔽或者捕捉。這對程序猿來說是個很頭疼的問題,在網上也有各種解決的辦法,但是筆者參考總結,決定用“GO鎖屏”的方式來實現:
這種辦法的思路是:把自己的程序設置為系統主屏幕!這樣,本來按home鍵是要轉到主屏幕的,設置好後,按home鍵則直接跳轉到我們的界面裡來了!
好了,讓我們新建一個activity,命名為“home”,用來搶奪home鍵。然後在manifest中設置如下:
復制代碼
<activity android:name="com.example.screenlocker.Home" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
復制代碼
這樣,當按home鍵的時候,如果是在main界面按的,系統不做反應。如果是在其他時候按下home鍵,就會跳轉到這個activity中來。
(首次運行時,會讓你選擇按home鍵跳轉到“主屏幕”或者“test1(咱們自己寫的程序的appName)”)
也就是說,我們已經實現了鎖屏界面對home的屏蔽。如果是在非鎖屏狀態下按home,就會調用home界面。我們只需在home界面裡如此設置即可:
onCreate(){
跳轉到系統主屏幕;//具體代碼見下文
finish();//結束這個activity
}
所以,這個activity沒有必要加載界面。故此,我們要將這個activity的theme設置為“不顯示”,這樣,不但避免浪費,更可以避免在跳轉的時候屏幕閃一下,影響用戶體驗
復制代碼
//manifest設置home的theme為noDisplay
<activity
android:name="com.example.screenlocker.Home"
android:theme="@android:style/Theme.NoDisplay" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
復制代碼
我們可以在home的oncreate裡打這樣一行代碼:
Log.e("", "home is called");finish();//調用finish以防止程序卡在一個不展示的activity
然後運行一遍試試效果,就可以清楚的發現,當我們何時按home鍵時,會調用這一activity。
3,home鍵已經搶過來了。那麼接下來,我們要做的是讓home界面跳轉到主界面:
首先,我們要獲取一個列表:列出所以可以作為主屏幕的程序:
復制代碼
List<String> pkgNamesT = new ArrayList<String>();
List<String> actNamesT = new ArrayList<String>();
List<ResolveInfo> resolveInfos = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
for (int i = 0; i < resolveInfos.size(); i++) {
String string = resolveInfos.get(i).activityInfo.packageName;
if (!string.equals(context.getPackageName())) {//排除自己的包名
pkgNamesT.add(string);
string = resolveInfos.get(i).activityInfo.name;
actNamesT.add(string);
}
}
復制代碼
然後,可以用alertDialog的方式讓用戶選擇要跳到哪個主屏幕,並用sharedPreferences記錄用戶的選擇。
復制代碼
new AlertDialog.Builder(context).setTitle("請選擇解鎖後的屏幕").setCancelable(false).setSingleChoiceItems(names, 0, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
editor.putString(packageName, pkgNames.get(which));
editor.putString(activityName, actNames.get(which));
editor.commit();
originalHome();
dialog.dismiss();
}
}).show();
復制代碼
這樣,下次的時候就可以根據包名和類名,直接跳轉到用戶所設置的主屏幕了:
復制代碼
String pkgName = sharedPreferences.getString(packageName, null);
String actName = sharedPreferences.getString(activityName, null);
ComponentName componentName = new ComponentName(pkgName, actName);
Intent intent = new Intent();
intent.setComponent(componentName);
context.startActivity(intent);
((Activity) context).finish();
復制代碼
4,當然,最後,我們也希望可以將其設置為開機啟動:
復制代碼
public class BootReceiver extends BroadcastReceiver{
String myPkgName = "com.example.screenlocker";//#包名
String myActName = "com.example.acts.myService";//#類名
@Override
public void onReceive(Context context, Intent intent) {
//啟動監聽服務
Intent myIntent=new Intent();
myIntent.setAction(myPkgName+"."+myActName);
context.startService(myIntent);
}
}
復制代碼
注意:別忘了在manifest中添加開機啟動的權限哦:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
不過我更推薦在home中加這麼一行代碼:
startService(new Intent(Home.this, ScreenReceiver.class));