Android4.2锁屏流程【Android锁屏解析三】

谷歌对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()这个方法,当系统开机准备好的情况下会调用这个方法,如下:

  1. public void systemReady() {  
  2.        if (mKeyguardMediator != null) {  
  3.            // tell the keyguard   
  4.            mKeyguardMediator.onSystemReady();  
  5.        }  
  6.        synchronized (mLock) {  
  7.            updateOrientationListenerLp();  
  8.            mSystemReady = true;  
  9.            mHandler.post(new Runnable() {  
  10.                public void run() {  
  11.                    updateSettings();  
  12.                }  
  13.            });  
  14.        }  
  15.    }  
 public void systemReady() {
        if (mKeyguardMediator != null) {
            // tell the keyguard
            mKeyguardMediator.onSystemReady();
        }
        synchronized (mLock) {
            updateOrientationListenerLp();
            mSystemReady = true;
            mHandler.post(new Runnable() {
                public void run() {
                    updateSettings();
                }
            });
        }
    }


Step2:看注释就知道下一步该干什么了,告诉锁屏的管理者,我准备好了,该你来控制加载锁屏界面了。接着调用到了KeyguardViewMediator.java这个类的onSystemReady()方法,如下:

  1. /** 
  2.      * Let us know that the system is ready after startup. 
  3.      */  
  4.     public void onSystemReady() {  
  5.         mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);  
  6.         synchronized (this) {  
  7.             if (DEBUG) Log.d(TAG, "onSystemReady");  
  8.             mSystemReady = true;  
  9.             mUpdateMonitor.registerCallback(mUpdateCallback);  
  10.   
  11.             // Suppress biometric unlock right after boot until things have settled if it is the   
  12.             // selected security method, otherwise unsuppress it.  It must be unsuppressed if it is   
  13.             // not the selected security method for the following reason:  if the user starts   
  14.             // without a screen lock selected, the biometric unlock would be suppressed the first   
  15.             // time they try to use it.   
  16.             //   
  17.             // Note that the biometric unlock will still not show if it is not the selected method.   
  18.             // Calling setAlternateUnlockEnabled(true) simply says don't suppress it if it is the   
  19.             // selected method.   
  20.             if (mLockPatternUtils.usingBiometricWeak()  
  21.                     && mLockPatternUtils.isBiometricWeakInstalled()  
  22.                     || mLockPatternUtils.usingVoiceWeak()  
  23.                     && FeatureOption.MTK_VOICE_UNLOCK_SUPPORT) {  
  24.                 if (DEBUG) Log.d(TAG, "suppressing biometric unlock during boot");  
  25.                 mUpdateMonitor.setAlternateUnlockEnabled(false);  
  26.             } else {  
  27.                 mUpdateMonitor.setAlternateUnlockEnabled(true);  
  28.             }  
  29.             /// M: power-off alarm @{   
  30.             if (!KeyguardUpdateMonitor.isAlarmBoot()) {  
  31.                 doKeyguardLocked();  
  32.             }  
  33.             /// @}   
  34.         }  
  35.         // Most services aren't available until the system reaches the ready state, so we   
  36.         // send it here when the device first boots.   
  37.         maybeSendUserPresentBroadcast();  
  38.     }  
/**
     * Let us know that the system is ready after startup.
     */
    public void onSystemReady() {
        mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
        synchronized (this) {
            if (DEBUG) Log.d(TAG, "onSystemReady");
            mSystemReady = true;
            mUpdateMonitor.registerCallback(mUpdateCallback);

            // Suppress biometric unlock right after boot until things have settled if it is the
            // selected security method, otherwise unsuppress it.  It must be unsuppressed if it is
            // not the selected security method for the following reason:  if the user starts
            // without a screen lock selected, the biometric unlock would be suppressed the first
            // time they try to use it.
            //
            // Note that the biometric unlock will still not show if it is not the selected method.
            // Calling setAlternateUnlockEnabled(true) simply says don't suppress it if it is the
            // selected method.
            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);
            }
            /// M: power-off alarm @{
            if (!KeyguardUpdateMonitor.isAlarmBoot()) {
                doKeyguardLocked();
            }
            /// @}
        }
        // Most services aren't available until the system reaches the ready state, so we
        // send it here when the device first boots.
        maybeSendUserPresentBroadcast();
    }

