手机滑屏解锁算法java,Android8.1 SystemUI Keyguard之滑动解锁流程

我们理解Keyguard的解锁流程主要从锁屏的界面Layout结构、touchEvent事件分发、解锁动作逻辑几个方面进行源码的分析

锁屏的界面Layout结构分析

StatusbarWindowView

整个锁屏界面的顶级View就是mStatusBarWindow

src/com/android/systemui/statusbar/phone/StatusBar.java

public void createAndAddWindows() {

addStatusBarWindow();

}

private void addStatusBarWindow() {

makeStatusBarView();

mStatusBarWindowManager = Dependency.get(StatusBarWindowManager.class);

mRemoteInputController = new RemoteInputController(mHeadsUpManager);

mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());

// add by hai.qin for story lock

if (mLockScreenManager != null) {

mLockScreenManager.setStatusBarWindowManager(mStatusBarWindowManager);

}

//

}

src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java

/**

* Adds the status bar view to the window manager.

*

* @param statusBarView The view to add.

* @param barHeight The height of the status bar in collapsed state.

*/

public void add(View statusBarView, int barHeight) {

// Now that the status bar window encompasses the sliding panel and its

// translucent backdrop, the entire thing is made TRANSLUCENT and is

// hardware-accelerated.

mLp = new WindowManager.LayoutParams(

ViewGroup.LayoutParams.MATCH_PARENT,

barHeight,

WindowManager.LayoutParams.TYPE_STATUS_BAR,

WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE

| WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING

| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH

| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH

| WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,

PixelFormat.TRANSLUCENT);

mLp.token = new Binder();

mLp.gravity = Gravity.TOP;

mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;

mLp.setTitle("StatusBar");

mLp.packageName = mContext.getPackageName();

mStatusBarView = statusBarView;

mBarHeight = barHeight;

mWindowManager.addView(mStatusBarView, mLp);

mLpChanged = new WindowManager.LayoutParams();

mLpChanged.copyFrom(mLp);

}

mStatusBarWindow是在StatusBar的create流程中调用WindowManager.addView()添加到窗口上的, type为WindowManager.LayoutParams.TYPE_STATUS_BAR

Layout结构

锁屏界面的Layout结构可以简单概括为以下结构:

mStatusBarWindow--> R.layout.super_status_bar

notification_panel--> R.layout.status_bar_expanded

keyguardBouncer-->R.layout.keyguard_bouncer

mStatusBarWindow-->notification_panel-->notification_container_parent-->keyguard_header(锁屏状态栏)

| |

| -->keyguard_bottom_area (lock_icon和充电状态等)

| |

| -->keyguard_status_view (锁屏时钟日期)

| |

| -->keyguard_up_slide (箭头提示动画)

|

-->keyguardBouncer(安全锁界面)

上划后显示的安全锁界面是KeyguardBouncer,但keyguardbouncer并没有写在super_status_bar的layout文件里面,那么他是在什么时候添加的呢?

KeyguarBouncer何时创建

src/com/android/systemui/statusbar/phone/StatusBar.java

-->start()-->startKeyguard()

protected void startKeyguard() {

...

mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this,

getBouncerContainer(), mScrimController,

mFingerprintUnlockController);

...

}

src/com/android/systemui/keyguard/KeyguardViewMediator.java

-->registerStatusBar()

src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java

-->registerStatusBar()

public void registerStatusBar(StatusBar statusBar,

ViewGroup container,

ScrimController scrimController,

FingerprintUnlockController fingerprintUnlockController,

DismissCallbackRegistry dismissCallbackRegistry) {

mStatusBar = statusBar;

mContainer = container;

mScrimController = scrimController;

mFingerprintUnlockController = fingerprintUnlockController;

mBouncer = SystemUIFactory.getInstance().createKeyguardBouncer(mContext,

mViewMediatorCallback, mLockPatternUtils, container, dismissCallbackRegistry);

}

那么这里的container是什么?

protected ViewGroup getBouncerContainer() {

return mStatusBarWindow;

}

是什么时候把keyguard_host_view加入到mStatuBarWindow的?

src/com/android/systemui/statusbar/phone/KeyguardBouncer.java

-->show()-->ensureView()-->inflateView()

