Android 8.1.0 SystemUI 修改之 - 系统锁屏状态下点击用户头像实现自动调用解锁界面

        公司接了国外的项目,项目是基于Android 8.1.0 源码进行系统定制。在系统有多个 User 并且是锁屏(有屏幕锁)状态下的时候,客户进行了操作,说 After switching user I should be getting screen to enter my PIN/Pattern/Password but I was still on the same screen just the position of user names were Swapped.device is not asking the security pin, pattern or password until user swipe up.It is very difficult to switch user if device has multiple users setup...

        首先搞明白客户是啥意思,英语水平一般,和产品聊了一下整明白了。客户意思是 在系统有多个 User (多个User 都设置了屏幕锁),并且设备是锁屏状态为前提条件下,当点击不同User 的头像进行切换用户之后,只是看到了用户的头像显示位置变了,系统并没有显示目标用户的解锁界面来方便的进行解锁目标用户,客户想要的是:锁屏状态下,切换 User 成功了,就立刻触发上划屏幕并输入密码的流程。

        新需求:系统锁屏状态下点击用户头像实现自动调用解锁界面 or 输入密码界面 or 直接解锁(无密码)。

        那该怎么做呢?思路是这样的:在如下锁屏界面点击User 的头像进行切换User 时,切换User成功之后,触发上滑解锁屏幕的逻辑(虽然现在看起来挺简单的,但是在做的时候踩了不少坑...)。

锁屏状态进行切换用户

        首先我们知道,这个Owner / Zzz 这两个User 所显示的View 是锁屏的 HostView,源码中查找  KeyguardHostView.java,源码中位置

...\SystemUI\src\com\android\keyguard\ 包

 查找到如下代码:

    private final KeyguardUpdateMonitorCallback mUpdateCallback =
            new KeyguardUpdateMonitorCallback() {

        @Override
        public void onUserSwitchComplete(int userId) {
            //关键代码
            getSecurityContainer().showPrimarySecurityScreen(false /* turning off */);

        }

        @Override
        public void onTrustGrantedWithFlags(int flags, int userId) {
            //code...
        }
    };

 可以看到在 mUpdateCallback 对象中有一个 回调方法 onUserSwitchComplete(userId),从这个回调方法的名字也可以看出来,它会在用户切换成功之后被回调。那这个 onUserSwitchComplete(userId)方法会在哪里被调用呢?看 KeyguardUpdateMonitor.java 这个关键类,源码位置

...\SystemUI\src\com\android\keyguard\ 包

关于这个类的作用,源码注释写的很清楚:

/**
 * Watches for updates that may be interesting to the keyguard, and provides
 * the up to date information as well as a registration for callbacks that care
 * to be updated.
 *
 * Note: ...
 */

它观察 keyguard 可能感兴趣的更新,并提供最新信息以及需要更新的回调注册。查看类中代码:

    private KeyguardUpdateMonitor(Context context) {
        mContext = context;
        //code ...

        //关键代码
        try {
            ActivityManager.getService().registerUserSwitchObserver(
                    new UserSwitchObserver() {
                        @Override
                        public void onUserSwitching(int newUserId, IRemoteCallback reply) {
                            mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCHING,
                                    newUserId, 0, reply));
                        }
                        @Override
                        public void onUserSwitchComplete(int newUserId) throws RemoteException {
                            mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCH_COMPLETE,
                                    newUserId, 0));
                        }
                    }, TAG);
        } catch (RemoteException e) {
            e.rethrowAsRuntimeException();
        }

        //code ...
    }

可以看到在ActivityManager 中注册了一个 UserSwitchObserver,当 onUserSwitchComplete()被回调时,会通过 mHandler 发送一条消息 MSG_USER_SWITCH_COMPLETE ,当 mHanlder 接收到消息之后,会执行 handleUserSwitchComplete(msg.arg1); 方法

    /**
     * Handle {@link #MSG_USER_SWITCH_COMPLETE}
     */
    protected void handleUserSwitchComplete(int userId) {
        for (int i = 0; i < mCallbacks.size(); i++) {
            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
            if (cb != null) {
                cb.onUserSwitchComplete(userId);
            }
        }
    }

这里出现了一个 mCallbacks,这是个啥东西?在 KeyguardUpdateMonitor.java 中 List 声明如下

    private final ArrayList<WeakReference<KeyguardUpdateMonitorCallback>>
            mCallbacks = Lists.newArrayList();

 KeyguardUpdateMonitor.java  还向外提供了一个注册回调的接口方法,用来对进行回调方法的添加注册:

   /**
     * Register to receive notifications about general keyguard information
     * (see {@link InfoCallback}.
     * @param callback The callback to register
     */
    public void registerCallback(KeyguardUpdateMonitorCallback callback) {
        if (DEBUG) Log.v(TAG, "*** register callback for " + callback);
        // Prevent adding duplicate callbacks
        for (int i = 0; i < mCallbacks.size(); i++) {
            if (mCallbacks.get(i).get() == callback) {
                if (DEBUG) Log.e(TAG, "Object tried to add another callback",
                        new Exception("Called by"));
                return;
            }
        }
        mCallbacks.add(new WeakReference<KeyguardUpdateMonitorCallback>(callback));
        removeCallback(null); // remove unused references
        sendUpdates(callback);
    }

