Android 9.0系统源码_SystemUI(六)滑动锁屏的创建

一、前言

前面几篇文章大致介绍了SystemUI的两个模块,StatusBar和QuickSetting,这篇文章开始分析Keyguard模块。
对于锁屏呢,需要有个基本认知,它分为两类,一是滑动锁屏,一是安全锁屏。滑动锁屏是指通过手指滑动即可解锁的锁屏,安全锁屏是指密码锁,图案锁,PIN码锁等等。这两种锁屏是在不同的地方创建的,不可一概而论,而本文只分析滑动锁屏。
https://blog.csdn.net/omnispace/article/details/78566838
https://juejin.cn/user/2805609401693943/posts

二、滑动锁屏视图

1、根据SystemUI之StatusBar创建可知,整个SystemUI视图是由super_status_bar.xml创建的

<!--根布局继承自FrameLayout的StatusBarWindowView-->
<com.android.systemui.statusbar.phone.StatusBarWindowView
    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"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:visibility="gone"
            sysui:ignoreRightInset="true"
            >
        <ImageView android:id="@+id/backdrop_back"
                   android:layout_width="match_parent"
                   android:scaleType="centerCrop"
                   android:layout_height="match_parent" />
        <ImageView android:id="@+id/backdrop_front"
                   android:layout_width="match_parent"
                   android:layout_height="match_parent"
                   android:scaleType="centerCrop"
                   android:visibility="invisible" />
    </com.android.systemui.statusbar.BackDropView>

    <com.android.systemui.statusbar.ScrimView
        android:id="@+id/scrim_behind"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:importantForAccessibility="no"
        sysui:ignoreRightInset="true"/>
        
 	状态栏容器
    <FrameLayout
        android:id="@+id/status_bar_container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
        
	车载模式的布局 
    <ViewStub android:id="@+id/fullscreen_user_switcher_stub"
              android:layout="@layout/car_fullscreen_user_switcher"
              android:layout_width="match_parent"
              android:layout_height="match_parent"/>
              
    整个下拉通知面版,包括滑动锁屏界面
    <include layout="@layout/status_bar_expanded"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="invisible" />
        
    这里亮度调节bar
    <include layout="@layout/brightness_mirror" />

	状态栏下拉后,背景,半透明灰色
    <com.android.systemui.statusbar.ScrimView
        android:id="@+id/scrim_in_front"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:importantForAccessibility="no"
        sysui:ignoreRightInset="true"/>

</com.android.systemui.statusbar.phone.StatusBarWindowView>

2、在这个布局中,include了一个status_bar_expanded.xml布局,这是整个下拉通知面版,包括滑动锁屏的各种控件,来看看这个布局:

<com.android.systemui.statusbar.phone.NotificationPanelView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:systemui="http://schemas.android.com/apk/res-auto"
    android:id="@+id/notification_panel"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/transparent" >

    滑动锁屏界面状态视图: 时间,日期
    <include
        layout="@layout/keyguard_status_view"
        android:visibility="gone" />

    <com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="@integer/notification_panel_layout_gravity"
        android:id="@+id/notification_container_parent"
        android:clipToPadding="false"
        android:clipChildren="false">
       
        QS界面
        <FrameLayout
            android:id="@+id/qs_frame"
            android:layout="@layout/qs_panel"
            android:layout_width="@dimen/qs_panel_width"
            android:layout_height="match_parent"
            android:layout_gravity="@integer/notification_panel_layout_gravity"
            android:clipToPadding="false"
            android:clipChildren="false"
            systemui:viewType="com.android.systemui.plugins.qs.QS" />

		显示通知的容器
        <com.android.systemui.statusbar.stack.NotificationStackScrollLayout
            android:id="@+id/notification_stack_scroller"
            android:layout_marginTop="@dimen/notification_panel_margin_top"
            android:layout_width="@dimen/notification_panel_width"
            android:layout_height="match_parent"
            android:layout_gravity="@integer/notification_panel_layout_gravity"
            android:layout_marginBottom="@dimen/close_handle_underlap" />

        <include layout="@layout/ambient_indication"
            android:id="@+id/ambient_indication_container" />

        <ViewStub
            android:id="@+id/keyguard_user_switcher"
            android:layout="@layout/keyguard_user_switcher"
            android:layout_height="match_parent"
            android:layout_width="match_parent" />
            
		滑动锁屏状态栏
        <include
            layout="@layout/keyguard_status_bar"
            android:visibility="invisible" />

        <Button
            android:id="@+id/report_rejected_touch"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="@dimen/status_bar_header_height_keyguard"
            android:text="@string/report_rejected_touch"
            android:visibility="gone" />

    </com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer>

	这里锁屏底部图标, 填充整个父布局,例如左下角的图标,右下角的图标Camera
    <include
        layout="@layout/keyguard_bottom_area"
        android:visibility="gone" />

    <com.android.systemui.statusbar.AlphaOptimizedView
        android:id="@+id/qs_navbar_scrim"
        android:layout_height="96dp"
        android:layout_width="match_parent"
        android:layout_gravity="bottom"
        android:visibility="invisible"
        android:background="@drawable/qs_navbar_scrim" />