protected void inflateView() {

removeView();

mHandler.removeCallbacks(mRemoveViewRunnable);

mRoot = (ViewGroup) LayoutInflater.from(mContext).inflate(R.layout.keyguard_bouncer, null);

mKeyguardView = mRoot.findViewById(R.id.keyguard_host_view);

mKeyguardView.setLockPatternUtils(mLockPatternUtils);

mKeyguardView.setViewMediatorCallback(mCallback);

mContainer.addView(mRoot, mContainer.getChildCount());

mRoot.setVisibility(View.INVISIBLE);

final WindowInsets rootInsets = mRoot.getRootWindowInsets();

if (rootInsets != null) {

mRoot.dispatchApplyWindowInsets(rootInsets);

}

}

不同类型的安全锁是怎么放入keyguard_host_view的?

src/com/android/keyguard/KeyguardSecurityContainer.java

private KeyguardSecurityView getSecurityView(SecurityMode securityMode) {

final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);

KeyguardSecurityView view = null;

final int children = mSecurityViewFlipper.getChildCount();

for (int child = 0; child < children; child++) {

if (mSecurityViewFlipper.getChildAt(child).getId() == securityViewIdForMode) {

view = ((KeyguardSecurityView)mSecurityViewFlipper.getChildAt(child));

break;

}

}

int layoutId = getLayoutIdFor(securityMode);

if (view == null && layoutId != 0) {

final LayoutInflater inflater = LayoutInflater.from(mContext);

if (DEBUG) Log.v(TAG, "inflating id = " + layoutId);

View v = inflater.inflate(layoutId, mSecurityViewFlipper, false);

mSecurityViewFlipper.addView(v);

updateSecurityView(v);

view = (KeyguardSecurityView)v;

}

return view;

}

每次获取securityview的时候 先判断是否在viewflippter里存在该id

没有的话 inflate一个新的同时addview到viewflippter里面

在显示的时候调用showSecurityScreen(SecurityMode securityMode)

private void showSecurityScreen(SecurityMode securityMode) {

...

// Find and show this child.

final int childCount = mSecurityViewFlipper.getChildCount();

final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);

for (int i = 0; i < childCount; i++) {

if (mSecurityViewFlipper.getChildAt(i).getId() == securityViewIdForMode) {

mSecurityViewFlipper.setDisplayedChild(i);

break;

}

}

...

}

会根据securitymode选择viewflipper中对应的child进行显示

touchEvent事件分发

我们这里分析上滑解锁过程中的touchEvent事件分发

让我们来先回顾一个android中的事件分发概念:事件序列

事件序列

在Android系统中,一个单独的事件基本上是没什么作用的,只有一个事件序列,才有意义。一个事件序列正常情况下,定义为 DOWN、MOVE(0或者多个)、UP/CANCEL。事件序列以DOWN事件开始,中间会有0或者多个MOVE事件,最后以UP事件或者CANCEL事件结束。

DOWN事件作为序列的开始,有一个很重要的职责,就是寻找事件序列的接受者,怎么理解呢?framework 在DOWN事件的传递过程中,需要根据View事件处理方法(onTouchEvent)的返回值来确定事件序列的接受者。如果一个View的onTouchEvent事件,在处理DOWN事件的时候返回true,说明它愿意接受并处理该事件序列。

上滑解锁

当用户移动手指时,产生touch down事件,

最外层view StatusBarWindowView会执行onInterceptTouchEvent,看是否需要拦截touch事件

再一级级往子View传递,都没有被拦截,之后执行OnTouchEvent从子View开始一级级往父View传递,到PanelView这里当手指移动的距离达到一定的阈值会调用onTrackingStarted从而设置mTracking的值为true,onTouchEvent返回true,接收此touch move事件,之后的touch事件直接传到此View。

在用户滑动过程会调用setExpandedHeightInternal,进而调用NotificationPanelView的onHeightUpdated进行锁屏上的时间和通知View根据手指的移动距离进行缩小、变透明处理。

当用户抬起手指时,产生touch up事件,PanelView接收到这个事件后会调用endMotionEvent,如果手指从down到up之间移动的距离达到一定阈值会调用onTrackingStopped

在上滑过程中,不断调用PanelView.java的setExpandedHeightInternal()->notifyBarPanelExpansionChanged()-->PanelBar.java的notifyBarPanelExpansionChanged()

src/com/android/systemui/statusbar/phone/PanelBar.java

