FW SystemUI Keyguard解析(一)

什么是keyguard?
  • Android在触发解锁流程的时候,会根据设置的解锁方式不同,获取不同的解锁布局,来展示解锁功能.
    • (1).PIN码,纯数字不含字母
    • (2).密码,数字+字母组合等
    • (3).其他SIM卡PIN码等

以上解锁方式,展示以及部分逻辑都是通过systemui的keyguard模块来展示的.

相关布局举例
  • 这里使用密码解锁来举例,界面如下

    针对密码输入的视图是通过KeyguardPINView来进行相关的逻辑交互.
  • 详细的视图层级如下图
    在这里插入图片描述
    可以看到通知的panel view和锁屏的布局都是在
    NotifycationShaodwWindowView里面,这两个视图在手机上面用起来大家应该都不陌生
    锁屏的时候会有通知展示,微信消息之类的,然后这个屏幕一上划就会进入输入密码的界面.
    往往这个交互过程中都会伴随着动画,这个动画就是systemui中的管理通知的manager中去实现的.
  • 接着回到锁屏这一块内容描述
    • 解锁相关的内容是怎么被添加到屏幕上的?
    • 又是怎么切换不同的解锁模式的?
    • 是根据什么判定应该显示什么解锁视图?
      等等一系列问题
查找或者解析当前的解锁View是谁?
  • KeyguardHostView被添加到视图上面并且完成解析的时候会调用onFinishInflate()
at com.android.keyguard.KeyguardSecurityContainer.getSecurityViewIdForMode(KeyguardSecurityContainer.java:892)
at com.android.keyguard.KeyguardSecurityContainer.getSecurityView(KeyguardSecurityContainer.java:468)
at com.android.keyguard.KeyguardSecurityContainer.showSecurityScreen(KeyguardSecurityContainer.java:770)
at com.android.keyguard.KeyguardSecurityContainer.showPrimarySecurityScreen(KeyguardSecurityContainer.java:669)
at com.android.keyguard.KeyguardHostView.onFinishInflate(KeyguardHostView.java:154)

最终会调用到KeyguardSecurityContainer.getSecurityViewIdForMode()函数

    private int getSecurityViewIdForMode(SecurityMode securityMode) {
        switch (securityMode) {
            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 SimPin: return R.id.keyguard_sim_pin_view;
            case SimPuk: return R.id.keyguard_sim_puk_view;
        }
        return 0;
    }
      public int getLayoutIdFor(SecurityMode securityMode) {
        switch (securityMode) {
            case Pattern: return R.layout.keyguard_pattern_view;
            case PIN: return R.layout.keyguard_pin_view;
            case Password: return R.layout.keyguard_password_view;
            case SimPin: return R.layout.keyguard_sim_pin_view;
            case SimPuk: return R.layout.keyguard_sim_puk_view;
            default:
                return 0;
        }
    }

这里会通过传入的模式返回固定的控件id,然后把匹配到的view返回给调用地方,记录下来,以方便后续做一系列操作,如果没有找到,证明这是第一次调用这个函数,view还没有被添加过,就直接匹配layoutId直接infalter一个新的View出来,并且同时会addmSecurityViewFlipper控件上.

 protected 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 = mInjectionInflationController.injectable(inflater)
                    .inflate(layoutId, mSecurityViewFlipper, false);
            mSecurityViewFlipper.addView(v);
            updateSecurityView(v);
            view = (KeyguardSecurityView)v;
            view.reset();
        }
        return view;
    }
如何滑动控制显示?

上面的内容已经能够看到解锁相关的视图也是添加到NotificationShadeWindowView这个父控件之下的,那被控制相关的猜测多多少少都和这个有关系,断点查看调用栈在这里插入图片描述
onTouch这里开始,也就是NotificationPanelViewController类中,会去调用super.onTouch

    @Override
            public boolean onTouch(View v, MotionEvent event) {
             //...省略
                handled |= super.onTouch(v, event);
                return !mDozing || mPulsing || handled;
            }

查看继承关系是继承自PancelViewControlller的,继续追溯父类
在这里插入图片描述
在父类的touch中当判定非误触等一系列判断值通过之后就会调用到setExpandedHeightInternal

                case MotionEvent.ACTION_MOVE:
                  //...省略代码
                    if (!mJustPeeked && (!mGestureWaitForTouchSlop || mTracking)
                            && !isTrackingBlocked()) {
                        setExpandedHeightInternal(newHeight);
                    }
                    break;

调用到PanelViewController

 public void setExpandedHeightInternal(float h) {
       //...省略
        onHeightUpdated(mExpandedHeight);
        notifyBarPanelExpansionChanged();
    }
    
 protected void notifyBarPanelExpansionChanged() {
        //...省略
        for (int i = 0; i < mExpansionListeners.size(); i++) {
            mExpansionListeners.get(i).onPanelExpansionChanged(mExpandedFraction, mTracking);
        }
    }

然后就调用到锁屏相关了StatusBarKeyguardViewManager

  @Override
    public void onPanelExpansionChanged(float expansion, boolean tracking) {
        //...省略
            if (expansion != KeyguardBouncer.EXPANSION_HIDDEN && tracking
                    && !mKeyguardStateController.canDismissLockScreen()
                    && !mBouncer.isShowing() && !mBouncer.isAnimatingAway()) {
                mBouncer.show(false /* resetSecuritySelection */, false /* scrimmed */);
            }
       //...省略
    }