</com.android.systemui.statusbar.phone.NotificationPanelView>

可以看到,滑动锁屏的各个部分比较分散,并不是在同一容器中集中创建的。

三、滑动锁屏的显示

1、一般我们通过电源键的开关来锁屏的,本文来分析下,从开机到滑动锁屏显示的过程。

在开机的过程中,当ActivityManagerService启动完毕后,会创建SystemUI

frameworks/base/services/java/com/android/server/SystemServer.java

    static final void startSystemUi(Context context, WindowManagerService windowManager) {
    	//启动SystemUIService
        Intent intent = new Intent();
        intent.setComponent(new ComponentName("com.android.systemui",
                    "com.android.systemui.SystemUIService"));
        intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
        context.startServiceAsUser(intent, UserHandle.SYSTEM);
        
        // 2. 通知WMS,SystemUI已经启动
        windowManager.onSystemUiStarted();
    }

2、前面的文章已经分析了SystemUI的启动,这个过程创建了整个SystemUI的视图,包括滑动锁屏的视图。现在来看看WindowManagerService在SystemUI启动后,做了什么

frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

    public void onSystemUiStarted() {
        mPolicy.onSystemUiStarted();
    }

3、Window Manager 通知了 PhoneWindowManager , SystemUI 已经启动。

public class PhoneWindowManager implements WindowManagerPolicy {
	...
    private void bindKeyguard() {
        synchronized (mLock) {
            if (mKeyguardBound) {
                return;
            }
            mKeyguardBound = true;
        }
        mKeyguardDelegate.bindService(mContext);
    }

    @Override
    public void onSystemUiStarted() {
        //绑定锁屏服务KeyguardService
        bindKeyguard();
    } 
	...
}

4、策略类 PhoneWindowManager 通过一个代理类 KeyguardServiceDelegate 来绑定了 KeyguardService。

KeyguardService 是一个标准的 Service,在绑定它的时候会返回一个 IBinder 对象,也就是服务端接口。我们在后面直接称 KeyguardService 为锁屏服务端。

锁屏组件所对应的服务名称:

<string name="config_keyguardComponent" translatable="false">com.android.systemui/com.android.systemui.keyguard.KeyguardService</string>
public class KeyguardServiceDelegate {
	...
    public void bindService(Context context) {
        Intent intent = new Intent();
        final Resources resources = context.getApplicationContext().getResources();

        final ComponentName keyguardComponent = ComponentName.unflattenFromString(
                resources.getString(com.android.internal.R.string.config_keyguardComponent));
        intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
        intent.setComponent(keyguardComponent);
		
		//绑定服务
        if (!context.bindServiceAsUser(intent, mKeyguardConnection,
                Context.BIND_AUTO_CREATE, mHandler, UserHandle.SYSTEM)) {
            Log.v(TAG, "*** Keyguard: can't bind to " + keyguardComponent);
            mKeyguardState.showing = false;
            mKeyguardState.showingAndNotOccluded = false;
            mKeyguardState.secure = false;
            synchronized (mKeyguardState) {
            mKeyguardState.deviceHasKeyguard = false;
            }
        } else {
            if (DEBUG) Log.v(TAG, "*** Keyguard started");
        }
    }
    ...
 }

5、这就是一个标准的绑定 Service 流程,通过绑定时传入的参数 mKeyguardConnection 可以查看成功绑定后的操作,而ServiceConnection 是服务绑定成功后返回的对象。

