谷歌对Android4.2的版本做了一些改动,突出的改动就是锁屏可以添加widget,即锁屏widget,
这个是谷歌的一个大的改动,先来说说android4.2做了哪些改动?
(1)Lock screen widgets如图:
(2)梦话模式的添加
简单介绍如下:白日梦是一个互动屏幕保护程序模式,当用户的设备开始停靠或充电。
在这种模式下,系统将启动一枕黄粱 - 远程安装的应用程序提供内容服务 - 设备的屏幕保护程序。
用户可以设置应用程序启用白日梦,然后选择显示遐想。
(3)更多显示的支持
(4)Native RTL support从右向左支持,例如印度语,就是这种显示格式。
等等,更多特性请参考官网:http://developer.android.com/about/versions/jelly-bean.html#42-external-display
好了,言归正传,我们来说说Android4.2锁屏的流程:咱们一步一步来说:
Step1:先看第一次开机的加载锁屏的过程,通过PhoneWindowManager.java这个类的systemReady()
这个方法,当系统开机准备好的情况下会调用这个方法,如下:
[java] view plain copy print ?
- public void systemReady() {
- if (mKeyguardMediator != null) {
-
- mKeyguardMediator.onSystemReady();
- }
- synchronized (mLock) {
- updateOrientationListenerLp();
- mSystemReady = true;
- mHandler.post(new Runnable() {
- public void run() {
- updateSettings();
- }
- });
- }
- }
Step2:看注释就知道下一步该干什么了,告诉锁屏的管理者,我准备好了,
该你来控制加载锁屏界面了。接着调用到了KeyguardViewMediator.java这个类的onSystemReady()方法,
如下:
[java] view plain copy print ?
-
-
-
- public void onSystemReady() {
- mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
- synchronized (this) {
- if (DEBUG) Log.d(TAG, "onSystemReady");
- mSystemReady = true;
- mUpdateMonitor.registerCallback(mUpdateCallback);
-
-
-
-
-
-
-
-
-
-
- if (mLockPatternUtils.usingBiometricWeak()
- && mLockPatternUtils.isBiometricWeakInstalled()
- || mLockPatternUtils.usingVoiceWeak()
- && FeatureOption.MTK_VOICE_UNLOCK_SUPPORT) {
- if (DEBUG) Log.d(TAG, "suppressing biometric unlock during boot");
- mUpdateMonitor.setAlternateUnlockEnabled(false);
- } else {
- mUpdateMonitor.setAlternateUnlockEnabled(true);
- }
-
- if (!KeyguardUpdateMonitor.isAlarmBoot()) {
- doKeyguardLocked();
- }
-
- }
-
-
- maybeSendUserPresentBroadcast();
- }
Step3:接着由doKeyguardLocked()这个方法来做启动锁屏界面的预处理,来看看这个方法都做了什么:
[java] view plain copy print ?
- private void doKeyguardLocked() {
- doKeyguardLocked(null);
- }
-
-
-
-
- private void doKeyguardLocked(Bundle options) {
-
- if (!mExternallyEnabled || KeyguardUpdateMonitor.isAlarmBoot()) {
- if (DEBUG) KeyguardUtils.xlogD(TAG, "doKeyguard: not showing because externally disabled");
-
-
-
-
-
-
-
-
-
-
- return;
- }
-
-
- if (mKeyguardViewManager.isShowing()) {
- if (DEBUG) KeyguardUtils.xlogD(TAG, "doKeyguard: not showing because it is already showing");
- return;
- }
-
-
- if (DEBUG) KeyguardUtils.xlogD(TAG, "doKeyguard: get keyguard.no_require_sim property before");
- final boolean requireSim = !SystemProperties.getBoolean("keyguard.no_require_sim",
- false);
- if (DEBUG) KeyguardUtils.xlogD(TAG, "doKeyguard: get keyguard.no_require_sim property after");
- final boolean provisioned = mUpdateMonitor.isDeviceProvisioned();
- final IccCardConstants.State state = mUpdateMonitor.getSimState();
- boolean lockedOrMissing = false;
-
- for (int i = PhoneConstants.GEMINI_SIM_1; i <= KeyguardUtils.getMaxSimId(); i++) {
- lockedOrMissing = (lockedOrMissing || isLockedOrMissingGemini(i, requireSim));
- if (lockedOrMissing) {
- break;
- }
- }
-
- if (DEBUG) KeyguardUtils.xlogD(TAG, "doKeyguard: get sim state after");
-
-
-
-
- boolean keyguardDisable = false;
-
-
- boolean motaUpdateFirst = true;
- if (motaUpdateFirst) {
-
- keyguardDisable = mLockPatternUtils.isLockScreenDisabled();
- } else {
-
- final ContentResolver cr = mContext.getContentResolver();
- String value = Settings.Secure.getString(cr, "lockscreen.disabled");
- boolean booleanValue = false;
- if( null!=value ){
- booleanValue = value.equals("1") ? true :false;
- }
- keyguardDisable = (!mLockPatternUtils.isSecure()) && booleanValue;
- }
-
-
- if (DEBUG) KeyguardUtils.xlogD(TAG, "doKeyguard: keyguardDisable query end");
-
-
- boolean dmLocked = KeyguardUpdateMonitor.getInstance(mContext).dmIsLocked();
- KeyguardUtils.xlogD(TAG, "lockedOrMissing is " + lockedOrMissing + ", requireSim=" + requireSim
- + ", provisioned=" + provisioned + ", keyguardisable=" + keyguardDisable + ", dmLocked=" + dmLocked);
-
- if (!lockedOrMissing && !provisioned && !dmLocked) {
- if (DEBUG) Log.d(TAG, "doKeyguard: not showing because device isn't provisioned"
- + " and the sim is not locked or missing");
- return;
- }
-
-
- if (mUserManager.getUsers(true).size() < 2
- && keyguardDisable && !lockedOrMissing && !KeyguardUpdateMonitor.getInstance(mContext).dmIsLocked()) {
- if (DEBUG) Log.d(TAG, "doKeyguard: not showing because lockscreen is off");
- return;
- }
-
- if (DEBUG) Log.d(TAG, "doKeyguard: showing the lock screen");
- showLocked(options);
- }
Step4、来注意最后调用的这个方法showLocked(options),这个方法是启动锁屏关键的方法,来看看:
[java] view plain copy print ?
-
-
-
-
- private void showLocked(Bundle options) {
- if (DEBUG) KeyguardUtils.xlogD(TAG, "showLocked");
-
- mShowKeyguardWakeLock.acquire();
- Message msg = mHandler.obtainMessage(SHOW, options);
- mHandler.sendMessage(msg);
- }
Step5、这下就通过发送消息来进一步启动锁屏界面,来看看这个mHandler中的SHOW都做了什么:
[java] view plain copy print ?
- public void handleMessage(Message msg) {
- if (DBG_MESSAGE) KeyguardUtils.xlogD(TAG, "handleMessage enter msg name=" + getMessageString(msg));
- switch (msg.what) {
- case SHOW:
- handleShow((Bundle) msg.obj);
- break;
调用
的是handleShow()这个方法:
[java] view plain copy print ?
-
-
-
-
- private void handleShow(Bundle options) {
- synchronized (KeyguardViewMediator.this) {
- if (DEBUG) KeyguardUtils.xlogD(TAG, "handleShow enter");
- if (!mSystemReady) return;
-
- if (mShowing) return;
-
- mKeyguardViewManager.show(options);
-
- if (DEBUG) KeyguardUtils.xlogD(TAG, "handleShow mKeyguardViewManager Show exit");
-
- mShowing = true;
- mKeyguardDonePending = false;
- updateActivityLockScreenState();
- adjustStatusBarLocked();
- userActivity();
- try {
- ActivityManagerNative.getDefault().closeSystemDialogs("lock");
- } catch (RemoteException e) {
- }
-
- if (DEBUG) KeyguardUtils.xlogD(TAG, "handleShow query AlarmBoot before");
-
- if (!KeyguardUpdateMonitor.isAlarmBoot()) {
- playSounds(true);
- } else {
- new Handler().postDelayed(new Runnable() {
- public void run() {
- sendRemoveIPOWinBroadcast();
- startAlarm();
- }
- }, 250);
- }
- mShowKeyguardWakeLock.release();
- if (DEBUG) KeyguardUtils.xlogD(TAG, "handleShow exit");
- }
- }
Step6、接着看mKeyguardViewManager.show(options);这个方法都干了什么:
[java] view plain copy print ?
-
-
-
-
- public synchronized void show(Bundle options) {
- if (DEBUG) KeyguardUtils.xlogD(TAG, "show(); mKeyguardView=" + mKeyguardView);
-
- boolean enableScreenRotation = shouldEnableScreenRotation();
- if (DEBUG) KeyguardUtils.xlogD(TAG, "show() query screen rotation after");
-
-
- KeyguardUpdateMonitor.getInstance(mContext).setQueryBaseTime();
-
- maybeCreateKeyguardLocked(enableScreenRotation, false, options);
-
- if (DEBUG) KeyguardUtils.xlogD(TAG, "show() maybeCreateKeyguardLocked finish");
-
- maybeEnableScreenRotation(enableScreenRotation);
-
-
-
-
-
- final int visFlags = View.STATUS_BAR_DISABLE_HOME;
- if (DEBUG) KeyguardUtils.xlogD(TAG, "show:setSystemUiVisibility(" + Integer.toHexString(visFlags)+")");
- mKeyguardHost.setSystemUiVisibility(visFlags);
-
- mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
- mKeyguardHost.setVisibility(View.VISIBLE);
- mKeyguardView.show();
- mKeyguardView.requestFocus();
- if (DEBUG) KeyguardUtils.xlogD(TAG, "show() exit; mKeyguardView=" + mKeyguardView);
- }
Step7,、这下终于看到如山真面目了,看里面的方法maybeCreateKeyguardLocked()
这个是真正起作用的地方:
[java] view plain copy print ?
- "FONT-SIZE: 18px">private void maybeCreateKeyguardLocked(boolean enableScreenRotation, boolean force,
- Bundle options) {
- final boolean isActivity = (mContext instanceof Activity);
-
- if (mKeyguardHost != null) {
- mKeyguardHost.saveHierarchyState(mStateContainer);
- }
-
- if (mKeyguardHost == null) {
- if (DEBUG) KeyguardUtils.xlogD(TAG, "keyguard host is null, creating it...");
-
- mKeyguardHost = new ViewManagerHost(mContext);
-
- int flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
- | WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN
- | WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
-
-
- KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
- if (monitor.dmIsLocked()) {
- if (DEBUG) KeyguardUtils.xlogD(TAG, "show(); dmIsLocked ");
- flags &= ~WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN;
- flags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
- flags |= WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
- } else if (KeyguardUpdateMonitor.isAlarmBoot()) {
- if (DEBUG) KeyguardUtils.xlogD(TAG, "show(); AlarmBoot ");
- flags &= ~WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
- flags &= ~WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
- flags |= WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN;
- }
-
- if (!mNeedsInput) {
- flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
- }
- if (ActivityManager.isHighEndGfx()) {
- flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
- }
-
- final int stretch = ViewGroup.LayoutParams.MATCH_PARENT;
- final int type = isActivity ? WindowManager.LayoutParams.TYPE_APPLICATION
- : WindowManager.LayoutParams.TYPE_KEYGUARD;
- WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- stretch, stretch, type, flags, PixelFormat.TRANSLUCENT);
- lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
- lp.windowAnimations = com.android.internal.R.style.Animation_LockScreen;
- if (ActivityManager.isHighEndGfx()) {
- lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
- lp.privateFlags |=
- WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED;
- }
- lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SET_NEEDS_MENU_KEY;
- if (isActivity) {
- lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
- }
-
-
- lp.setTitle(isActivity ? "KeyguardMock" : "Keyguard");
- mWindowLayoutParams = lp;
- mViewManager.addView(mKeyguardHost, lp);
- }
-
-
- if (force && mKeyguardView != null) {
- mKeyguardView.cleanUp();
- }
-
- if (force || mKeyguardView == null) {
- inflateKeyguardView(options);
- mKeyguardView.requestFocus();
- }
- updateUserActivityTimeoutInWindowLayoutParams();
- mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
-
- mKeyguardHost.restoreHierarchyState(mStateContainer);
- }
这下通过 mViewManager.addView(mKeyguardHost, lp);这个方法真正地把锁屏界面添加到屏幕上,
其实这个就是个view,挡在了手机的屏幕的最上方。而这个mKeyguardHost就是锁屏的根。
而第一次加载的时候mKeyguardView为空,调用inflateKeyguardView(),初始化锁屏的view。
Step8、来看看这个inflateKeyguardView()这个方法都加载了哪个布局:
[java] view plain copy print ?
- private void inflateKeyguardView(Bundle options) {
-
- int resId = R.id.keyguard_host_view;
- int layoutId = R.layout.keyguard_host_view;
- if(KeyguardUpdateMonitor.isAlarmBoot()){
- layoutId = com.mediatek.internal.R.layout.power_off_alarm_host_view;
- resId = com.mediatek.internal.R.id.keyguard_host_view;
- }
-
- View v = mKeyguardHost.findViewById(resId);
- if (v != null) {
- mKeyguardHost.removeView(v);
- }
-
- if (false) Slog.d(TAG, "inflateKeyguardView: b/7094175 mContext.config="
- + mContext.getResources().getConfiguration());
-
-
- mCreateOrientation = mContext.getResources().getConfiguration().orientation;
-
- final LayoutInflater inflater = LayoutInflater.from(mContext);
- View view = inflater.inflate(layoutId, mKeyguardHost, true);
- mKeyguardView = (KeyguardHostView) view.findViewById(resId);
- mKeyguardView.setLockPatternUtils(mLockPatternUtils);
- mKeyguardView.setViewMediatorCallback(mViewMediatorCallback);
-
-
-
-
- if (mViewMediatorCallback != null) {
- KeyguardPasswordView kpv = (KeyguardPasswordView) mKeyguardView.findViewById(
- R.id.keyguard_password_view);
-
- if (kpv != null) {
- mViewMediatorCallback.setNeedsInput(kpv.needsInput());
- }
- }
-
-
- updateKeyguardViewFromOptions(options);
- }
这个加载了keyguard_host_view这个layout,来看看这个布局是怎么写的:
[html] view plain copy print ?
- <com.android.internal.policy.impl.keyguard.KeyguardHostView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/res/android"
- android:id="@+id/keyguard_host_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:gravity="center_horizontal"
- android:orientation="vertical">
-
- <com.android.internal.policy.impl.keyguard.SlidingChallengeLayout
- android:id="@+id/sliding_layout"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- androidprv:layout_childType="mediatekLayerBackground">
- FrameLayout>
-
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- androidprv:layout_childType="pageDeleteDropTarget">
- <include layout="@layout/keyguard_widget_remove_drop_target"
- android:id="@+id/keyguard_widget_pager_delete_target"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="top|center_horizontal" />
- FrameLayout>
-
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- androidprv:layout_childType="widgets">
- <include layout="@layout/keyguard_widget_pager"
- android:id="@+id/app_widget_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center"/>
- FrameLayout>
-
- <View android:layout_width="match_parent"
- android:layout_height="match_parent"
- androidprv:layout_childType="scrim"
- android:background="#99000000" />
-
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- androidprv:layout_childType="mediatekLayerForeground">
- FrameLayout>
-
- <com.android.internal.policy.impl.keyguard.KeyguardSecurityContainer
- android:id="@+id/keyguard_security_container"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_maxHeight="@dimen/keyguard_security_height"
- androidprv:layout_childType="challenge"
- android:padding="0dp"
- android:gravity="bottom|center_horizontal">
- <com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper
- android:id="@+id/view_flipper"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:clipToPadding="false"
- android:paddingTop="@dimen/keyguard_security_view_margin"
- android:gravity="center">
- com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper>
- com.android.internal.policy.impl.keyguard.KeyguardSecurityContainer>
-
- <ImageButton
- android:layout_width="match_parent"
- android:layout_height="@dimen/kg_widget_pager_bottom_padding"
- androidprv:layout_childType="expandChallengeHandle"
- android:focusable="true"
- android:background="@null"
- android:src="@drawable/keyguard_expand_challenge_handle"
- android:scaleType="center"
- android:contentDescription="@string/keyguard_accessibility_expand_lock_area" />
-
- com.android.internal.policy.impl.keyguard.SlidingChallengeLayout>
- com.android.internal.policy.impl.keyguard.KeyguardHostView>
而这个KeyguardHostView.java就是锁屏的真正的处理的view,该添加什么样的锁屏,例如:PIN,Pattern,PUK,Password等等,
都是由它来控制的,最后会调用到getLayoutIdFor()这个方法,来启动那种锁屏界面,如下:
[java] view plain copy print ?
- private int getLayoutIdFor(SecurityMode securityMode) {
- switch (securityMode) {
- case None: return R.layout.keyguard_selector_view;
- case Pattern: return R.layout.keyguard_pattern_view;
- case PIN: return R.layout.keyguard_pin_view;
- case Password: return R.layout.keyguard_password_view;
- case Biometric: return R.layout.keyguard_face_unlock_view;
- case Account: return R.layout.keyguard_account_view;
-
-
-
- case SimPinPukMe1: return com.mediatek.internal.R.layout.keyguard_sim_pin_puk_view;
- case SimPinPukMe2: return com.mediatek.internal.R.layout.keyguard_sim_pin_puk_view;
-
- case SimPinPukMe3: return com.mediatek.internal.R.layout.keyguard_sim_pin_puk_view;
- case SimPinPukMe4: return com.mediatek.internal.R.layout.keyguard_sim_pin_puk_view;
-
-
-
- case AlarmBoot: return com.mediatek.internal.R.layout.power_off_alarm_view;
-
-
- case Voice: return R.layout.zz_keyguard_voice_unlock_view;
- default:
- return 0;
- }
- }
到这,锁屏已经初始化完了,要想下面接着分析,估计大家应该都能分析过去了;
特别说明:
1、加载锁屏widget的地方在KeyguardHostView.java的onFinishInflate()中,
调用的addDefaultWidget()这个方法中添加了单click事件,最后调
用到KeyguardActivityLauncher.java的launcherWidgetPicker()这个方法;
2、要想你写的widget能被锁屏widget过滤出来,只需要在wdget的xml中添加一个属性即可:
android:widgetCategory="home_screen|keyguard",这样你写的桌面widget,
也能在锁屏wiget过滤出来,具体布局需要你微调下;
添加一张图,