by jing.chen
锁屏的解锁操作是在锁屏界面向上滑动实现的,通过向上滑动调出解锁界面(如图案、PIN、密码解锁界面),在解锁界面输入正确的密码之后解锁显示launcher。向上滑动如何调出解锁界面,需要分析PanelView的onTouchEvent事件,用户向上滑动的整个touch事件分析如下:
1、用户手指touch屏幕,产生touch down事件,最底层view StatusBarWindowView会执行onInterceptTouchEvent,看是否需要拦截touch事件,touch down事件在此没有被拦截,再一级级往子View传递,都没有被拦截,之后执行OnTouchEvent,从子View开始往父View传递,一级级往父View传递,也都没有消耗touch down事件;
2、用户移动手指,产生touch move事件,最底层view StatusBarWindowView会执行onInterceptTouchEvent,看是否需要拦截touch事件,touch move事件在此也没有被拦截,再一级级往子View传递,都没有被拦截,之后执行OnTouchEvent,从子View开始往父View传递,一级级往父View传递,到PanelView这里当手指移动的距离达到一定的阈值会调用onTrackingStarted从而设置mTracking的值为true,接收此touch move事件,之后的touch事件直接传到此View。在用户滑动过程会调用setExpandedHeightInternal,进而调用NotificationPanelView的onHeightUpdated进行锁屏上的时间和通知View根据手指的移动距离进行缩小、变透明处理;
3、用户抬起手指,产生touch up事件,PanelView接收到这个事件后会调用endMotionEvent,如果手指从down到up之间移动的距离达到一定阈值会调用onTrackingStopped,从而调出解锁界面;
代码分析如下:
PanelView.java
@Override
public boolean onTouchEvent(MotionEvent event) {
...
final float x = event.getX(pointerIndex);
final float y = event.getY(pointerIndex);
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
mGestureWaitForTouchSlop = isFullyCollapsed() || hasConflictingGestures();
mIgnoreXTouchSlop = isFullyCollapsed() || shouldGestureIgnoreXTouchSlop(x, y);
}
switch (event.getActionMasked()) {
...
case MotionEvent.ACTION_MOVE:
float h = y - mInitialTouchY;
// If the panel was collapsed when touching, we only need to check for the
// y-component of the gesture, as we have no conflicting horizontal gesture.
if (Math.abs(h) > mTouchSlop
&& (Math.abs(h) > Math.abs(x - mInitialTouchX)
|| mIgnoreXTouchSlop)) {
mTouchSlopExceeded = true;
if (mGestureWaitForTouchSlop && !mTracking && !mCollapsedAndHeadsUpOnDown) {
if (!mJustPeeked && mInitialOffsetOnTouch != 0f) {
startExpandMotion(x, y, false /* startTracking */, mExpandedHeight);
h = 0;
}
cancelHeightAnimator();
removeCallbacks(mPeekRunnable);
mPeekPending = false;
onTrackingStarted();//向上滑动时,手指移动的距离达到一定的阈值会调用onTrackingStarted,设置mTracking值为true,从而接收touch事件
}
}
final float newHeight = Math.max(0, h + mInitialOffsetOnTouch);
if (newHeight > mPeekHeight) {
if (mPeekAnimator != null) {
mPeekAnimator.cancel();
}
mJustPeeked = false;
}
if (-h >= getFalsingThreshold()) {
mTouchAboveFalsingThreshold = true;
mUpwardsWhenTresholdReached = isDirectionUpwards(x, y);
}
if (!mJustPeeked && (!mGestureWaitForTouchSlop || mTracking) && !isTrackingBlocked()) {
setExpandedHeightInternal(newHeight);//用户滑动过程会调用setExpandedHeightInternal,进而调用NotificationPanelView的onHeightUpdated进行锁屏上的时间和通知View根据手指的移动距离进行缩小、变透明处理
}
trackMovement(event);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
trackMovement(event);
endMotionEvent(event, x, y, false /* forceCancel */);
break;
}
return !mGestureWaitForTouchSlop || mTracking;
}
protected void onTrackingStarted() {
endClosing();
mTracking = true;
mCollapseAfterPeek = false;
mBar.onTrackingStarted();
notifyExpandingStarted();
notifyBarPanelExpansionChanged();
}
NotificationPanelView.java
@Override
protected void onHeightUpdated(float expandedHeight, boolean isMovingInKeyguard) {
mExpandedHeightTpv = expandedHeight;
LogUtils.d(TAG,"--------onHeightUpdated");
if (!mQsExpanded || mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted) {
positionClockAndNotifications(isMovingInKeyguard);//锁屏上的时间和通知View根据手指的移动距离进行缩小、变透明处理
}
...
}
endMotionEvent调出解锁界面流程如下:
->PanelView.endMotionEvent(MotionEvent event, float x, float y, boolean forceCancel)
->NotificationPanelView.onTrackingStopped(boolean expand)
->PhoneStatusBar.onTrackingStopped(boolean expand)
->PhoneStatusBar.showBouncer()
->StatusBarKeyguardViewManager.dismiss()
->StatusBarKeyguardViewManager.dismiss(boolean authenticated)
->StatusBarKeyguardViewManager.showBouncer(boolean authenticated)
->KeyguardBouncer.show(boolean resetSecuritySelection, boolean authenticated)
->KeyguardBouncer.mShowRunnable
调用KeyguardBouncer.show(boolean resetSecuritySelection, boolean authenticated)方法并没有重新加载解锁界面,解锁界面是在灭屏时调用StatusBarKeyguardViewManager.reset后重新inflate进来的,在此处只是通过KeyguardBouncer.mShowRunnable将这个解锁界面显示出来,灭屏时解锁界面的调用栈如下:
“main@52911” prio=5 tid=0x1 nid=NA runnable
java.lang.Thread.State: RUNNABLE
at com.android.keyguard.KeyguardSecurityContainer.showSecurityScreen(KeyguardSecurityContainer.java:584)
at com.android.keyguard.KeyguardSecurityContainer.showPrimarySecurityScreen(KeyguardSecurityContainer.java:416)
at com.android.keyguard.KeyguardHostView.onFinishInflate(KeyguardHostView.java:168)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:867)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:821)
at android.view.LayoutInflater.parseInclude(LayoutInflater.java:994)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:854)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:821)
at android.view.LayoutInflater.inflate(LayoutInflater.java:518)
- locked <0xd005> (a java.lang.Object[])
at android.view.LayoutInflater.inflate(LayoutInflater.java:426)
at android.view.LayoutInflater.inflate(LayoutInflater.java:377)
at com.android.systemui.statusbar.phone.KeyguardBouncer.inflateView(KeyguardBouncer.java:317)
at com.android.systemui.statusbar.phone.KeyguardBouncer.ensureView(KeyguardBouncer.java:307)
at com.android.systemui.statusbar.phone.KeyguardBouncer.needsFullscreenBouncer(KeyguardBouncer.java:347)
at com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.showBouncerOrKeyguard(StatusBarKeyguardViewManager.java:130)
at com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.reset(StatusBarKeyguardViewManager.java:192)
至此把用户设定的解锁界面(图案、密码、PIN码)显示出来