public class KeyguardServiceDelegate {
	...
 private final ServiceConnection mKeyguardConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mKeyguardService = new KeyguardServiceWrapper(mContext,
                    IKeyguardService.Stub.asInterface(service), mCallback);
            if (mKeyguardState.systemIsReady) {
                // If the system is ready, it means keyguard crashed and restarted.
                mKeyguardService.onSystemReady();
     		}
     		...
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            if (DEBUG) Log.v(TAG, "*** Keyguard disconnected (boo!)");
            mKeyguardService = null;
            mKeyguardState.reset();
            mHandler.post(() -> {
                try {
                    // There are no longer any keyguard windows on secondary displays, so pass
                    // INVALID_DISPLAY. All that means is that showWhenLocked activities on
                    // secondary displays now get to show.
                    ActivityManager.getService().setLockScreenShown(true /* keyguardShowing */,
                            false /* aodShowing */, INVALID_DISPLAY);
                } catch (RemoteException e) {
                    // Local call.
                }
            });
        }
    };
    ...
    }

6、通过查询各种状态,然后按顺序向锁屏服务端发送指令。这里我们只分析服务端的onSystemReady()的实现

代理类 KeyguardServiceDelegate 内部通过 KeyguardState 对象保存锁屏的状态,从而控制 KeyguardService 的行为。

    private final IKeyguardService.Stub mBinder = new IKeyguardService.Stub() {
    	...
        @Override // Binder interface
        public void onSystemReady() {
            // SYSTEM 用户或者有 android.Manifest.permission.CONTROL_KEYGUARD 权限
            checkPermission();
            mKeyguardViewMediator.onSystemReady();
        }
        ...
    };

7、锁屏服务端又辗转通知了 KeyguardViewMediator,KeyguardViewMediator 向 Handler 发送了一个 SYSTEM_READY 事件来处理,最终会调用 handleSystemReady()

frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java

public class KeyguardViewMediator extends SystemUI {
	...
    public void onSystemReady() {
        mHandler.obtainMessage(SYSTEM_READY).sendToTarget();
    }
    ...
    private Handler mHandler = new Handler(Looper.myLooper(), null, true) {
    @Override
    public void handleMessage(Message msg) {
     switch (msg.what) {
            ...
            case SYSTEM_READY:
                    handleSystemReady();
                    break;
            ...        
     }
	}
}    
  • KeyguardViewMediator是典型的中介者模式的应用,它综合了各方面的信息来控制锁屏。
  • KeyguardViewMediator有一个与主线程 Looper 关联的 Handlder,事件和处理都是通过这个 Handler,因此保证了事件处理的有序性,这样就不会导致界面刷新混乱。
    private void handleSystemReady() {
        synchronized (this) {
            mSystemReady = true;
            // 开启锁屏, 这里参数为 null
            doKeyguardLocked(null);

            // KeyguardUpdateMonitor 通过监听数据库Uri, 注册广播接收器,向各种服务注册监听,从而获取到与锁屏有关的更新
            // KeyguardViewMediator 这个中介者关心的回调如下
            // 1. onClockVisibilityChanged
            // 2. onDeviceProvisioned
            // 3. onSimStateChanged
            // 4. onBiometricAuthFailed, onBiometricAuthenticated
            // 5. onHasLockscreenWallpaperChanged
            mUpdateMonitor.registerCallback(mUpdateCallback);
        }
        maybeSendUserPresentBroadcast();
    }

8、doKeyguardLocked方法又会调用showLocked方法,showLocked方法通过Handler发送SHOW类型的消息,最终调用handleShow方法,handleShow方法会继续调用KeyguardDisplayManager的show方法。

  
    private void doKeyguardLocked(Bundle options) {
    	...
        showLocked(options);
    }
	...
	//显示锁屏
    private void showLocked(Bundle options) {
     	...
        mShowKeyguardWakeLock.acquire();
        Message msg = mHandler.obtainMessage(SHOW, options);
        mHandler.sendMessage(msg);
    }
	...
	//处理消息的Handler
	private Handler mHandler = new Handler(Looper.myLooper(), null, true /*async*/) {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case SHOW:
                	//处理显示锁屏的消息
                    handleShow((Bundle) msg.obj);
                    break;
           		...
         }
   }
 	
    private void handleShow(Bundle options) {
    	...
       //继续调用KeyguardDisplayManager的show方法
       mKeyguardDisplayManager.show();
    }

9、KeyguardDisplayManager的show方法又会继续调用updateDisplays方法,

