Setting 選擇密碼解析(一)

選擇解鎖方式頁面的加載

首先還是從ChooseLockGenericFragment開始分析,由上一篇鏈接 我們知道當第一次選擇密碼時,會執行helper.launchConfirmationActivity(CONFIRM_EXISTING_REQUEST,getString(R.string.unlock_set_unlock_launch_picker_title), true, mUserId)方法並返回false,從而執行updatePreferencesOrFinish(savedInstanceState != null);這個方法,我們來看下此方法,代碼如下:

private void updatePreferencesOrFinish(boolean isRecreatingActivity) {
            Intent intent = getActivity().getIntent();
            int quality = intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, -1);
            Log.d("faceid", "quality = " + quality+ " :isRecreatingActivity: "+isRecreatingActivity);
            //由於沒有設置過密碼,此時quality爲-1
    			if (quality == -1) {
                // If caller didn't specify password quality, show UI and allow the user to choose.
                quality = intent.getIntExtra(MINIMUM_QUALITY_KEY, -1);
                quality = mController.upgradeQuality(quality);//quality:0
                final boolean hideDisabledPrefs = intent.getBooleanExtra(
                        HIDE_DISABLED_PREFS, false);//hideDisabledPrefs false
                final PreferenceScreen prefScreen = getPreferenceScreen();
                if (prefScreen != null) {
                    prefScreen.removeAll();
                }
                addPreferences();//添加布局
                removePreferencesForFaceid();
                disableUnusablePreferences(quality, hideDisabledPrefs); //如果此時是人臉跳到此頁面時hideDisabledPrefs爲true,會disable 無和滑動兩種鎖屏方式
                updatePreferenceText();//更新每個解鎖方式的文字
                updateCurrentPreference();//更新當前選中的解鎖方式的文字狀態
                updatePreferenceSummaryIfNeeded();
            } else if (!isRecreatingActivity) { //todo  爲什麼不走這裏
                // Don't start the activity again if we are recreated for configuration change
                updateUnlockMethodAndFinish(quality, false, true /* chooseLockSkipped */);
            }
        }

此時佈局已經加載完成,界面截圖如下:
在這裏插入圖片描述

選擇解鎖方式

當選擇解鎖方式時,點擊任意一種都會執行onPreferenceTreeClick方法,我們來看下,如果我們選擇patten,會執行甚麼,代碼如下:

// /vendor/mediatek/proprietary/package/apps/MTKSettings/src/
//com/android/settings/password/ChooseLockGeneric.java/ChooseLockGenericFragment
@Override
        public boolean onPreferenceTreeClick(Preference preference) {
            final String key = preference.getKey();//此時key爲:unlock_set_pattern
           	//isUnlockMethodSecure會判斷key是否爲none或者swipe,此時key爲pattern,返回false
            if (!isUnlockMethodSecure(key) && mLockPatternUtils.isSecure(mUserId)) {
                ......
            } else if (KEY_SKIP_FINGERPRINT.equals(key)) {
                ......
            } else {
                //此時會執行setUnlockMethod(unlock_set_pattern)方法
                return setUnlockMethod(key);
            }
        }

接着來看setUnlockMethod方法,代碼如下:

// /vendor/mediatek/proprietary/package/apps/MTKSettings/src/
//com/android/settings/password/ChooseLockGeneric.java/ChooseLockGenericFragment
private boolean setUnlockMethod(String unlockMethod) {
            EventLog.writeEvent(EventLogTags.LOCK_SCREEN_TYPE, unlockMethod);
            //此時的unlockMethod爲unlock_set_pattern,所以lock爲PATTERN
            ScreenLockType lock = ScreenLockType.fromKey(unlockMethod);
            if (lock != null) {
                switch (lock) {
                    case NONE:
                    case SWIPE:
                       ......
                        return true;
                    case PATTERN:
                    case PIN:
                    case PASSWORD:
                    case MANAGED:
                        //所以會執行此方法
                        maybeEnableEncryption(lock.defaultQuality, false);
                        return true;
                }
            }
            return false;
        }

接着我們來看下maybeEnableEncryption(lock.defaultQuality, false);方法,方法如下:

// /vendor/mediatek/proprietary/package/apps/MTKSettings/src/
//com/android/settings/password/ChooseLockGeneric.java/ChooseLockGenericFragment
private void maybeEnableEncryption(int quality, boolean disabled) {
            DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(DEVICE_POLICY_SERVICE);
            if (UserManager.get(getActivity()).isAdminUser()
                    && mUserId == UserHandle.myUserId()
                    && LockPatternUtils.isDeviceEncryptionEnabled()
                    && !LockPatternUtils.isFileEncryptionEnabled()
                    && !dpm.getDoNotAskCredentialsOnBoot()) {
                //此時的quality爲65535爲圖案
                mEncryptionRequestQuality = quality;
                //mEncryptionRequestDisabled爲false
                mEncryptionRequestDisabled = disabled;
               //此時獲取到的intent爲ChooseLockPattern.java
                Intent unlockMethodIntent = getIntentForUnlockMethod(quality);
                //mForChangeCredRequiredForBoot爲false
                unlockMethodIntent.putExtra(
                        ChooseLockSettingsHelper.EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT,
                        mForChangeCredRequiredForBoot);
                final Context context = getActivity();
                // If accessibility is enabled and the user hasn't seen this dialog before, set the
                // default state to agree with that which is compatible with accessibility
                // (password not required).
                final boolean accEn = AccessibilityManager.getInstance(context).isEnabled();
                final boolean required = mLockPatternUtils.isCredentialRequiredToDecrypt(!accEn);
                //此方法新建裏個Intent,值爲EncryptionInterstitial.class,並將unlockMethodIntent
                //作爲參數放到此intent中。
                Intent intent = getEncryptionInterstitialIntent(context, quality, required,
                        unlockMethodIntent);
                //mForFingerprint爲:false
                intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT,
                        mForFingerprint);
                //mHideDrawer爲:false
                intent.putExtra(EXTRA_HIDE_DRAWER, mHideDrawer);
                startActivityForResult(
                        intent,
                        mIsSetNewPassword && mHasChallenge
                                ? CHOOSE_LOCK_BEFORE_FINGERPRINT_REQUEST
                                : ENABLE_ENCRYPTION_REQUEST);
            } else {
                ......
            }
        }

安全啓動頁面

此時執行startActivityForResult跳轉到EncryptionInterstitial.java,頁面截圖如下:
在這裏插入圖片描述
接着上面來看,由於EncryptionInterstitial繼承SettingsActivity,所以打印log如下:
在這裏插入圖片描述
所以下面我們來看看EncryptionInterstitialFragment,代碼如下:

// /vendor/mediatek/proprietary/package/apps/MTKSettings/src/
//com/android/settings/EncryptionInterstitial.java/EncryptionInterstitialFragment 
@Override
        public void onViewCreated(View view, Bundle savedInstanceState) {
            super.onViewCreated(view, savedInstanceState);

            /* M: fragment lifecycle should be same as Activity lifecycle */
            Log.d(TAG, "EncryptionInterstitialFragment onViewCreated,  isActivityFinishing: "
                  + (getActivity().isFinishing()) + "isActivity Destroyed: "
                  + (getActivity().isDestroyed()) + "Monkey user :" + Utils.isMonkeyRunning());

            ......
			//右下方的是按鈕,點擊後會跳轉到設置解鎖圖案頁面
            mRequirePasswordToDecrypt = view.findViewById(R.id.encrypt_require_password);
            //左下方的否按鈕,點擊會返回
            mDontRequirePasswordToDecrypt = view.findViewById(R.id.encrypt_dont_require_password);
           	//獲取intent裏的參數,由前面可知爲false
            boolean forFingerprint = getActivity().getIntent().getBooleanExtra(
                    ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
            Intent intent = getActivity().getIntent();
            //65536,爲圖案
            mRequestedPasswordQuality = intent.getIntExtra(EXTRA_PASSWORD_QUALITY, 0);
            //此mUnlockMethodIntent爲ChooseLockPattern.java
            mUnlockMethodIntent = intent.getParcelableExtra(EXTRA_UNLOCK_METHOD_INTENT);
            final int msgId;
            switch (mRequestedPasswordQuality) {
                case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
                    //此時forFingerprint爲false,所以msgId爲:R.string.encryption_interstitial_message_pattern;
                    //即:"爲了進一步保護此設備的安全,您可以將設備設爲需要繪製解鎖圖案才能啓動。在設備啓動之前,
                    //無法接聽電話、接收消息或通知(包括鬧鐘)。\n\n這樣一來,即使設備丟失或被盜,
                    //其中的數據仍安全無虞。要將設備設爲需要繪製解鎖圖案才能啓動嗎?"
                    msgId = forFingerprint ?
                            R.string.encryption_interstitial_message_pattern_for_fingerprint :
                            R.string.encryption_interstitial_message_pattern;
                    break;
                ......
            }
            
            TextView message = (TextView) getActivity().findViewById(R.id.encryption_message);
            //爲TextView 設置值
            message.setText(msgId);
			//爲這兩個按鈕設置點擊事件
            mRequirePasswordToDecrypt.setOnClickListener(this);
            mDontRequirePasswordToDecrypt.setOnClickListener(this);
			......

           
        }

接着我們看下這兩個按鈕的點擊事件,方法如下:

// /vendor/mediatek/proprietary/package/apps/MTKSettings/src/
//com/android/settings/EncryptionInterstitial.java/EncryptionInterstitialFragment
@Override
        public void onClick(View view) {
            //點擊右下角 是 按鈕
            if (view == mRequirePasswordToDecrypt) {
                //log顯示accEn爲false
                final boolean accEn = AccessibilityManager.getInstance(getActivity()).isEnabled();
                if (accEn && !mPasswordRequired) {
                    ......
                } else {
                    //此時會執行到這裏
                    //setRequirePasswordState方法就是將參數true賦值給mPasswordRequired
                    //此時mPasswordRequired = true
                    setRequirePasswordState(true);
                    startLockIntent();
                }
            } else {
                setRequirePasswordState(false);
                startLockIntent();
            }
        }

接着我們看下startLockIntent();方法,方法如下:

// /vendor/mediatek/proprietary/package/apps/MTKSettings/src/
//com/android/settings/EncryptionInterstitial.java/EncryptionInterstitialFragment
protected void startLockIntent() {
    		//此時的mUnlockMethodIntent即爲上面intent獲取的參數
    		//此時mUnlockMethodIntent值爲ChooseLockPattern.java
            if (mUnlockMethodIntent != null) {
                //mPasswordRequired爲true
                mUnlockMethodIntent.putExtra(EXTRA_REQUIRE_PASSWORD, mPasswordRequired);
                //CHOOSE_LOCK_REQUEST 爲100
                startActivityForResult(mUnlockMethodIntent, CHOOSE_LOCK_REQUEST);
            } else {
                Log.wtf(TAG, "no unlock intent to start");
                finish();
            }
        }

此時將執行startActivityForResult(mUnlockMethodIntent, CHOOSE_LOCK_REQUEST);跳轉到ChooseLockPattern.java進行圖案密碼的輸入,頁面截圖如下:
在這裏插入圖片描述

圖案密碼輸入頁面

我們首先看下onViewCreated生命週期,代碼如下:

@Override
        public void onViewCreated(View view, Bundle savedInstanceState) {
            super.onViewCreated(view, savedInstanceState);
           	//控件的一些初始化
            ......
            //繪製圖案密碼的監聽
            mLockPatternView.setOnPatternListener(製圖案密碼的監聽);
            //爲左下方和右下方按鈕設置點擊事件的監聽
            mFooterLeftButton.setOnClickListener(this);
            mFooterRightButton.setOnClickListener(this);
			//confirmCredentials爲:false
            final boolean confirmCredentials = getActivity().getIntent()
                    .getBooleanExtra(ChooseLockGeneric.CONFIRM_CREDENTIALS, true);
            Intent intent = getActivity().getIntent();
            //mCurrentPattern爲:null
            mCurrentPattern = intent.getStringExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);//null
            //mHasChallenge爲:false
            mHasChallenge = intent.getBooleanExtra(
                    ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, false);
            //mChallenge爲:false
            mChallenge = intent.getLongExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0);
			//由於第一次進入,此時savedInstanceState爲null
            if (savedInstanceState == null) {
                //confirmCredentials此時爲false
                if (confirmCredentials) {
                    ......
                } else {
                    //此時會執行updateStage
                    updateStage(Stage.Introduction);
                }
            } else {
               ......
            }
        }