Step3:接着由doKeyguardLocked()这个方法来做启动锁屏界面的预处理,来看看这个方法都做了什么:

  1. private void doKeyguardLocked() {  
  2.     doKeyguardLocked(null);  
  3. }  
  4.   
  5. /** 
  6.  * Enable the keyguard if the settings are appropriate. 
  7.  */  
  8. private void doKeyguardLocked(Bundle options) {  
  9.     // if another app is disabling us, don't show   
  10.     if (!mExternallyEnabled || KeyguardUpdateMonitor.isAlarmBoot()) {  
  11.         if (DEBUG) KeyguardUtils.xlogD(TAG, "doKeyguard: not showing because externally disabled");  
  12.   
  13.         // note: we *should* set mNeedToReshowWhenReenabled=true here, but that makes   
  14.         // for an occasional ugly flicker in this situation:   
  15.         // 1) receive a call with the screen on (no keyguard) or make a call   
  16.         // 2) screen times out   
  17.         // 3) user hits key to turn screen back on   
  18.         // instead, we reenable the keyguard when we know the screen is off and the call   
  19.         // ends (see the broadcast receiver below)   
  20.         // TODO: clean this up when we have better support at the window manager level   
  21.         // for apps that wish to be on top of the keyguard   
  22.         return;  
  23.     }  
  24.   
  25.     // if the keyguard is already showing, don't bother   
  26.     if (mKeyguardViewManager.isShowing()) {  
  27.         if (DEBUG) KeyguardUtils.xlogD(TAG, "doKeyguard: not showing because it is already showing");  
  28.         return;  
  29.     }  
  30.   
  31.     // if the setup wizard hasn't run yet, don't show   
  32.     if (DEBUG) KeyguardUtils.xlogD(TAG, "doKeyguard: get keyguard.no_require_sim property before");  
  33.     final boolean requireSim = !SystemProperties.getBoolean("keyguard.no_require_sim",  
  34.             false);  
  35.     if (DEBUG) KeyguardUtils.xlogD(TAG, "doKeyguard: get keyguard.no_require_sim property after");  
  36.     final boolean provisioned = mUpdateMonitor.isDeviceProvisioned();  
  37.     final IccCardConstants.State state = mUpdateMonitor.getSimState();  
  38.     boolean lockedOrMissing = false;  
  39.     /// M: Support GeminiPlus   
  40.     for (int i = PhoneConstants.GEMINI_SIM_1; i <= KeyguardUtils.getMaxSimId(); i++) {  
  41.         lockedOrMissing = (lockedOrMissing || isLockedOrMissingGemini(i, requireSim));  
  42.         if (lockedOrMissing) {  
  43.             break;  
  44.         }  
  45.     }  
  46.       
  47.     if (DEBUG) KeyguardUtils.xlogD(TAG, "doKeyguard: get sim state after");  
  48.   
  49.     /// M: MTK MOTA UPDATE when on ics2 keygaurd set none,update to JB,the keyguard will show LockScreen.   
  50.     /// MTK MOTA UPDATE when the phone first boot,check the settingDB mirged or not ,because mota update,   
  51.     /// the settingdb migrate slow than keygaurd(timing sequence problem) @{   
  52.     boolean keyguardDisable = false;  
  53.       
  54.     /*************************************TODO   
  55.     boolean motaUpdateFirst = true;//mLockPatternUtils.isDbMigrated();   
  56.     if (motaUpdateFirst) {  
  57.         /// DB mogi done   
  58.         keyguardDisable = mLockPatternUtils.isLockScreenDisabled();  
  59.     } else {  
  60.         /// DB not mogi   
  61.         final ContentResolver cr = mContext.getContentResolver();  
  62.         String value = Settings.Secure.getString(cr, "lockscreen.disabled");  
  63.         boolean booleanValue = false;  
  64.         ifnull!=value ){  
  65.             booleanValue = value.equals("1") ? true :false;  
  66.         }  
  67.         keyguardDisable = (!mLockPatternUtils.isSecure()) && booleanValue;  
  68.     }  
  69.     /// @}   
  70.       
  71.     if (DEBUG) KeyguardUtils.xlogD(TAG, "doKeyguard: keyguardDisable query end");  
  72.       
  73.     /// M: Add new condition DM lock is not true   
  74.     boolean dmLocked = KeyguardUpdateMonitor.getInstance(mContext).dmIsLocked();  
  75.     KeyguardUtils.xlogD(TAG, "lockedOrMissing is " + lockedOrMissing + ", requireSim=" + requireSim  
  76.         + ", provisioned=" + provisioned + ", keyguardisable=" + keyguardDisable + ", dmLocked=" + dmLocked);  
  77.       
  78.     if (!lockedOrMissing && !provisioned && !dmLocked) {  
  79.         if (DEBUG) Log.d(TAG, "doKeyguard: not showing because device isn't provisioned"  
  80.                 + " and the sim is not locked or missing");  
  81.         return;  
  82.     }  
  83.   
  84.     /// M: Add a new condition DM lock is not on, or user can still bypass dm lock when Keygaurd is disabled   
  85.     if (mUserManager.getUsers(true).size() < 2  
  86.             && keyguardDisable && !lockedOrMissing && !KeyguardUpdateMonitor.getInstance(mContext).dmIsLocked()) {  
  87.         if (DEBUG) Log.d(TAG, "doKeyguard: not showing because lockscreen is off");  
  88.         return;  
  89.     }  
  90.   
  91.     if (DEBUG) Log.d(TAG, "doKeyguard: showing the lock screen");  
  92.     showLocked(options);  
  93. }  
    private void doKeyguardLocked() {
        doKeyguardLocked(null);
    }

    /**
     * Enable the keyguard if the settings are appropriate.
     */
    private void doKeyguardLocked(Bundle options) {
        // if another app is disabling us, don't show
        if (!mExternallyEnabled || KeyguardUpdateMonitor.isAlarmBoot()) {
            if (DEBUG) KeyguardUtils.xlogD(TAG, "doKeyguard: not showing because externally disabled");

            // note: we *should* set mNeedToReshowWhenReenabled=true here, but that makes
            // for an occasional ugly flicker in this situation:
            // 1) receive a call with the screen on (no keyguard) or make a call
            // 2) screen times out
            // 3) user hits key to turn screen back on
            // instead, we reenable the keyguard when we know the screen is off and the call
            // ends (see the broadcast receiver below)
            // TODO: clean this up when we have better support at the window manager level
            // for apps that wish to be on top of the keyguard
            return;
        }

        // if the keyguard is already showing, don't bother
        if (mKeyguardViewManager.isShowing()) {
            if (DEBUG) KeyguardUtils.xlogD(TAG, "doKeyguard: not showing because it is already showing");
            return;
        }

        // if the setup wizard hasn't run yet, don't show
        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;
        /// M: Support GeminiPlus
        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");

        /// M: MTK MOTA UPDATE when on ics2 keygaurd set none,update to JB,the keyguard will show LockScreen.
        /// MTK MOTA UPDATE when the phone first boot,check the settingDB mirged or not ,because mota update,
        /// the settingdb migrate slow than keygaurd(timing sequence problem) @{
        boolean keyguardDisable = false;
        
        /*************************************TODO
        boolean motaUpdateFirst = true;//mLockPatternUtils.isDbMigrated();
        if (motaUpdateFirst) {
            /// DB mogi done
            keyguardDisable = mLockPatternUtils.isLockScreenDisabled();
        } else {
            /// DB not mogi
            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");
        
        /// M: Add new condition DM lock is not true
        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;
        }

        /// M: Add a new condition DM lock is not on, or user can still bypass dm lock when Keygaurd is disabled
        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),这个方法是启动锁屏关键的方法,来看看:

  1. /** 
  2.     * Send message to keyguard telling it to show itself 
  3.     * @see #handleShow() 
  4.     */  
  5.    private void showLocked(Bundle options) {  
  6.        if (DEBUG) KeyguardUtils.xlogD(TAG, "showLocked");  
  7.        // ensure we stay awake until we are finished displaying the keyguard   
  8.        mShowKeyguardWakeLock.acquire();  
  9.        Message msg = mHandler.obtainMessage(SHOW, options);  
  10.        mHandler.sendMessage(msg);  
  11.    }  
 /**
     * Send message to keyguard telling it to show itself
     * @see #handleShow()
     */
    private void showLocked(Bundle options) {
        if (DEBUG) KeyguardUtils.xlogD(TAG, "showLocked");
        // ensure we stay awake until we are finished displaying the keyguard
        mShowKeyguardWakeLock.acquire();
        Message msg = mHandler.obtainMessage(SHOW, options);
        mHandler.sendMessage(msg);
    }

Step5、这下就通过发送消息来进一步启动锁屏界面,来看看这个mHandler中的SHOW都做了什么:

  1. public void handleMessage(Message msg) {  
  2.             if (DBG_MESSAGE) KeyguardUtils.xlogD(TAG, "handleMessage enter msg name=" + getMessageString(msg));  
  3.             switch (msg.what) {  
  4.                 case SHOW:  
  5.                     handleShow((Bundle) msg.obj);  
  6.                     break;  
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()这个方法:

  1. /** 
  2.      * Handle message sent by {@link #showLocked}. 
  3.      * @see #SHOW 
  4.      */  
  5.     private void handleShow(Bundle options) {  
  6.         synchronized (KeyguardViewMediator.this) {  
  7.             if (DEBUG) KeyguardUtils.xlogD(TAG, "handleShow enter");  
  8.             if (!mSystemReady) return;  
  9.             /// M: if already showing, just return   
  10.             if (mShowing) return;  
  11.   
  12.             mKeyguardViewManager.show(options);  
  13.               
  14.             if (DEBUG) KeyguardUtils.xlogD(TAG, "handleShow mKeyguardViewManager Show exit");  
  15.               
  16.             mShowing = true;  
  17.             mKeyguardDonePending = false;  
  18.             updateActivityLockScreenState();  
  19.             adjustStatusBarLocked();  
  20.             userActivity();  
  21.             try {  
  22.                 ActivityManagerNative.getDefault().closeSystemDialogs("lock");  
  23.             } catch (RemoteException e) {  
  24.             }  
  25.   
  26.             if (DEBUG) KeyguardUtils.xlogD(TAG, "handleShow query AlarmBoot before");  
  27.             // Do this at the end to not slow down display of the keyguard.   
  28.             if (!KeyguardUpdateMonitor.isAlarmBoot()) {  
  29.                 playSounds(true);  
  30.             } else {  
  31.                 new Handler().postDelayed(new Runnable() {  
  32.                     public void run() {  
  33.                         sendRemoveIPOWinBroadcast();  
  34.                         startAlarm();  
  35.                     }  
  36.                 }, 250);  
  37.             }  
  38.             mShowKeyguardWakeLock.release();  
  39.             if (DEBUG) KeyguardUtils.xlogD(TAG, "handleShow exit");  
  40.         }  
  41.     }  
/**
     * Handle message sent by {@link #showLocked}.
     * @see #SHOW
     */
    private void handleShow(Bundle options) {
        synchronized (KeyguardViewMediator.this) {
            if (DEBUG) KeyguardUtils.xlogD(TAG, "handleShow enter");
            if (!mSystemReady) return;
            /// M: if already showing, just 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");
            // Do this at the end to not slow down display of the keyguard.
            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);这个方法都干了什么:

  1. /** 
  2.     * Show the keyguard.  Will handle creating and attaching to the view manager 
  3.     * lazily. 
  4.     */  
  5.    public synchronized void show(Bundle options) {  
  6.        if (DEBUG) KeyguardUtils.xlogD(TAG, "show(); mKeyguardView=" + mKeyguardView);  
  7.   
  8.        boolean enableScreenRotation = shouldEnableScreenRotation();  
  9.        if (DEBUG) KeyguardUtils.xlogD(TAG, "show() query screen rotation after");  
  10.   
  11.        /// M: Incoming Indicator for Keyguard Rotation @{   
  12.        KeyguardUpdateMonitor.getInstance(mContext).setQueryBaseTime();  
  13.        /// @}   
  14.        maybeCreateKeyguardLocked(enableScreenRotation, false, options);  
  15.          
  16.        if (DEBUG) KeyguardUtils.xlogD(TAG, "show() maybeCreateKeyguardLocked finish");  
  17.          
  18.        maybeEnableScreenRotation(enableScreenRotation);  
  19.   
  20.        // Disable common aspects of the system/status/navigation bars that are not appropriate or   
  21.        // useful on any keyguard screen but can be re-shown by dialogs or SHOW_WHEN_LOCKED   
  22.        // activities. Other disabled bits are handled by the KeyguardViewMediator talking   
  23.        // directly to the status bar service.   
  24.        final int visFlags = View.STATUS_BAR_DISABLE_HOME;  
  25.        if (DEBUG) KeyguardUtils.xlogD(TAG, "show:setSystemUiVisibility(" + Integer.toHexString(visFlags)+")");  
  26.        mKeyguardHost.setSystemUiVisibility(visFlags);  
  27.   
  28.        mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);  
  29.        mKeyguardHost.setVisibility(View.VISIBLE);  
  30.        mKeyguardView.show();  
  31.        mKeyguardView.requestFocus();  
  32.        if (DEBUG) KeyguardUtils.xlogD(TAG, "show() exit; mKeyguardView=" + mKeyguardView);  
  33.    }  
 /**
     * Show the keyguard.  Will handle creating and attaching to the view manager
     * lazily.
     */
    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");

        /// M: Incoming Indicator for Keyguard Rotation @{
        KeyguardUpdateMonitor.getInstance(mContext).setQueryBaseTime();
        /// @}
        maybeCreateKeyguardLocked(enableScreenRotation, false, options);
        
        if (DEBUG) KeyguardUtils.xlogD(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.
        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()这个是真正起作用的地方:

  1. <SPAN style="FONT-SIZE: 18px">private void maybeCreateKeyguardLocked(boolean enableScreenRotation, boolean force,  
  2.             Bundle options) {  
  3.         final boolean isActivity = (mContext instanceof Activity); // for test activity   
  4.   
  5.         if (mKeyguardHost != null) {  
  6.             mKeyguardHost.saveHierarchyState(mStateContainer);  
  7.         }  
  8.   
  9.         if (mKeyguardHost == null) {  
  10.             if (DEBUG) KeyguardUtils.xlogD(TAG, "keyguard host is null, creating it...");  
  11.   
  12.             mKeyguardHost = new ViewManagerHost(mContext);  
  13.   
  14.             int flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN  
  15.                     | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR  
  16.                     | WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN  
  17.                     | WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;  
  18.   
  19.             /// M: Modify to support DM lock, hide statusbr when dm lock power on @{   
  20.             KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);  
  21.             if (monitor.dmIsLocked()) { //in the first created   
  22.                 if (DEBUG) KeyguardUtils.xlogD(TAG, "show(); dmIsLocked ");  
  23.                 flags &= ~WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN;  
  24.                 flags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;  
  25.                 flags |= WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;  
  26.             } else if (KeyguardUpdateMonitor.isAlarmBoot()) {  
  27.                 if (DEBUG) KeyguardUtils.xlogD(TAG, "show(); AlarmBoot ");  
  28.                 flags &= ~WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;  
  29.                 flags &= ~WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;  
  30.                 flags |= WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN;  
  31.             }  
  32.             /// M: @}   
  33.             if (!mNeedsInput) {  
  34.                 flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;  
  35.             }  
  36.             if (ActivityManager.isHighEndGfx()) {  
  37.                 flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;  
  38.             }  
  39.   
  40.             final int stretch = ViewGroup.LayoutParams.MATCH_PARENT;  
  41.             final int type = isActivity ? WindowManager.LayoutParams.TYPE_APPLICATION  
  42.                     : WindowManager.LayoutParams.TYPE_KEYGUARD;  
  43.             WindowManager.LayoutParams lp = new WindowManager.LayoutParams(  
  44.                     stretch, stretch, type, flags, PixelFormat.TRANSLUCENT);  
  45.             lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;  
  46.             lp.windowAnimations = com.android.internal.R.style.Animation_LockScreen;  
  47.             if (ActivityManager.isHighEndGfx()) {  
  48.                 lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;  
  49.                 lp.privateFlags |=  
  50.                         WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED;  
  51.             }  
  52.             lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SET_NEEDS_MENU_KEY;  
  53.             if (isActivity) {  
  54.                 lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;  
  55.             }  
  56.             /// M: Poke user activity when operating Keyguard   
  57.             //lp.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY;   
  58.             lp.setTitle(isActivity ? "KeyguardMock" : "Keyguard");  
  59.             mWindowLayoutParams = lp;  
  60.             mViewManager.addView(mKeyguardHost, lp);  
  61.         }  
  62.           
  63.         /// M: If force and keyguardView is not null, we should relase memory hold by old keyguardview   
  64.         if (force && mKeyguardView != null) {  
  65.             mKeyguardView.cleanUp();  
  66.         }  
  67.   
  68.         if (force || mKeyguardView == null) {  
  69.             inflateKeyguardView(options);  
  70.             mKeyguardView.requestFocus();  
  71.         }  
  72.         updateUserActivityTimeoutInWindowLayoutParams();  
  73.         mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);  
  74.   
  75.         mKeyguardHost.restoreHierarchyState(mStateContainer);  
  76.     }</SPAN>  
<span style="FONT-SIZE: 18px">private void maybeCreateKeyguardLocked(boolean enableScreenRotation, boolean force,
            Bundle options) {
        final boolean isActivity = (mContext instanceof Activity); // for test 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;

            /// M: Modify to support DM lock, hide statusbr when dm lock power on @{
            KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
            if (monitor.dmIsLocked()) { //in the first created
                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;
            }
            /// M: @}
            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;
            }
            /// M: Poke user activity when operating Keyguard
            //lp.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY;
            lp.setTitle(isActivity ? "KeyguardMock" : "Keyguard");
            mWindowLayoutParams = lp;
            mViewManager.addView(mKeyguardHost, lp);
        }
        
        /// 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) {
            inflateKeyguardView(options);
            mKeyguardView.requestFocus();
        }
        updateUserActivityTimeoutInWindowLayoutParams();
        mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);

        mKeyguardHost.restoreHierarchyState(mStateContainer);
    }</span>