public class KeyguardDisplayManager {
  	...
  	public void show() {
        if (!mShowing) {
            if (DEBUG) Slog.v(TAG, "show");
            mMediaRouter.addCallback(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY,
                    mMediaRouterCallback, MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY);
            updateDisplays(true);
        }
        mShowing = true;
    }
    ...
  protected void updateDisplays(boolean showing) {
        Presentation originalPresentation = mPresentation;
        if (showing) {
            MediaRouter.RouteInfo route = mMediaRouter.getSelectedRoute(
                    MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY);
            boolean useDisplay = route != null
                    && route.getPlaybackType() == MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE;
            Display presentationDisplay = useDisplay ? route.getPresentationDisplay() : null;

            if (mPresentation != null && mPresentation.getDisplay() != presentationDisplay) {
                if (DEBUG) Slog.v(TAG, "Display gone: " + mPresentation.getDisplay());
                mPresentation.dismiss();
                mPresentation = null;
            }

            if (mPresentation == null && presentationDisplay != null) {
                if (DEBUG) Slog.i(TAG, "Keyguard enabled on display: " + presentationDisplay);
                mPresentation = new KeyguardPresentation(mContext, presentationDisplay,
                        R.style.keyguard_presentation_theme);
                mPresentation.setOnDismissListener(mOnDismissListener);
                try {
                    mPresentation.show();
                } catch (WindowManager.InvalidDisplayException ex) {
                    Slog.w(TAG, "Invalid display:", ex);
                    mPresentation = null;
                }
            }
        } else {
            if (mPresentation != null) {
                mPresentation.dismiss();
                mPresentation = null;
            }
        }

        // mPresentation is only updated when the display changes
        if (mPresentation != originalPresentation) {
            final int displayId = mPresentation != null
                    ? mPresentation.getDisplay().getDisplayId() : INVALID_DISPLAY;
            mCallback.onSecondaryDisplayShowingChanged(displayId);
        }
    }
 	...
 }
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据提供的引用内容,我们可以了解到Android 9.0系统源码中SwipeHelper.java的位置为frameworks/base/packages/SystemUI/src/com/android/systemui/SwipeHelper.java。SwipeHelper.java是一个帮助类,用于处理滑动手势的事件。下面是SwipeHelper.java源码分析的流程: 1.首先,我们需要了解SwipeHelper.java的作用和功能。SwipeHelper.java是一个帮助类,用于处理滑动手势的事件。它可以检测用户的手势方向,并根据手势方向执行相应的操作。 2.接下来,我们需要了解SwipeHelper.java的主要方法和变量。SwipeHelper.java包含了一些重要的方法和变量,例如: - mSwipeDirection:表示滑动的方向,可以是上、下、左、右等方向。 - mSwipeThreshold:表示滑动的阈值,当用户滑动的距离超过这个阈值时,才会触发滑动事件。 - onInterceptTouchEvent:用于拦截触摸事件,判断是否需要处理滑动事件。 - onTouchEvent:用于处理触摸事件,根据手势方向执行相应的操作。 3.然后,我们需要了解SwipeHelper.java的具体实现。SwipeHelper.java主要实现了以下几个方法: - onInterceptTouchEvent:该方法用于拦截触摸事件,判断是否需要处理滑动事件。在该方法中,SwipeHelper会根据触摸事件的类型和位置,判断是否需要处理滑动事件。如果需要处理滑动事件,则返回true,否则返回false。 - onTouchEvent:该方法用于处理触摸事件,根据手势方向执行相应的操作。在该方法中,SwipeHelper会根据触摸事件的类型和位置,判断用户的手势方向,并根据手势方向执行相应的操作。例如,如果用户向左滑动,则会执行onSwipeLeft方法。 - onSwipeLeft:该方法用于处理向左滑动事件。在该方法中,SwipeHelper会执行一些操作,例如关闭通知栏、打开侧边栏等。 - onSwipeRight:该方法用于处理向右滑动事件。在该方法中,SwipeHelper会执行一些操作,例如打开通知栏、关闭侧边栏等。 - onSwipeUp:该方法用于处理向上滑动事件。在该方法中,SwipeHelper会执行一些操作,例如打开最近任务列表、打开应用程序列表等。 - onSwipeDown:该方法用于处理向下滑动事件。在该方法中,SwipeHelper会执行一些操作,例如关闭最近任务列表、关闭应用程序列表等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值