首先我們看下stage是什麼,其實stage 表示當用戶選擇一個patten時,用戶此時的狀態。
由下面可知,stage是一個枚舉,分別有Introduction、NeedToConfirm、ChoiceConfirmed、FirstChoiceValid等幾種狀態,每種狀態裏面的參數如下所示,其實就是繪製密碼界面所需要的一些控件的值。
接着我們來看下updateStage(Stage.Introduction);方法,看下第一次繪製時會執行什麼,截圖如下:
在這裏插入圖片描述
方法如下:

protected void updateStage(Stage stage) {
            //第一次進入時stage爲:Stage.Introduction
    		//定義一個變量保存之前的state
    		final Stage previousStage = mUiStage;
			//當前的狀態
            mUiStage = stage;

            // header text, footer text, visibility and
            // enabled state all known from the stage
            if (stage == Stage.ChoiceTooShort) {
                mHeaderText.setText(
                        getResources().getString(
                                stage.headerMessage,
                                LockPatternUtils.MIN_LOCK_PATTERN_SIZE));
            } else {
                //此時Introduction.headerMessage爲:"繪製解鎖圖案"
                mHeaderText.setText(stage.headerMessage);
            }
    		//由上面可知,此時的mForFingerprint爲false
            int message = mForFingerprint ? stage.messageForFingerprint : stage.message;
            //此時message爲:"爲了安全起見,請設置解鎖圖案",ID_EMPTY_MESSAGE = -1
    		if (message == ID_EMPTY_MESSAGE) {
                mMessageText.setText("");
            } else {
                //所以會執行這裏,賦值給mMessageText
                mMessageText.setText(message);
            }
    		//Introduction.footerMessage爲ID_EMPTY_MESSAGE,即-1
            if (stage.footerMessage == ID_EMPTY_MESSAGE) {
                //執行這裏,設置爲空字符
                mFooterText.setText("");
            } else {
                mFooterText.setText(stage.footerMessage);
            }

            if (stage == Stage.ConfirmWrong || stage == Stage.ChoiceTooShort) {
                TypedValue typedValue = new TypedValue();
                Theme theme = getActivity().getTheme();
                theme.resolveAttribute(R.attr.colorError, typedValue, true);
                mHeaderText.setTextColor(typedValue.data);

            } else {
                //爲mHeaderText設置字體顏色
                if (mDefaultHeaderColorList != null) {
                    mHeaderText.setTextColor(mDefaultHeaderColorList);
                }

                if (stage == Stage.NeedToConfirm && mForFingerprint) {
                    mHeaderText.setText("");
                    mTitleText.setText(R.string.lockpassword_draw_your_pattern_again_header);
                }
            }
			//更新左下方按鈕的狀態
            updateFooterLeftButton(stage, mFooterLeftButton);
			//設置右下方按鈕的文字
            setRightButtonText(stage.rightMode.text);
    		//設置右下方按鈕的是否可用,即是否可點擊
            setRightButtonEnabled(stage.rightMode.enabled);

            // Introduction.patternEnabled爲true
            if (stage.patternEnabled) {
                mLockPatternView.enableInput();
            } else {
                mLockPatternView.disableInput();
            }

            // the rest of the stuff varies enough that it is easier just to handle
            // on a case by case basis.
            mLockPatternView.setDisplayMode(DisplayMode.Correct);
            boolean announceAlways = false;

            switch (mUiStage) {
                case Introduction:
                    //mUiStage爲Introduction,執行這裏,進行一個清除Pattern的動作,
                    mLockPatternView.clearPattern();
                    break;
                case HelpScreen:
                    mLockPatternView.setPattern(DisplayMode.Animate, mAnimatePattern);
                    break;
                case ChoiceTooShort:
                    mLockPatternView.setDisplayMode(DisplayMode.Wrong);
                    postClearPatternRunnable();
                    announceAlways = true;
                    break;
                case FirstChoiceValid:
                    break;
                case NeedToConfirm:
                    mLockPatternView.clearPattern();
                    break;
                case ConfirmWrong:
                    mLockPatternView.setDisplayMode(DisplayMode.Wrong);
                    postClearPatternRunnable();
                    announceAlways = true;
                    break;
                case ChoiceConfirmed:
                    break;
            }

    		//當狀態改變時,設置mHeaderText狀態可以改變
            if (previousStage != stage || announceAlways) {
                mHeaderText.announceForAccessibility(mHeaderText.getText());
            }
        }