这下通过 mViewManager.addView(mKeyguardHost, lp);这个方法真正地把锁屏界面添加到屏幕上,其实这个就是个view,挡在了手机的屏幕的最上方。而这个mKeyguardHost就是锁屏的根。而第一次加载的时候 mKeyguardView为空,调用 inflateKeyguardView(),初始化锁屏的view。


Step8、来看看这个inflateKeyguardView()这个方法都加载了哪个布局:

  1. private void inflateKeyguardView(Bundle options) {  
  2.         /// M: add for power-off alarm @{   
  3.         int resId = R.id.keyguard_host_view;  
  4.         int layoutId = R.layout.keyguard_host_view;  
  5.         if(KeyguardUpdateMonitor.isAlarmBoot()){  
  6.             layoutId = com.mediatek.internal.R.layout.power_off_alarm_host_view;  
  7.             resId = com.mediatek.internal.R.id.keyguard_host_view;  
  8.         }  
  9.         /// @}   
  10.         View v = mKeyguardHost.findViewById(resId);  
  11.         if (v != null) {  
  12.             mKeyguardHost.removeView(v);  
  13.         }  
  14.         // TODO: Remove once b/7094175 is fixed   
  15.         if (false) Slog.d(TAG, "inflateKeyguardView: b/7094175 mContext.config="  
  16.                 + mContext.getResources().getConfiguration());  
  17.           
  18.         /// M: Save new orientation   
  19.         mCreateOrientation = mContext.getResources().getConfiguration().orientation;  
  20.           
  21.         final LayoutInflater inflater = LayoutInflater.from(mContext);  
  22.         View view = inflater.inflate(layoutId, mKeyguardHost, true);  
  23.         mKeyguardView = (KeyguardHostView) view.findViewById(resId);  
  24.         mKeyguardView.setLockPatternUtils(mLockPatternUtils);  
  25.         mKeyguardView.setViewMediatorCallback(mViewMediatorCallback);  
  26.   
  27.         // HACK   
  28.         // The keyguard view will have set up window flags in onFinishInflate before we set   
  29.         // the view mediator callback. Make sure it knows the correct IME state.   
  30.         if (mViewMediatorCallback != null) {  
  31.             KeyguardPasswordView kpv = (KeyguardPasswordView) mKeyguardView.findViewById(  
  32.                     R.id.keyguard_password_view);  
  33.   
  34.             if (kpv != null) {  
  35.                 mViewMediatorCallback.setNeedsInput(kpv.needsInput());  
  36.             }  
  37.         }  
  38.   
  39.         /// Extract this block to a single function   
  40.         updateKeyguardViewFromOptions(options);  
  41.     }  
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(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);
        }
        // TODO: Remove once b/7094175 is fixed
        if (false) Slog.d(TAG, "inflateKeyguardView: b/7094175 mContext.config="
                + mContext.getResources().getConfiguration());
        
        /// M: Save new orientation
        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);

        // 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) {
            KeyguardPasswordView kpv = (KeyguardPasswordView) mKeyguardView.findViewById(
                    R.id.keyguard_password_view);

            if (kpv != null) {
                mViewMediatorCallback.setNeedsInput(kpv.needsInput());
            }
        }

        /// Extract this block to a single function
        updateKeyguardViewFromOptions(options);
    }
