FingerprintEnrollFindSensor 界面切换

地点:FingerprintEnrollFindSensor.java //Android P
事件:通过指纹切到录入界面 FingerprintEnrollEnrolling.java
FingerprintEnrollFindSensor 界面切换_第1张图片
对比于Android N 这里不再支持NEXT按钮切到右边的界面。
那么指纹是如何切到右边的录入界面的呢?

进入左边界面在我们设置过强认证解锁的方式之后,就会获得非空的token(byte[]),直接调用 startLookingForFingerprint()
如果没有设置过强认证方式,则launch对象的设置界面要求设置,设置完成后,回到左边界面,会调用 startLookingForFingerprint()。

starLookingForFingerprint()的调用将让指纹开始工作。
    private void startLookingForFingerprint() {
        mSidecar = (FingerprintEnrollSidecar) getFragmentManager().findFragmentByTag(
                FingerprintEnrollEnrolling.TAG_SIDECAR);
        if (mSidecar == null) {
            mSidecar = new FingerprintEnrollSidecar();
            getFragmentManager().beginTransaction()
                    .add(mSidecar, FingerprintEnrollEnrolling.TAG_SIDECAR).commit();
        }
//经过上面的逻辑,FingerprintEnrollSidecar这个fragment生命周期就开始了,接着导致enroll
//(token,....)被调用。指纹开始工作
        mSidecar.setListener(new Listener() {//这是匿名内部类的方式实现接口方法,其实还有其他实现方式如在外部类中实现Listener这个接口
            @Override
            public void onEnrollmentProgressChange(int steps, int remaining) {
                mNextClicked = true;
                proceedToEnrolling(true /* cancelEnrollment */);
            }

            @Override
            public void onEnrollmentHelp(CharSequence helpString) {
            }

            @Override
            public void onEnrollmentError(int errMsgId, CharSequence errString) {
              //fix: use the same fingerprint to go to enrolling page!
                if (errString!=null)
                    Log.d("call back","onEnrollmentError proceedToEnrolling"+" errMsgId: "+errMsgId+" errString "+ errString.toString());
                if (errString==null||errMsgId==FingerprintManager.FINGERPRINT_ERROR_UNABLE_TO_PROCESS)
                    proceedToEnrolling(false /* cancelEnrollment */);
                else if (mNextClicked && errMsgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED) {
                    mNextClicked = false;
                    proceedToEnrolling(false /* cancelEnrollment */);
                }
            }
        });
    }

这里重点关注一下mNextClicked =和proceedToEnrolling(boolean /* cancelEnrollment /);
private boolean mNextClicked; // 这个变量是全局性的,且多次出现,如下:
FingerprintEnrollFindSensor 界面切换_第2张图片
先看到 proceedToEnrolling(boolean /
cancelEnrollment */);

private void proceedToEnrolling(boolean cancelEnrollment) {
            if (mSidecar != null) {
                if (cancelEnrollment) {
                    if (mSidecar.cancelEnrollment()) {
                        // Enrollment cancel requested. When the cancellation is successful,
                        // onEnrollmentError will be called with FINGERPRINT_ERROR_CANCELED, calling
                        // this again.
                        return;
                    }
                }
                getFragmentManager().beginTransaction().remove(mSidecar).commitAllowingStateLoss();
                mSidecar = null;
                startActivityForResult(getEnrollingIntent(), ENROLLING);
            }
        }

如果流程走到通过 startActivityForResult(getEnrollingIntent(), ENROLLING)跳转。

 /**
   Activity explaining the fingerprint sensor location for fingerprint enrollment.
 */
public class FingerprintEnrollFindSensor extends FingerprintEnrollBase {
......
//FingerprintEnrollBase.java
protected Intent getEnrollingIntent() {
        Intent intent = new Intent();
        intent.setClassName("com.android.settings", FingerprintEnrollEnrolling.class.getName());
        intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, mToken);
        if (mUserId != UserHandle.USER_NULL) {
            intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
        }
        return intent;
    }
}

则将跳转到FingerprintEnrollEnrolling界面。
大致过程是:指纹enroll操作后,反馈信息的回调,最终导致Listener接口中的对应方法得到调用,如下:(1.onEnrollmentProgressChange,2.onEnrollmentHelp,3.onEnrollmentError)而它们将导致proceedToEnrolling(boolean /*cancelEnrollment */)调用,最终导致页面的跳转
不过,这里有一个逻辑细节(mNextClicked)很重要。需要做展开分析,才能彻底弄清这个跳转逻辑过程。
mNextClicked是一个典型的状态控制变量,它具体有什么作用?
startActivityForResult的使用,对应有onActivityResult

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
           ....
                startLookingForFingerprint();
            } else {
                finish();
            }
        } else if (requestCode == ENROLLING) {
            if (resultCode == RESULT_FINISHED) {
                setResult(RESULT_FINISHED);
                finish();
            } else if (resultCode == RESULT_SKIP) {
                setResult(RESULT_SKIP);
                finish();
            } else if (resultCode == RESULT_TIMEOUT) {
             ........
                } else {
                    // We came back from enrolling but it wasn't completed, start again.
                    startLookingForFingerprint();
                }
            }
        } else {
            super.onActivityResult(requestCode, resultCode, data);
        }
    }