public void panelExpansionChanged(float frac, boolean expanded) {

Log.d("WANG", "panelExpansionChanged frac=" + frac + " expaned=" + expanded );

boolean fullyClosed = true;

boolean fullyOpened = false;

if (SPEW) LOG("panelExpansionChanged: start state=%d", mState);

PanelView pv = mPanel;

pv.setVisibility(expanded ? VISIBLE : INVISIBLE);

// adjust any other panels that may be partially visible

if (expanded) {

if (mState == STATE_CLOSED) {

go(STATE_OPENING);

onPanelPeeked();

}

fullyClosed = false;

final float thisFrac = pv.getExpandedFraction();

if (SPEW) LOG("panelExpansionChanged: -> %s: f=%.1f", pv.getName(), thisFrac);

fullyOpened = thisFrac >= 1f;

}

if (fullyOpened && !mTracking) {

go(STATE_OPEN);

onPanelFullyOpened();

} else if (fullyClosed && !mTracking && mState != STATE_CLOSED) {

go(STATE_CLOSED);

onPanelCollapsed();

}

if (SPEW) LOG("panelExpansionChanged: end state=%d [%s%s ]", mState,

fullyOpened?" fullyOpened":"", fullyClosed?" fullyClosed":"");

}

达到阈值时,expanded == false fullyClosed == true

调用onPanelCollapsed()->调用子类PhoneStatubarView.java的onPanelCollapsed()

src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java

@Override

public void onPanelCollapsed() {

super.onPanelCollapsed();

// Close the status bar in the next frame so we can show the end of the animation.

post(mHideExpandedRunnable);

mIsFullyOpenedPanel = false;

}

private Runnable mHideExpandedRunnable = new Runnable() {

@Override

public void run() {

if (mPanelFraction == 0.0f) {

mBar.makeExpandedInvisible();

}

}

};

解锁动作逻辑

整个解锁过程分为两个部分:1. 隐藏notification_panel 2. 展示keyguard_bouncer或直接解锁

src/com/android/systemui/statusbar/phone/StatusBar.java

void makeExpandedInvisible() {

if (SPEW) Log.d(TAG, "makeExpandedInvisible: mExpandedVisible=" + mExpandedVisible

+ " mExpandedVisible=" + mExpandedVisible);

if (!mExpandedVisible || mStatusBarWindow == null) {

return;

}

// Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868)

mStatusBarView.collapsePanel(/*animate=*/ false, false /* delayed*/,

1.0f /* speedUpFactor */);

mNotificationPanel.closeQs();

mExpandedVisible = false;

visibilityChanged(false);

// Shrink the window to the size of the status bar only

mStatusBarWindowManager.setPanelVisible(false);

mStatusBarWindowManager.setForceStatusBarVisible(false);

// Close any guts that might be visible

closeAndSaveGuts(true /* removeLeavebehind */, true /* force */, true /* removeControls */,

-1 /* x */, -1 /* y */, true /* resetMenu */);

runPostCollapseRunnables();

setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);

showBouncerIfKeyguard();

recomputeDisableFlags(mNotificationPanel.hideStatusBarIconsWhenExpanded() /* animate */);

// Trimming will happen later if Keyguard is showing - doing it here might cause a jank in

// the bouncer appear animation.

if (!mStatusBarKeyguardViewManager.isShowing()) {

WindowManagerGlobal.getInstance().trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);

}

}

mStatusBarWindowManager.setPanelVisible(false);

调用WindowManager更改为StatusBarWindow的高度, 只保留状态栏高度

mStatusBarWindowManager.setForceStatusBarVisible(false);

调用WindowManager使状态栏不可见

showBouncerIfKeyguard()->showBouncer()最终调用到StatusBarKeyguardViewManager的dismiss()->showBouncer()方法

src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java

private void showBouncer() {

if (mShowing) {

mBouncer.show(false /* resetSecuritySelection */);

}

updateStates();

}

src/com/android/systemui/statusbar/phone/KeyguardBouncer.java

public void show(boolean resetSecuritySelection) {

...

// If allowed, try to dismiss the Keyguard. If no security auth (password/pin/pattern) is

// set, this will dismiss the whole Keyguard. Otherwise, show the bouncer.

if (allowDismissKeyguard && mKeyguardView.dismiss(activeUserId)) {

return;

}

...

}

在没有安全锁的情况下,会回调KeyguardHostView的finish方法

@Override

public void finish(boolean strongAuth, int targetUserId) {

// If there's a pending runnable because the user interacted with a widget

// and we're leaving keyguard, then run it.

boolean deferKeyguardDone = false;

if (mDismissAction != null) {

deferKeyguardDone = mDismissAction.onDismiss();

mDismissAction = null;

mCancelAction = null;

}

if (mViewMediatorCallback != null) {

if (deferKeyguardDone) {

mViewMediatorCallback.keyguardDonePending(strongAuth, targetUserId);

} else {

mViewMediatorCallback.keyguardDone(strongAuth, targetUserId);

}

}

}