这个加载了keyguard_host_view这个layout,来看看这个布局是怎么写的:

  1. <com.android.internal.policy.impl.keyguard.KeyguardHostView  
  2.     xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     xmlns:androidprv="http://schemas.android.com/apk/res/android"  
  4.     android:id="@+id/keyguard_host_view"  
  5.     android:layout_width="match_parent"  
  6.     android:layout_height="match_parent"  
  7.     android:gravity="center_horizontal"  
  8.     android:orientation="vertical">  
  9.   
  10.     <com.android.internal.policy.impl.keyguard.SlidingChallengeLayout  
  11.         android:id="@+id/sliding_layout"  
  12.         android:layout_width="match_parent"  
  13.         android:layout_height="match_parent">  
  14.           
  15.         <FrameLayout  
  16.             android:layout_width="match_parent"  
  17.             android:layout_height="match_parent"  
  18.             androidprv:layout_childType="mediatekLayerBackground">  
  19.         </FrameLayout>  
  20.   
  21.         <FrameLayout  
  22.             android:layout_width="match_parent"  
  23.             android:layout_height="wrap_content"  
  24.             androidprv:layout_childType="pageDeleteDropTarget">  
  25.             <include layout="@layout/keyguard_widget_remove_drop_target"  
  26.                 android:id="@+id/keyguard_widget_pager_delete_target"  
  27.                 android:layout_width="wrap_content"  
  28.                 android:layout_height="wrap_content"  
  29.                 android:layout_gravity="top|center_horizontal" />  
  30.         </FrameLayout>  
  31.   
  32.         <FrameLayout  
  33.             android:layout_width="match_parent"  
  34.             android:layout_height="match_parent"  
  35.             androidprv:layout_childType="widgets">  
  36.             <include layout="@layout/keyguard_widget_pager"  
  37.                 android:id="@+id/app_widget_container"  
  38.                 android:layout_width="match_parent"  
  39.                 android:layout_height="match_parent"  
  40.                 android:layout_gravity="center"/>  
  41.         </FrameLayout>  
  42.   
  43.         <View android:layout_width="match_parent"  
  44.               android:layout_height="match_parent"  
  45.               androidprv:layout_childType="scrim"  
  46.               android:background="#99000000" />  
  47.           
  48.         <FrameLayout  
  49.             android:layout_width="match_parent"  
  50.             android:layout_height="match_parent"  
  51.             androidprv:layout_childType="mediatekLayerForeground">  
  52.         </FrameLayout>  
  53.   
  54.         <com.android.internal.policy.impl.keyguard.KeyguardSecurityContainer  
  55.             android:id="@+id/keyguard_security_container"  
  56.             android:layout_width="wrap_content"  
  57.             android:layout_height="wrap_content"  
  58.             android:layout_maxHeight="@dimen/keyguard_security_height"  
  59.             androidprv:layout_childType="challenge"  
  60.             android:padding="0dp"  
  61.             android:gravity="bottom|center_horizontal">  
  62.             <com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper  
  63.                 android:id="@+id/view_flipper"  
  64.                 android:layout_width="match_parent"  
  65.                 android:layout_height="match_parent"  
  66.                 android:clipToPadding="false"  
  67.                 android:paddingTop="@dimen/keyguard_security_view_margin"  
  68.                 android:gravity="center">  
  69.             </com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper>  
  70.         </com.android.internal.policy.impl.keyguard.KeyguardSecurityContainer>  
  71.   
  72.         <ImageButton  
  73.               android:layout_width="match_parent"  
  74.               android:layout_height="@dimen/kg_widget_pager_bottom_padding"  
  75.               androidprv:layout_childType="expandChallengeHandle"  
  76.               android:focusable="true"  
  77.               android:background="@null"  
  78.               android:src="@drawable/keyguard_expand_challenge_handle"  
  79.               android:scaleType="center"  
  80.               android:contentDescription="@string/keyguard_accessibility_expand_lock_area" />  
  81.   
  82.     </com.android.internal.policy.impl.keyguard.SlidingChallengeLayout>  
  83. </com.android.internal.policy.impl.keyguard.KeyguardHostView>  