接着我們看下onResume方法,當用戶交互時會調用此方法,方法如下:

@Override
        public void onResume() {
            super.onResume();
            updateStage(mUiStage);
			//一個後臺fragment,用來跟蹤保存和校驗用戶選擇的密碼(pattern/pin/password).
            if (mSaveAndFinishWorker != null) {
                setRightButtonEnabled(false);
                //設置監聽器,當兩次密碼輸入完成保存成功後,激活監聽器跳轉到通知頁面
                mSaveAndFinishWorker.setListener(this);
            }
        }

可以看到,當用戶進行交互,即繪製圖案時,會不斷的調用updateStage(mUiStage);來更新界面狀態。且第一次進入時即mUiStage爲Introduction時,會進行一個pattern的初始化動作,清除圖案保證沒有繪製過。
接下來我們看下繪製圖案密碼的監聽mLockPatternView.setOnPatternListener

protected LockPatternView.OnPatternListener mChooseNewLockPatternListener =
                new LockPatternView.OnPatternListener() {
				//開始繪製圖案時調用
                public void onPatternStart() {
                    mLockPatternView.removeCallbacks(mClearPatternRunnable);
                    patternInProgress();
                }
				//清除圖案時調用
                public void onPatternCleared() {
                    //mClearPatternRunnable爲一個Runnable,調用了mLockPatternView.clearPattern();
                    mLockPatternView.removeCallbacks(mClearPatternRunnable);
                }
				//圖案繪製完成時調用
                public void onPatternDetected(List<LockPatternView.Cell> pattern) {
                    if (mUiStage == Stage.NeedToConfirm || mUiStage == Stage.ConfirmWrong) {
                        if (mChosenPattern == null) throw new IllegalStateException(
                                "null chosen pattern in stage 'need to confirm");
                        if (mChosenPattern.equals(pattern)) {
                            updateStage(Stage.ChoiceConfirmed);
                        } else {
                            updateStage(Stage.ConfirmWrong);
                        }
                    } else if (mUiStage == Stage.Introduction || mUiStage == Stage.ChoiceTooShort){
                        //此處的pattern爲一個二維數組,Cell[][],即代表的是一個3x3的矩陣
                        //public static final int MIN_LOCK_PATTERN_SIZE = 4;
                        if (pattern.size() < LockPatternUtils.MIN_LOCK_PATTERN_SIZE) {
                            updateStage(Stage.ChoiceTooShort);
                        } else {
                            mChosenPattern = new ArrayList<LockPatternView.Cell>(pattern);
                            updateStage(Stage.FirstChoiceValid);
                        }
                    } else {
                        throw new IllegalStateException("Unexpected stage " + mUiStage + " when "
                                + "entering the pattern.");
                    }
                }

                public void onPatternCellAdded(List<Cell> pattern) {
					//空實現
                }
				//開始繪製時由onPatternStart調用 ,主要作用爲頁面一些文字屬性更新
                private void patternInProgress() {
                    mHeaderText.setText(R.string.lockpattern_recording_inprogress);//"完成後鬆開手指"
                    if (mDefaultHeaderColorList != null) {
                        mHeaderText.setTextColor(mDefaultHeaderColorList);
                    }
                    mFooterText.setText("");
                    mFooterLeftButton.setEnabled(false);
                    mFooterRightButton.setEnabled(false);

                    if (mTitleHeaderScrollView != null) {
                        mTitleHeaderScrollView.post(new Runnable() {
                            @Override
                            public void run() {
                                mTitleHeaderScrollView.fullScroll(ScrollView.FOCUS_DOWN);
                            }
                        });
                    }
                }
         };

