android4.4的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
等等,这些都是细节,后续博客中将继续分析




郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。