<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()这个方法,来启动那种锁屏界面,如下:

  1. private int getLayoutIdFor(SecurityMode securityMode) {  
  2.         switch (securityMode) {  
  3.             case None: return R.layout.keyguard_selector_view;  
  4.             case Pattern: return R.layout.keyguard_pattern_view;  
  5.             case PIN: return R.layout.keyguard_pin_view;  
  6.             case Password: return R.layout.keyguard_password_view;  
  7.             case Biometric: return R.layout.keyguard_face_unlock_view;  
  8.             case Account: return R.layout.keyguard_account_view;  
  9.             /// M: Modify Sim unlock layout @{   
  10.             //case SimPin: return R.layout.keyguard_sim_pin_view;   
  11.             //case SimPuk: return R.layout.keyguard_sim_puk_view;   
  12.             case SimPinPukMe1: return com.mediatek.internal.R.layout.keyguard_sim_pin_puk_view;  
  13.             case SimPinPukMe2: return com.mediatek.internal.R.layout.keyguard_sim_pin_puk_view;  
  14.             /// M: Support GeminiPlus   
  15.             case SimPinPukMe3: return com.mediatek.internal.R.layout.keyguard_sim_pin_puk_view;  
  16.             case SimPinPukMe4: return com.mediatek.internal.R.layout.keyguard_sim_pin_puk_view;  
  17.             /// @}   
  18.   
  19.             /// M: power-off alarm @{   
  20.             case AlarmBoot: return com.mediatek.internal.R.layout.power_off_alarm_view;  
  21.             /// @}   
  22.             ///M: add voice unlock view layout   
  23.             case Voice: return R.layout.zz_keyguard_voice_unlock_view;  
  24.             default:  
  25.                 return 0;  
  26.         }  
  27.     }  
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;
            /// M: Modify Sim unlock layout @{
            //case SimPin: return R.layout.keyguard_sim_pin_view;
            //case SimPuk: return R.layout.keyguard_sim_puk_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;
            /// M: Support GeminiPlus
            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;
            /// @}

            /// M: power-off alarm @{
            case AlarmBoot: return com.mediatek.internal.R.layout.power_off_alarm_view;
            /// @}
            ///M: add voice unlock view layout
            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过滤出来,具体布局需要你微调下;


添加一张图,




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值