所以到這裏便有了一個大概的思路,mChooseNewLockPatternListener監聽用戶行爲,是否進行繪製圖案,當開始繪製時,觸發onPatternStart,onPatternStart又接着調用patternInProgress來更新UI,繪製過程中界面如下:
在這裏插入圖片描述
當繪製完成時,調用onPatternDetected,我們看下第一次繪製完成,此方法做了什麼,方法如下:

protected LockPatternView.OnPatternListener mChooseNewLockPatternListener =
                new LockPatternView.OnPatternListener() {
				......
				//圖案繪製完成時調用
                public void onPatternDetected(List<LockPatternView.Cell> pattern) {
                    if (mUiStage == Stage.NeedToConfirm || mUiStage == Stage.ConfirmWrong) {
                        ......
                    } else if (mUiStage == Stage.Introduction || mUiStage == Stage.ChoiceTooShort){
                        //第一次繪製時mUiStage爲Introduction,所以會執行到這裏
                        //當繪製完成時,pattern.size()爲5
                        if (pattern.size() < LockPatternUtils.MIN_LOCK_PATTERN_SIZE) {
                            updateStage(Stage.ChoiceTooShort);
                        } else {
                            //此時會執行到這裏
                            mChosenPattern = new ArrayList<LockPatternView.Cell>(pattern);
                            updateStage(Stage.FirstChoiceValid);
                        }
                    } else {
                        throw new IllegalStateException("Unexpected stage " + mUiStage + " when "
                                + "entering the pattern.");
                    }
                }

               ......
         };

接着調用updateStage(Stage.FirstChoiceValid);即上面的方法,可以看到,此時updateStage方法僅僅是做了一些更新UI的操作,比如設置buttun的狀態和字體以及header的文字等,我們可以看下此時的頁面,頁面如下:
在這裏插入圖片描述
此時狀態爲Stage.FirstChoiceValid,當點擊下一步時,執行onclick方法如下:

public void onClick(View v) {
            if (v == mFooterLeftButton) {
                handleLeftButton();
            } else if (v == mFooterRightButton) {
                handleRightButton();
            }
        }

handleRightButton();方法如下:


public void handleRightButton() {
    		//由於此時mUiStage爲FirstChoiceValid,他的rightMode爲Continue
            if (mUiStage.rightMode == RightButtonMode.Continue) {
                if (mUiStage != Stage.FirstChoiceValid) {
                    throw new IllegalStateException("expected ui stage "
                            + Stage.FirstChoiceValid + " when button is "
                            + RightButtonMode.Continue);
                }
                updateStage(Stage.NeedToConfirm);
            } else if (mUiStage.rightMode == RightButtonMode.Confirm) {
               ......
            } else if (mUiStage.rightMode == RightButtonMode.Ok) {
                ......
            }
        }
        

第二次輸入密碼

可以看到又執行updateStage(Stage.NeedToConfirm),在這個方法中便會調用mLockPatternView.clearPattern重置UI,讓用戶重新繪製。此時用戶界面爲:
在這裏插入圖片描述
當第二次繪製完成時,此時會再調用onPatternDetected,我們來看下此時的方法,方法如下:

public void onPatternDetected(List<LockPatternView.Cell> pattern) {
    				//由於上面調用了updateStage(Stage.NeedToConfirm),此方法裏面會將mUiStage
    				//賦值爲Stage.NeedToConfirm
                    if (mUiStage == Stage.NeedToConfirm || mUiStage == Stage.ConfirmWrong) {
                        if (mChosenPattern == null) throw new IllegalStateException(
                                "null chosen pattern in stage 'need to confirm");
                        //此時判斷當前繪製的圖案是否和上次繪製一樣
                        if (mChosenPattern.equals(pattern)) {
                            //一樣則會調用
                            updateStage(Stage.ChoiceConfirmed);
                        } else {
                            updateStage(Stage.ConfirmWrong);
                        }
                    } else if (mUiStage == Stage.Introduction || mUiStage == Stage.ChoiceTooShort){
                       ......
                    } else {
                        ......
                    }
                }

可以看到會調用updateStage(Stage.ChoiceConfirmed);來更新UI,我們看下當兩次繪製圖案一樣時,此時用戶界面是什麼,截圖如下:
在這裏插入圖片描述

