转载自http://blog.csdn.net/u013656135/article/details/43274161
Andriod谷歌账号解锁功能,通常情况下不会显示,如果用户忘记密码,输错几次后会显示一个Button。如下图中FORGOT PATTERN,这是我修改以后让这个Button一直显示。触摸这个Button以后就出现一个登入画面,其中包括需要用户添些的username(gmail)和用户所添账户的密(password)。
当然要想通过账户(account)来解锁,必须先在设置中添加账户(add account)。目前Android有几种保密锁屏:Pattern、Password、PIN。
在去写代码之前,或者是看Android是怎么实现这个功能之前,心里想一下,这个功能要怎么去实现,想必都会有这样的思路(除去界面不说):
下面就图案解锁来分析一下实现过程,当然初次接触锁屏有必要看下锁屏界面的布局,如果熟悉Keyguard,自然就知道图案解锁的View 在KeyguardPatternView.java中。要看图案解锁 此时锁屏的布局,当然用hierarchyviewer.bat 来查看布局比较方便,左右对比很容易看出控件锁在的View,如图:
从上图中找到com.android.keyguard.KeyguardPatternView,然后直接找到KeyguardPatternView.java文件开始分析。
1、通过Button控件 显示login 界面:
[html] view plain copy
- mForgotPatternButton = (Button) findViewById(R.id.forgot_password_button);
- // note: some configurations don't have an emergency call area
- if (mForgotPatternButton != null) {
- mForgotPatternButton.setText(R.string.kg_forgot_pattern_button_text);
- mForgotPatternButton.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- mCallback.showBackupSecurity();
- }
- });
- }
通过Button的Click 显示Login界面,Android通过调用KeyguardSecurityCallback接口中的showBackupSecurity()函数来实现,而真正实现showBackupSecurity()函数的地方是在
KeyguardHostView.java :
[html] view plain copy
- private KeyguardSecurityCallback mCallback = new KeyguardSecurityCallback() {
- ...
- @Override
- public void showBackupSecurity() {
- KeyguardHostView.this.showBackupSecurityScreen();
- }
- ...
[html] view plain copy
- /**
- * Shows the backup security screen for the current security mode. This could be used for
- * password recovery screens but is currently only used for pattern unlock to show the
- * account unlock screen and biometric unlock to show the user's normal unlock.
- */
- private void showBackupSecurityScreen() {
- if (DEBUG) Log.d(TAG, "showBackupSecurity()");
- SecurityMode backup = mSecurityModel.getBackupSecurityMode(mCurrentSecuritySelection)
- showSecurityScreen(backup);
- }
最终调用的是函数showSecurityScreen(backup)而backup是SecurityMode,通过getBackupSecurityMode()赋值,来获得当前是那种安全锁屏模式。
[html] view plain copy
- SecurityMode getBackupSecurityMode(SecurityMode mode) {
- switch(mode) {
- case Biometric:
- return getSecurityMode();
- case Pattern:
- return SecurityMode.Account;
- }
- return mode; // no backup, return current security mode
- }
showBackupSecurityScreen()函数的说明,只为Pattern 和 Biometric 两种模式所用,看函数getBackupSecurityMode() 就知道为什么了。在showSecurityScreen()中,会通过SecurityMode去获取对应的view。
KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection);
KeyguardSecurityView newView = getSecurityView(securityMode);
getSecurityView()通过SecurityMode 获取到对应的布局ID,然后对view进行加工 return 各种
SecurityMode 所需要的View.
[html] view plain copy
- private KeyguardSecurityView getSecurityView(SecurityMode securityMode) {
- ...
- final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
- ...
- }
- private int getSecurityViewIdForMode(SecurityMode securityMode) {
- switch (securityMode) {
- case None: return R.id.keyguard_selector_view;
- case Pattern: return R.id.keyguard_pattern_view;
- case PIN: return R.id.keyguard_pin_view;
- case Password: return R.id.keyguard_password_view;
- case Biometric: return R.id.keyguard_face_unlock_view;
- case Account: return R.id.keyguard_account_view;
- case SimPin: return R.id.keyguard_sim_pin_view;
- case Sim2Puk: return R.id.keyguard_sim_puk_view;
- }
- return 0;
而keyguard_account_view就是Login登入界面所需要的,在这里也可以肯定只有当SecurityMode = Account时才会显示Login界面,而getBackupSecurityMode函数中,只有Pattern模式才会返回Account ,所以Password 和 PIN 模式即使有FORGOT PASSWORD 也不会显示Login界面。所以要增加这两种模式显示login就要把getBackupSecurityMode()改成:
[html] view plain copy
- SecurityMode getBackupSecurityMode(SecurityMode mode) {
- switch(mode) {
- case Biometric:
- return getSecurityMode();
- case Pattern:
- case Password:
- case PIN:
- return SecurityMode.Account;
- }
- return mode; // no backup, return current security mode
- }
到这里Login界面分析完了,然后就是重点---解锁。
2、账号解锁:Login界面的东西,都在KeyguardAccountView.java中。
从“确定”或者“ok”Button 入手:
-> mOk = (Button) findViewById(R.id.ok);
-> mOk.setOnClickListener(this);
public void onClick(View v) {
mCallback.userActivity(0);
if (v == mOk) {
asyncCheckPassword();
}
}
触摸确认button,执行asyncCheckPassword(),异步检查账户,按照上面简单的逻辑视图,
先要获取Account,所以在asyncCheckPassword()可以看到:
Account account = findIntendedAccount(login);
[html] view plain copy
- private Account findIntendedAccount(String username) {
- Account[] accounts = AccountManager.get(mContext).getAccountsByTypeAsUser("com.google",
- new UserHandle(mLockPatternUtils.getCurrentUser()));
- // Try to figure out which account they meant if they
- // typed only the username (and not the domain), or got
- // the case wrong.
- Account bestAccount = null;
- int bestScore = 0;
- for (Account a: accounts) {
- int score = 0;
- if (username.equals(a.name)) {
- score = 4;
- } else if (username.equalsIgnoreCase(a.name)) {
- score = 3;
- } else if (username.indexOf('@') < 0) {
- int i = a.name.indexOf('@');
- if (i >= 0) {
- String aUsername = a.name.substring(0, i);
- if (username.equals(aUsername)) {
- score = 2;
- } else if (username.equalsIgnoreCase(aUsername)) {
- score = 1;
- }
- }
- }
- if (score > bestScore) {
- bestAccount = a;
- bestScore = score;
- } else if (score == bestScore) {
- bestAccount = null;
- }
- }
- return bestAccount;
- }
首先通过AccountManager 获得系统中的账号,然后遍历系统的账户,查找匹配用户输入的账户,然后将查找到的账户赋给bestaccount,如果bestaccount = null 就告知用户Fail,基本符合上面简单的逻辑图中对Account的判断。
通过asyncCheckPassword()函数了解到,不管account是否为null,都会调用postOnCheckPasswordResult()
[html] view plain copy
- private void postOnCheckPasswordResult(final boolean success) {
- // ensure this runs on UI thread
- mLogin.post(new Runnable() {
- public void run() {
- if (success) {
- // clear out forgotten password
- mLockPatternUtils.setPermanentlyLocked(false);
- mLockPatternUtils.setLockPatternEnabled(false);
- mLockPatternUtils.saveLockPattern(null);
- // launch the 'choose lock pattern' activity so
- // the user can pick a new one if they want to
- Intent intent = new Intent();
- intent.setClassName(LOCK_PATTERN_PACKAGE, LOCK_PATTERN_CLASS);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- mContext.startActivityAsUser(intent,
- new UserHandle(mLockPatternUtils.getCurrentUser()));
- mCallback.reportSuccessfulUnlockAttempt();
- // dismiss keyguard
- mCallback.dismiss(true);
- } else {
- mSecurityMessageDisplay.setMessage(R.string.kg_login_invalid_input, true);
- mPassword.setText("");
- mCallback.reportFailedUnlockAttempt();
- }
- }
- });
- }
在postOnCheckPasswordResult()函数中,如果传进来的值是false,即account=null.函数走else,通过显示一个message告诉用户目前情况。如果传进来的值是true,即account=!null.就要执行解锁功能,并跳转到指定界面。
mLockPatternUtils.setPermanentlyLocked(false);
mLockPatternUtils.setLockPatternEnabled(false);
mLockPatternUtils.saveLockPattern(null);
这就是解锁,但只有图案解锁,把图案密码设置为null。这也就导致password 和 PIN 不能解锁。所以为了这两种方式也能解锁,需要在这里增加:
mLockPatternUtils.saveLockPassword(null, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
或者直接改成mLockPatternUtils.clearLock(success);
如果不确定password 和 PIN 两种模式是不是这样解锁的,可以跟一下在设置里面输入密码确认时设置里面是如何执行解锁的。
Intent intent = new Intent();
intent.setClassName(LOCK_PATTERN_PACKAGE, LOCK_PATTERN_CLASS);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivityAsUser(intent,
new UserHandle(mLockPatternUtils.getCurrentUser()));
跳转选择锁屏的界面。当然也不一定非要跳转的到这个界面,这里这么做的主要意思是为了在解锁以后方便用户再次选择选择锁屏方式。当然不选也没关系,直接返回桌面,系统会默认将锁屏设置为滑动锁屏。
至此,Android账户解锁大致流程就是如此。
PS:忘了一个地方,Pattern 界面FORGOT 的Button 如果要一直都显示,可看KeyguardPatternView.java中的函数updateFooter()。这里是控制Button是否显示的地方。
Android的本意是:如果用户忘记密码,可以尝试几次,如果在规定的次数内仍不能解锁,就会显示FORGOT button,允许用户通过账户解锁。