通过KeyguardBouncer去控制锁屏视图,这里post了一个Runnable,在Runnable中把当前mRoot显示
并且根据不同条件,最终会调用Keyguard视图的显示动画.

public void show(boolean resetSecuritySelection, boolean isScrimmed) {
      //...省略
        if (mKeyguardStateController.isFaceAuthEnabled() && !needsFullscreenBouncer()
                && !mKeyguardUpdateMonitor.userNeedsStrongAuth()
                && !mKeyguardBypassController.getBypassEnabled()) {
            mHandler.postDelayed(mShowRunnable, BOUNCER_FACE_DELAY);
        } else {
            DejankUtils.postAfterTraversal(mShowRunnable);
        }
         //...省略
    }

 private final Runnable mShowRunnable = new Runnable() {
        @Override
        public void run() {
            mRoot.setVisibility(View.VISIBLE);
            //...省略代码
            if (mKeyguardView.getHeight() != 0 && mKeyguardView.getHeight() != mStatusBarHeight) {
                mKeyguardView.startAppearAnimation();
            } else {
                mKeyguardView.getViewTreeObserver().addOnPreDrawListener(
                        new ViewTreeObserver.OnPreDrawListener() {
                            @Override
                            public boolean onPreDraw() {
                                mKeyguardView.getViewTreeObserver().removeOnPreDrawListener(this);
                                mKeyguardView.startAppearAnimation();
                                return true;
                            }
                        });
                mKeyguardView.requestLayout();
            }
            mShowingSoon = false;
            if (mExpansion == EXPANSION_VISIBLE) {
                mKeyguardView.onResume();
                mKeyguardView.resetSecurityContainer();
                showPromptReason(mBouncerPromptReason);
            }
        }
    };

比如KeyguardPINView的显示动画就一个进入的位移动画

  public void startAppearAnimation() {
        enableClipping(false);
        setAlpha(1f);
        setTranslationY(mAppearAnimationUtils.getStartTranslation());
        AppearAnimationUtils.startTranslationYAnimation(this, 0 /* delay */, 500 /* duration */,
                0, mAppearAnimationUtils.getInterpolator());
        mAppearAnimationUtils.startAnimation2d(mViews,
                new Runnable() {
                    @Override
                    public void run() {
                        enableClipping(true);
                    }
                });
    }

以上就是通过manager中去滑动屏幕显示keyguard的流程了

锁屏布局内部View解析之,玻璃效果

在文章一开始的时候有一个布局的树状图
里面能看到在NotificationShadeWindowView包含了很多个布局,我们就看看代码中是在哪里实现的.

  • 布局文件super_notification_shadow.xml 中会使用NotificationShadeWindowView视图,然后这个xml文件就会包含我们一开始的布局树状图里面所有子View了.
  • 这个xml文件是在哪里被解析使用的?
    • 会在StausBar中通过调用SuperStatusBarViewFactory 类的方法来创建得到,这样视图被解析后,那么后续这个xml文件中所有自定义View的逻辑就会开始跑起来.
public class StatusBar {
//...省略
protected NotificationShadeWindowView mNotificationShadeWindowView;
//...省略
    private void inflateStatusBarWindow() {
        mNotificationShadeWindowView = mSuperStatusBarViewFactory.getNotificationShadeWindowView();
        //...省略
    }
//...省略
}

public class SuperStatusBarViewFactory {
   public NotificationShadeWindowView getNotificationShadeWindowView() {
        //...省略
        mNotificationShadeWindowView = (NotificationShadeWindowView)
                mInjectionInflationController.injectable(
                LayoutInflater.from(mContext)).inflate(R.layout.super_notification_shade,
                /* root= */ null);
         //...省略
        return mNotificationShadeWindowView;
    }
}

  • 对应的xml文件代码如下,注意:以下代码简化了很多宽度高度等代码,主要是为了方便查看视图嵌套关系
<com.android.systemui.statusbar.phone.NotificationShadeWindowView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:sysui="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">

    <com.android.systemui.statusbar.BackDropView
            android:id="@+id/backdrop" >
        <ImageView android:id="@+id/backdrop_back" />
        <ImageView android:id="@+id/backdrop_front" />
    </com.android.systemui.statusbar.BackDropView>
	<!--玻璃效果背景,锁屏展示后,这个视图内部会通过动态设置Drawble的形式,展示类似于半透明黑色遮罩,或者毛玻璃效果等,视定制内容而定-->
    <com.android.systemui.statusbar.ScrimView
        android:id="@+id/scrim_behind" />

    <include layout="@layout/status_bar_expanded" />

    <include layout="@layout/brightness_mirror" />
    <!--玻璃前景,也是为了视图效果-->
    <com.android.systemui.statusbar.ScrimView
        android:id="@+id/scrim_in_front"/>

    <LinearLayout
        android:id="@+id/lock_icon_container">
        <com.android.systemui.statusbar.phone.LockIcon
            android:id="@+id/lock_icon"/>
        <com.android.keyguard.KeyguardMessageArea
            android:id="@+id/keyguard_message_area" />
    </LinearLayout>
</com.android.systemui.statusbar.phone.NotificationShadeWindowView>
  • 25
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值