標籤:android keyguard
在總結鎖屏代碼之前,有兩個中心思想要銘記於心
A) KeyguardHostView就是我們最終所要展示的介面,所以不論用什麼方法手段,都要將這個KeyguardHostView添加到視窗中,後續填充它,都是細節問題
B) 那麼問題來了,通常我們將一個view添加到視窗中會用什麼方法呢?
答案有兩種 1 WindowManager.addView() 2 LayoutInflater.inflate(resID, parentView, true); 而在鎖屏代碼中這兩種方法都有用到
-----------------------------------------------華麗麗的分割線-------------------------------------------------------------------
接下來用一張圖來解釋流程
-------------------------------又是可愛的分割線--------------------------------------------------------
可以看到, 開機後首先從PhoneWindowManager的systemReady方法調用兩個封裝類(KeyguardServiceDelegate.java KeyguardServiceWrapper.java)之後會調用到KeyguardService.java中的onSystemReady,進而調用鎖屏代碼的總調度使KeyguardViewMediator.java。它就是鎖屏的CEO。做我們軟體的都知道,CEO一般不會太牽涉代碼問題,只管分配,協調工作,客戶/供應鏈的溝通。在KeyguardViewMediator.java中的mExternallyEnabled變數就是總監與客戶談判的籌碼,如果客戶第三方通過KeyguardManager.diableKeyguard()方法禁用系統鎖屏後,此變數會置為false,從而不會繪製系統鎖定畫面,otherwise,將任務大手一甩直接丟給總經理KeyguardViewManager.java.具體代碼如下:
<span style="font-size:12px;"> /** * Enable the keyguard if the settings are appropriate. */ private void doKeyguardLocked(Bundle options) { boolean isSimSecure = mUpdateMonitor.isSimPinSecure(); ///M: if another app is disabling us (except Sim Secure), then don't show if ((!mExternallyEnabled && !isSimSecure)|| PowerOffAlarmManager.isAlarmBoot()) { if (DEBUG) KeyguardUtils.xlogD(TAG, "doKeyguard: not showing because externally disabled"); return; } 。。。 showLocked(options); } private void handleShow(Bundle options) { 。。。 mKeyguardViewManager.show(options); 。。。 }通過以上代碼,所有的任務都已經落實到KeyguardViewManager.java的頭上,接下來看看這個總經理是如何工作的 /** * Show the keyguard. Will handle creating and attaching to the view manager * lazily. */ public synchronized void show(Bundle options) { if (DEBUG) Log.d(TAG, "show(); mKeyguardView=" + mKeyguardView); boolean enableScreenRotation = KeyguardUtils.shouldEnableScreenRotation(mContext); if (DEBUG) Log.d(TAG, "show() query screen rotation after"); /// M: Incoming Indicator for Keyguard Rotation @{ KeyguardUpdateMonitor.getInstance(mContext).setQueryBaseTime(); /// @} <span style="color:#FF0000;">maybeCreateKeyguardLocked(enableScreenRotation, false, options);</span> if (DEBUG) Log.d(TAG, "show() maybeCreateKeyguardLocked finish"); maybeEnableScreenRotation(enableScreenRotation); // Disable common aspects of the system/status/navigation bars that are not appropriate or // useful on any keyguard screen but can be re-shown by dialogs or SHOW_WHEN_LOCKED // activities. Other disabled bits are handled by the KeyguardViewMediator talking // directly to the status bar service. int visFlags = View.STATUS_BAR_DISABLE_HOME; if (shouldEnableTranslucentDecor()) { mWindowLayoutParams.flags |= WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; } if (DEBUG) Log.d(TAG, "show:setSystemUiVisibility(" + Integer.toHexString(visFlags)+")"); mKeyguardHost.setSystemUiVisibility(visFlags); mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams); mKeyguardHost.setVisibility(View.VISIBLE); <span style="color:#FF0000;">mKeyguardView.show();</span> mKeyguardView.requestFocus(); if (DEBUG) Log.d(TAG, "show() exit; mKeyguardView=" + mKeyguardView); }關鍵代碼所以全部貼出來分析分析吧, <span style="color:#009900;">//有一點需要提前注意mKeyguardHost只是一個空View,mKeyguardView才是真正的KeyguardHostView</span>boolean enableScreenRotation用來判斷是否允許旋轉螢幕,KeyguardUpdateMonitor.getInstance(mContext).setQueryBaseTime();此行代碼是為了重新設定query的時間,比如未接來電,未讀簡訊等,之所以重新set是為了仿照iphone手機,繪製鎖屏時,查詢未讀資訊/未接電話的數目是針對本次鎖屏後收到的未讀資訊/未接電話
maybeCreateKeyguardLocked(enableScreenRotation, false, options); 我擦~終於到關鍵代碼了,此方法就是真正建立鎖屏的方法,來一睹芳容吧 private void maybeCreateKeyguardLocked(boolean enableScreenRotation, boolean force, Bundle options) { if (mKeyguardHost != null) { mKeyguardHost.saveHierarchyState(mStateContainer); } if (mKeyguardHost == null) { if (DEBUG) Log.d(TAG, "keyguard host is null, creating it..."); <span style="color:#990000;">mKeyguardHost = new ViewManagerHost(mContext);</span> 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; if (!mNeedsInput) { flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; } final int stretch = ViewGroup.LayoutParams.MATCH_PARENT; final int type = 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 = R.style.Animation_LockScreen; lp.screenOrientation = enableScreenRotation ? ActivityInfo.SCREEN_ORIENTATION_USER : ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; 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; /// M: Poke user activity when operating Keyguard //lp.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY; lp.setTitle("Keyguard"); mWindowLayoutParams = lp; ///M: skip add KeyguardHost into viewManager in AT case if (!KeyguardViewMediator.isKeyguardInActivity) { <span style="color:#CC0000;">mViewManager.addView(mKeyguardHost, lp);</span> } else { if (DEBUG) Log.d(TAG, "skip add mKeyguardHost into mViewManager for testing"); } KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mBackgroundChanger); } /// M: If force and keyguardView is not null, we should relase memory hold by old keyguardview if (force && mKeyguardView != null) { mKeyguardView.cleanUp(); } if (force || mKeyguardView == null) { mKeyguardHost.setCustomBackground(null); mKeyguardHost.removeAllViews(); <span style="color:#CC0000;">inflateKeyguardView(options);</span> mKeyguardView.requestFocus(); } updateUserActivityTimeoutInWindowLayoutParams(); mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams); mKeyguardHost.restoreHierarchyState(mStateContainer); }此處紅色代碼中通過mViewManager.addView(mKeyguardHost, lp);將初始話的mKeyguardHost(空view)通過WindowManager.addView的方式添加到視窗之中
然後在通過inflateKeyguardView(options);方法將真正的KeyguardHostView也添加到mKeyguardHost中, 具體如何添加請看代碼:
private void inflateKeyguardView(Bundle options) {
/// M: add for power-off alarm @{
int resId = R.id.keyguard_host_view;
int layoutId = R.layout.keyguard_host_view;
if(PowerOffAlarmManager.isAlarmBoot()){
resId = R.id.power_off_alarm_host_view;
layoutId = R.layout.mtk_power_off_alarm_host_view;
}
/// @}
View v = mKeyguardHost.findViewById(resId);
if (v != null) {
mKeyguardHost.removeView(v);
}
/// M: Save new orientation
mCreateOrientation = mContext.getResources().getConfiguration().orientation;
mCreateScreenWidthDp = mContext.getResources().getConfiguration().screenWidthDp;
mCreateScreenHeightDp = mContext.getResources().getConfiguration().screenHeightDp;
final LayoutInflater inflater = LayoutInflater.from(mContext);
View view = inflater.inflate(layoutId, mKeyguardHost, true);
mKeyguardView = (KeyguardHostView) view.findViewById(resId);
mKeyguardView.setLockPatternUtils(mLockPatternUtils);
mKeyguardView.setViewMediatorCallback(mViewMediatorCallback);
mKeyguardView.initializeSwitchingUserState(options != null &&
options.getBoolean(IS_SWITCHING_USER));
// HACK
// The keyguard view will have set up window flags in onFinishInflate before we set
// the view mediator callback. Make sure it knows the correct IME state.
if (mViewMediatorCallback != null) {
// Start of cube26 code
if (mLockscreenNotifications)
mNotificationView.setViewMediator(mViewMediatorCallback);
// End of cube26 code
KeyguardPasswordView kpv = (KeyguardPasswordView) mKeyguardView.findViewById(
R.id.keyguard_password_view);
if (kpv != null) {
mViewMediatorCallback.setNeedsInput(kpv.needsInput());
}
}
if (options != null) {
int widgetToShow = options.getInt(LockPatternUtils.KEYGUARD_SHOW_APPWIDGET,
AppWidgetManager.INVALID_APPWIDGET_ID);
if (widgetToShow != AppWidgetManager.INVALID_APPWIDGET_ID) {
mKeyguardView.goToWidget(widgetToShow);
}
}
}
紅色標記處,通過LayoutInflater.inflate(resId, parentView, true)的方式將R.layout.keyguard_host_view添加到mKeyguardHost這個空view中
自此真正的KeyguardHostView已經添加到視窗中,並且通過各中layoutparam將其顯示在最上層,後續的就是如何將其顯示,使用何種方式顯示,比如Slide,Swipe,Password
等等,這些都是細節,後續部落格中將繼續分析
android4.4的Keyguard心得