而在我们的 KeyguardHostView.java 的构造器中调用了这个方法:

    public KeyguardHostView(Context context, AttributeSet attrs) {
        super(context, attrs);
        //关键代码
        KeyguardUpdateMonitor.getInstance(context).registerCallback(mUpdateCallback);
    }

那流程就清晰了:

在 KeyguardUpdateMonitor.java  中维护了一个 ArrayList<WeakReference<KeyguardUpdateMonitorCallback>>,并对外提供了一个注册添加接口对象的接口方法 registerCallback(mUpdateCallback);(类似于setListener),而在 KeyguardHostView.java 的构造器中调用并添加了接口对象到这个 list  中。

KeyguardUpdateMonitor.java  构造器中注册了 UserSwitchObserver,当观察者观察到当前设备切换用户成功之后,会调用 handleUserSwitchComplete(userId)方法,在该方法中会遍历维护的这个 list 中所有注册进来的 KeyguardUpdateMonitorCallback 接口对象的 onUserSwitchComplete(userId); 方法。

这样,当用户切换成功之后,代码会执行到 KeyguardHostView.java 的 onUserSwitchComplete(userId),此时 KeyguardHostView 对象会调用 KeyguardSecurityContainer 对象的 showPrimarySecurityScreen(false /* turning off */)方法,并最终会调用到  KeyguardSecurityContainer 对象的 showSecurityScreen(securityMode);,即 Container  根据目标 User 设置的securityMode 来展示不同的界面。

 知道了流程,现在我们可以这么修改:

在 showSecurityScreen(securityMode);执行完成之后,我们可以发送一个广播出去,触发 StatusBar 的上滑解锁逻辑。

 【step1】在 KeyguardSecurityContainer 中,添加方法:

   /**
	
     * Shows the unlock screen for the user. (添加方法)
	
     * If the target user does not set a lock such as Pattern / PIN / Password, enter the system directly.
	
     * @param turningOff true if the device is being turned off
	
     * @param isDeviceNeedShowUnlockScreen true if the device need show the unlock screen for the user
	
     */
	
    void showPrimarySecurityScreen(boolean turningOff,boolean isDeviceNeedShowUnlockScreen ) {
	
        showPrimarySecurityScreen(turningOff);
	
	
        if (isDeviceNeedShowUnlockScreen) {
	
            Intent targetIntent =  new Intent(Intent.ACTION_DEVICE_NEED_SHOW_UNLOCK_SCREEN);
	
            mContext.sendBroadcast(targetIntent);
	
         }
	
    }
	
    /**
     * Shows the primary security screen for the user. This will be either the multi-selector
     * or the user's security method.
     * @param turningOff true if the device is being turned off
     */
    void showPrimarySecurityScreen(boolean turningOff) {
        SecurityMode securityMode = mSecurityModel.getSecurityMode(
                KeyguardUpdateMonitor.getCurrentUser());
        if (DEBUG) Log.v(TAG, "showPrimarySecurityScreen(turningOff=" + turningOff + ")");
        showSecurityScreen(securityMode);
}

 【step2】在 KeyguardHostView 中修改方法调用:

    private final KeyguardUpdateMonitorCallback mUpdateCallback =
            new KeyguardUpdateMonitorCallback() {

        @Override
        public void onUserSwitchComplete(int userId) {
            //关键方法
            getSecurityContainer().showPrimarySecurityScreen(false /* turning off */,true /* if need show unlock screen */);

        }

        @Override
        public void onTrustGrantedWithFlags(int flags, int userId) {
            //code...
        }
    };

【step3】在 StatusBar 中添加并注册广播:

Intent.ACTION_DEVICE_NEED_SHOW_UNLOCK_SCREEN

 

    @Override
    public void start() {
        //code...

        
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_USER_SWITCHED);
        //关键代码
        filter.addAction(Intent.ACTION_DEVICE_NEED_SHOW_UNLOCK_SCREEN);
        filter.addAction(Intent.ACTION_USER_ADDED);
        filter.addAction(Intent.ACTION_USER_PRESENT);
        mContext.registerReceiver(mBaseBroadcastReceiver, filter);

        //code...
    }

处理这个 Action:

 private final BroadcastReceiver mBaseBroadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (Intent.ACTION_USER_SWITCHED.equals(action)) {

                //code...

            } else if (Intent.ACTION_DEVICE_NEED_SHOW_UNLOCK_SCREEN.equals(action)) {

                //关键代码
                makeExpandedInvisible();

            } else if (Intent.ACTION_USER_ADDED.equals(action)) {

                //code...

            } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
     
                //code...

            } else if (BANNER_ACTION_CANCEL.equals(action) || BANNER_ACTION_SETUP.equals(action)) {

                //code...

            } else if (NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION.equals(action)) {

                //code...

            }
        }
    };

至此,我们完成了 :系统锁屏状态下点击用户头像实现自动调用解锁界面 or 输入密码界面 or 直接解锁(无密码)。进行编译刷机,验证流程正常。

        第一次写关于修改系统源码的博客,可能有一些不足,写下来是为了下次遇到类似问题可以快速解决,另外写下来可能会给到别人一些帮助。如果你认为有其他的方式可以实现这个需求,可以评论区评论交流一下哇~~

-----------------------------------------------------密封线-----------------------------------------------------

 

其实在做这个任务的时候遇到了一些坑,现在没时间写了,抽空写一下。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值