疑問:兩次繪製的密碼是怎麼保存的?

我們來看下當第一次繪製完密碼時,調用onPatternDetected,我們看下此時代碼:

public void onPatternDetected(List<LockPatternView.Cell> pattern) {
                    if (mUiStage == Stage.NeedToConfirm || mUiStage == Stage.ConfirmWrong) {
                       ......
                    } else if (mUiStage == Stage.Introduction || mUiStage == Stage.ChoiceTooShort){
                        if (pattern.size() < LockPatternUtils.MIN_LOCK_PATTERN_SIZE) {
                            ......
                        } else {
                            //新建一個ArrayList將繪製完成的pattern保存到此mChosenPattern
                            mChosenPattern = new ArrayList<LockPatternView.Cell>(pattern);
                            updateStage(Stage.FirstChoiceValid);
                        }
                    } else {
                        ......
                    }
                }

可以看到此時會新建一個ArrayList將第一次繪製完成的pattern保存到此mChosenPattern,用來做第二次繪製完成密碼時的判斷,即

protected LockPatternView.OnPatternListener mChooseNewLockPatternListener =
                new LockPatternView.OnPatternListener() {
    
                public void onPatternDetected(List<LockPatternView.Cell> pattern) {
                    if (mUiStage == Stage.NeedToConfirm || mUiStage == Stage.ConfirmWrong) {
                        if (mChosenPattern == null) throw new IllegalStateException(
                                "null chosen pattern in stage 'need to confirm");
                        if (mChosenPattern.equals(pattern)) {
                            updateStage(Stage.ChoiceConfirmed);
                        } else {
                            updateStage(Stage.ConfirmWrong);
                        }
                    } else if (mUiStage == Stage.Introduction || mUiStage == Stage.ChoiceTooShort){
                       ......
                    } else {
                       ......
                    }
                }
               
         };

可以看到調用mChosenPattern.equals(pattern)來進行判斷兩次繪製的是否一樣。

繪製兩次完成

接着來看,當繪製兩次相同的密碼後,我們點擊確認按鈕後,執行onclick方法,調用handleRightButton();方法如下:

public void handleRightButton() {
            if (mUiStage.rightMode == RightButtonMode.Continue) {
                .....
            } else if (mUiStage.rightMode == RightButtonMode.Confirm) {
                if (mUiStage != Stage.ChoiceConfirmed) {
                    throw new IllegalStateException("expected ui stage " + Stage.ChoiceConfirmed
                            + " when button is " + RightButtonMode.Confirm);
                }
                startSaveAndFinish();
            } else if (mUiStage.rightMode == RightButtonMode.Ok) {
                ......
            }
        }

可以看到此時調用startSaveAndFinish();來保存圖案,方法如下:

// /vendor/mediatek/proprietary/package/apps/MTKSettings/src/com/android/
// /settings/password/ChooseLockPattern.java/ChooseLockPatternFragment
private void startSaveAndFinish() {
            if (mSaveAndFinishWorker != null) {
                Log.w(TAG, "startSaveAndFinish with an existing SaveAndFinishWorker.");
                return;
            }

            setRightButtonEnabled(false);

            mSaveAndFinishWorker = new SaveAndFinishWorker();
            mSaveAndFinishWorker.setListener(this);

            getFragmentManager().beginTransaction().add(mSaveAndFinishWorker,
                    FRAGMENT_TAG_SAVE_AND_FINISH).commit();
            getFragmentManager().executePendingTransactions();

            final boolean required = getActivity().getIntent().getBooleanExtra(
                    EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, true);
            mSaveAndFinishWorker.start(mChooseLockSettingsHelper.utils(), required,
                    mHasChallenge, mChallenge, mChosenPattern, mCurrentPattern, mUserId);
        }

首先爲mSaveAndFinishWorker設置了一個監聽器,接着調用mSaveAndFinishWorker.start開啓一個AsyncTask異步線程,在子線程中調用saveAndVerifyInBackground()用來保存圖案設置並返回一個intent,結束後觸發監聽器(mFinished = true;)然後調用startActivity(intent) 跳轉到RedactionInterstitial.java,此時頁面爲如下截圖:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章