mViewMediatorCallback定义在KeyguardViewMediator中

@Override

public void keyguardDone(boolean strongAuth, int targetUserId) {

if (targetUserId != ActivityManager.getCurrentUser()) {

return;

}

tryKeyguardDone();

}

一系列调用来到解锁的核心代码

mKeyguardGoingAwayRunnable.run();

private final Runnable mKeyguardGoingAwayRunnable = new Runnable() {

@Override

public void run() {

Trace.beginSection("KeyguardViewMediator.mKeyGuardGoingAwayRunnable");

if (DEBUG) Log.d(TAG, "keyguardGoingAway");

//Modified for MYOS begin

try {

mStatusBarKeyguardViewManager.keyguardGoingAway();

int flags = 0;

if (mStatusBarKeyguardViewManager.shouldDisableWindowAnimationsForUnlock()

|| mWakeAndUnlocking || mAniSpeedup) {

flags |= WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;

}

if (mStatusBarKeyguardViewManager.isGoingToNotificationShade()) {

flags |= WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;

}

if (mStatusBarKeyguardViewManager.isUnlockWithWallpaper()) {

flags |= WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;

}

mUpdateMonitor.setKeyguardGoingAway(true /* goingAway */);

// Don't actually hide the Keyguard at the moment, wait for window

// manager until it tells us it's safe to do so with

// startKeyguardExitAnimation.

ActivityManager.getService().keyguardGoingAway(flags);

} catch (RemoteException e) {

Log.e(TAG, "Error while calling WindowManager", e);

}

//Modified for MYOS end

Trace.endSection();

}

};

解锁过程的核心实质上是锁屏启动了一个runnable,

通知AMS和WMS显示锁屏下方的activity组件窗口以及调用该activity组件的生命周期

void keyguardGoingAway(int flags) {

if (!mKeyguardShowing) {

return;

}

Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "keyguardGoingAway");

mWindowManager.deferSurfaceLayout();

try {

setKeyguardGoingAway(true);

mWindowManager.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY,

false /* alwaysKeepCurrent */, convertTransitFlags(flags),

false /* forceOverride */);

updateKeyguardSleepToken();

// Some stack visibility might change (e.g. docked stack)

mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);

mStackSupervisor.addStartingWindowsForVisibleActivities(true /* taskSwitch */);

mWindowManager.executeAppTransition();

} finally {

Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "keyguardGoingAway: surfaceLayout");

mWindowManager.continueSurfaceLayout();

Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);

}

}

在系统准备解锁完成后,PhoneWindowManager回调KeyguardService的startKeyguardExitAnimation

private void handleStartKeyguardExitAnimation(long startTime, long fadeoutDuration) {

Trace.beginSection("KeyguardViewMediator#handleStartKeyguardExitAnimation");

if (DEBUG) Log.d(TAG, "handleStartKeyguardExitAnimation startTime=" + startTime

+ " fadeoutDuration=" + fadeoutDuration);

synchronized (KeyguardViewMediator.this) {

if (!mHiding) {

return;

}

mHiding = false;

if (mWakeAndUnlocking && mDrawnCallback != null) {

// Hack level over 9000: To speed up wake-and-unlock sequence, force it to report

// the next draw from here so we don't have to wait for window manager to signal

// this to our ViewRootImpl.

mStatusBarKeyguardViewManager.getViewRootImpl().setReportNextDraw();

notifyDrawn(mDrawnCallback);

mDrawnCallback = null;

}

// only play "unlock" noises if not on a call (since the incall UI

// disables the keyguard)

if (TelephonyManager.EXTRA_STATE_IDLE.equals(mPhoneState)) {

playSounds(false);

}

mWakeAndUnlocking = false;

setShowingLocked(false);

mDismissCallbackRegistry.notifyDismissSucceeded();

mStatusBarKeyguardViewManager.hide(startTime, fadeoutDuration);

resetKeyguardDonePendingLocked();

mHideAnimationRun = false;

adjustStatusBarLocked();

sendUserPresentBroadcast();

mUpdateMonitor.setKeyguardGoingAway(false /* goingAway */);

// ADD FOR FINGERPRINT SHOT BEGIN

mFingerPrintManager.notifyFpService(1, null);

// ADD FOR FINGERPRINT SHOT END

}

Trace.endSection();

}

播放解锁声音、设置StatusBar的flag、发出ACTION_USER_PRESENT广播、隐藏KeyguardView,解锁流程结束

推荐文章

你可能感兴趣的:(手机滑屏解锁算法java)