SystemUI之GlobalActions

2,747 阅读3分钟

本文来分析 SystemUI 的 GlobalAction 模块,GlobalAction 这个名字一听,就感觉范围很广,但是实际只处理了长按 Power 键这个功能。

长按 Power 键首先是需要经过底层处理,然后调用上层的 PhoneWindowManager 来处理,但限于本人的知识范畴,本文将从 PhoneWindowManager 说起。

    // frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
    
    public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
        switch (keyCode) {
            case KeyEvent.KEYCODE_POWER: {
                // ...
                if (down) {
                    // 按下Power会向Handler发送一个长按消息
                    interceptPowerKeyDown(event, interactive);
                } else {
                    // 
                    interceptPowerKeyUp(event, interactive, canceled);
                }
                break;
            }        
        }
    }

Power 事件不会传给 Activity来处理,而是由 PhoneWindowManager 自己处理,它会向 Handler 发送一个消息,最终通过 powerLongPress() 来处理。

    // frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
    
    private void powerLongPress() {
        final int behavior = getResolvedLongPressOnPowerBehavior();
        switch (behavior) {
            case LONG_PRESS_POWER_GLOBAL_ACTIONS:
                showGlobalActionsInternal();
                break;
        }
    }

    void showGlobalActionsInternal() {
        if (mGlobalActions == null) {
            // mWindowManagerFuncs 由 WindowManagerService 实现
            mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs);
        }
        final boolean keyguardShowing = isKeyguardShowingAndNotOccluded();
        mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());
    }

PhoneWindowManager 把处理长按 Power 的任务交给了 GlobalActions。

GlobalActions 会监听 SystemUI 的状态,从而决定长按 Power 键的处理流程。所以 GlobalActions 相当于一个策略类。

    // frameworks/base/services/core/java/com/android/server/policy/GlobalActions.java
    
    public void showDialog(boolean keyguardShowing, boolean deviceProvisioned) {
        // ...
        
        // GlobalActions在构造函数中会向StatusBarManagerService注册一个监听
        // 当SystemUI就绪后,mGlobalActionsAvailable的值就为true
        if (mGlobalActionsAvailable) {
            // mGlobalActionsProvider 是由StatusBarManagerService创建并保存的
            mGlobalActionsProvider.showGlobalActions();
        }
    }

经过 GlobalActions 的决策,它把这个任务又交给了 StatusBarManagerService 了。这一次终于和 SystemUI 搭上线了。

// frameworks/base/services/core/java/com/android/server/statusbar/StatusBarManagerService.java

    private final GlobalActionsProvider mGlobalActionsProvider = new GlobalActionsProvider() {
        @Override
        public void showGlobalActions() {
            if (mBar != null) {
                try {
                    // mBar是StatusBar注册的服务CommandQueue
                    mBar.showGlobalActionsMenu();
                } catch (RemoteException ex) {}
            }
        }
    }

StatusBarManagerService 是一个服务端,它通知了客户端 StatusBar。

在 StatusBar 启动的时候,它会向 StatusBarManagerService 注册一个服务 CommandQueue,也就是这里的 mBar 变量。

    // frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
    
    @Override
    public void showGlobalActionsMenu() {
        synchronized (mLock) {
            mHandler.removeMessages(MSG_SHOW_GLOBAL_ACTIONS);
            // 通过handleShowGlobalActionsMenu()回调给注册者
            mHandler.obtainMessage(MSG_SHOW_GLOBAL_ACTIONS).sendToTarget();
        }
    }

从前面几篇文章可以发现,SystemUI 的很多模块都向 CommandQueue 注册了回调。当 StatusBarManagerService 向 CommandQueue 回调后,它会通知注册回调的模块,这里它会回调 handleShowGlobalActionsMenu(),而它是由 SystemUI 的一个服务实现,这个服务就是 GlobalActionsComponent,也就是本文的主角。

    // frameworks/base/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
    
    @Override
    public void handleShowGlobalActionsMenu() {
        // 交给了GlobalActionsImpl
        mExtension.get().showGlobalActions(this);
    }

GlobalActionsComponent 一看名字就是代表 SystemUI 的一个模块,它会把任务交给 GlobalActionsImpl 来处理。

前面分析 VolumeUI 也可以发现,VolumeDialogComponent 代表 VolumeUI 模块,它把具体任务也是交给了 VolumeDialogImpl 处理。

    // frameworks/base/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
    
    @Override
    public void showGlobalActions(GlobalActionsManager manager) {
        if (mDisabled) return;
        if (mGlobalActions == null) {
            mGlobalActions = new GlobalActionsDialog(mContext, manager);
        }
        mGlobalActions.showDialog(mKeyguardMonitor.isShowing(),
                mDeviceProvisionedController.isDeviceProvisioned(),
                mPanelExtension.get());
    }

可以发现显示 Dialog UI 的操作,交给了 GlobalActionsDialog。

GlobalActionsDialog 严格来说,并不完全是 MVP 架构中的 Presenter 角色,它自己还处理了关机UI的显示。个人觉得应该把这个逻辑还是交给 GlobalActionsDialog,因为它是真正意义上的 View 层。

    // frameworks/base/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
    
    public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned,
            GlobalActionsPanelPlugin panelPlugin) {
        // ...
        
        if (mDialog != null) {

        } else {
            handleShow();
        }
    }

    private void handleShow() {
        awakenIfNecessary();
        // 创建Dialog
        mDialog = createDialog();
        prepareDialog();

        if (mAdapter.getCount() == 1
                && mAdapter.getItem(0) instanceof SinglePressAction
                && !(mAdapter.getItem(0) instanceof LongPressAction)) {
            // 如果只有一个选项,立即执行
            ((SinglePressAction) mAdapter.getItem(0)).onPress();
        } else {
            WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes();
            attrs.setTitle("ActionsDialog");
            attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
            mDialog.getWindow().setAttributes(attrs);
            // 如果有多个选项,就显示一个Dialog
            mDialog.show();
            // 通知WindowManagerService
            mWindowManagerFuncs.onGlobalActionsShown();
        }
    }

从这里就可以清晰的看到创建Dialog到显示的过程,细节就不深究了。