proceedToEnrolling中的mSidecar.cancelEnrollment()如何导致onEnrollmentError回调的:
private CancellationSignal mEnrollmentCancel;

mEnrollmentCancel = new CancellationSignal();
mFingerprintManager.enroll(mToken, mEnrollmentCancel,
0 /* flags */, mUserId, mEnrollmentCallback);

mSidecar.cancelEnrollment()将导致mEnrollmentCancel.cancel()调用

// FingerprintManager.java
@RequiresPermission(MANAGE_FINGERPRINT)
 public void enroll(byte [] token, CancellationSignal cancel, int flags,
            int userId, EnrollmentCallback callback) {
      ....
     if (cancel != null) {
            if (cancel.isCanceled()) {
                Slog.w(TAG, "enrollment already canceled");
                return;
            } else {
                cancel.setOnCancelListener(new OnEnrollCancelListener());
            }
                  ....
        }
//import android.os.CancellationSignal.OnCancelListener;
 private class OnEnrollCancelListener implements OnCancelListener {
        @Override
        public void onCancel() {
            cancelEnrollment();
        }
    }
private void cancelEnrollment() {
        if (mService != null) try {
            mService.cancelEnrollment(mToken);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
//FingerprintService.java
 @Override // Binder call
        public void cancelEnrollment(final IBinder token) {
            checkPermission(MANAGE_FINGERPRINT);
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    ClientMonitor client = mCurrentClient;
                    if (client instanceof EnrollClient && client.getToken() == token) {
                        client.stop(client.getToken() == token);
                    }
                }
            });
        }
//EnrollClient.java
 @Override
    public int stop(boolean initiatedByClient) {
        if (mAlreadyCancelled) {
            Slog.w(TAG, "stopEnroll: already cancelled!");
            return 0;
        }
        IBiometricsFingerprint daemon = getFingerprintDaemon();
        if (daemon == null) {
            Slog.w(TAG, "stopEnrollment: no fingerprint HAL!");
            return ERROR_ESRCH;
        }
        try {
            final int result = daemon.cancel();
            if (result != 0) {
                Slog.w(TAG, "startEnrollCancel failed, result = " + result);
                return result;
            }
        } catch (RemoteException e) {
            Slog.e(TAG, "stopEnrollment failed", e);
        }
        if (initiatedByClient) {
            onError(FingerprintManager.FINGERPRINT_ERROR_CANCELED, 0 /* vendorCode */);
        }
        mAlreadyCancelled = true;
        return 0;
    }

通过 cancel.setOnCancelListener(new OnEnrollCancelListener());注册监听,
而mEnrollmentCancel.cancel()调用将导致onCancel()回调–> cancelEnrollment()调用(具体分析见:CancellationSignal.java),最终将导致daemon.cancel()—>onEnrollmentError回调的。
分析CancellationSignal.java
该类提供删除信号类,提供终止操作的能力,具体分析时,注意一下mIsCanceled这个boolean变量。它也是一个状态控制变量,解决的多次调用问题。

public void setOnCancelListener(OnCancelListener listener) {
        synchronized (this) {
            waitForCancelFinishedLocked();

            if (mOnCancelListener == listener) {
                return;
            }
            mOnCancelListener = listener;
            if (!mIsCanceled || listener == null) {
                return;
            }
        }
        listener.onCancel();//一般情况下不会走到这里。
    }

CancellationSignal的用法:
先通过setOnCancelListener(OnCancelListener listener) 设置监听,并对OnCancelListener接口方法做具体实现;
再通过CancellationSignal 的对象去调用cancel(),进而调用已经具体实现了的接口方法,如onCancel()。

回到mNextClicked状态控制变量(一般为boolean)
当成功录入第一步,将导致onEnrollmentProgressChange调用–>mNextClicked变为true—> proceedToEnrolling(true /* cancelEnrollment /)—>mSidecar.cancelEnrollment返回值(true)–>return—>指纹停止工作>onEnrollmentError回调—>(mNextClicked 为true)–>mNextClicked重新变成默认值false—> proceedToEnrolling(false /* cancelEnrollment /)—>startActivityForResult调用,最终成功跳转。

Note:onActivityResult的部分可以关注一下,但这里不做具体的分析。
作为boolean类型的状态控制变量一般与return连用,可改变代码执行流程,分析的时候,一定要小心谨慎。只要出现如下结构,就多注意一点:

  //controller 全局变量,且多次出现
   if((boolean)controller ....){
        .....
       return ... ;
    }
    or
  if((boolean)controller ....){
        .....
    }

ps.

getFragmentManager().beginTransaction().remove(mSidecar).commitAllowingStateLoss();
mSidecar = null;

FingerprintEnrollSidecar是个一个fragment,可以将它的逻辑(界面)绑到(add)FingerprintEnrollFindSensor这个Activity中,用完之后,也可以将该fragment从Activity中解绑(remove)。

你可能感兴趣的:(Fingerprint)