1. 程式人生 > >Android7.0&8.0 Power Menu 新增截圖選項

Android7.0&8.0 Power Menu 新增截圖選項

首先介紹下這個Power長按,顯示Power Menu的流程,然後再介紹如何去新增一個新的選項,新的選項的功能是截圖。

Power Menu長按的流程:
1.長按Power的處理在frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java

    private void powerLongPress() {
        final int behavior = getResolvedLongPressOnPowerBehavior();
        switch (behavior) {
        case
LONG_PRESS_POWER_NOTHING: break; case LONG_PRESS_POWER_GLOBAL_ACTIONS: mPowerKeyHandled = true; if (!performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false)) { performAuditoryFeedbackForAccessibilityIfNeed(); } showGlobalActionsInternal(); break
; case LONG_PRESS_POWER_SHUT_OFF: case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM: mPowerKeyHandled = true; performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false); sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS); mWindowManagerFuncs.shutdown(behavior == LONG_PRESS_POWER_SHUT_OFF); break
; } }

2.下面會走到showGlobalActionsInternal()

    void showGlobalActionsInternal() {
        sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
        if (mGlobalActions == null) {
            mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs);
        }
        final boolean keyguardShowing = isKeyguardShowingAndNotOccluded();
        mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());
        if (keyguardShowing) {
            // since it took two seconds of long press to bring this up,
            // poke the wake lock so they have some time to see the dialog.
            mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
        }
    }

3.showGlobalActionsInternal會去新建一個GlobalActions,然後去顯示Menu這個對話方塊。對話方塊顯示是在frameworks/base/services/core/core/java/com/android/server/policy/GlobalActions.java中的

    /**
     * Show the global actions dialog (creating if necessary)
     * @param keyguardShowing True if keyguard is showing
     */
    public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {
        mKeyguardShowing = keyguardShowing;
        mDeviceProvisioned = isDeviceProvisioned;
        if (mDialog != null) {
            mDialog.dismiss();
            mDialog = null;
            // Show delayed, so that the dismiss of the previous dialog completes
            mHandler.sendEmptyMessage(MESSAGE_SHOW);
        } else {
            handleShow();
        }
    }

4.然後會走handleShow(),handleShow()會去建立一個對話方塊。

    private void handleShow() {
        awakenIfNecessary();
        mDialog = createDialog();
        prepareDialog();

        // If we only have 1 item and it's a simple press action, just do this action.
        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("GlobalActions");
            mDialog.getWindow().setAttributes(attrs);
            mDialog.show();
            mDialog.getWindow().getDecorView().setSystemUiVisibility(View.STATUS_BAR_DISABLE_EXPAND);
        }
    }

5.最後就是這個createDialog(),它會去建立Power Menu對話方塊,對於新增新的選項,修改選項執行的內容等都可以在這做修改。

    /**
     * Create the global actions dialog.
     * @return A new dialog.
     */
    private GlobalActionsDialog createDialog() {
        // Simple toggle style if there's no vibrator, otherwise use a tri-state
        if (!mHasVibrator) {
            mSilentModeAction = new SilentModeToggleAction();
        } else {
            mSilentModeAction = new SilentModeTriStateAction(mContext, mAudioManager, mHandler);
        }
        mAirplaneModeOn = new ToggleAction(
                R.drawable.ic_lock_airplane_mode,
                R.drawable.ic_lock_airplane_mode_off,
                R.string.global_actions_toggle_airplane_mode,
                R.string.global_actions_airplane_mode_on_status,
                R.string.global_actions_airplane_mode_off_status) {

            void onToggle(boolean on) {
                if (mHasTelephony && Boolean.parseBoolean(
                        SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE))) {
                    mIsWaitingForEcmExit = true;
                    // Launch ECM exit dialog
                    Intent ecmDialogIntent =
                            new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null);
                    ecmDialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    mContext.startActivity(ecmDialogIntent);
                } else {
                    changeAirplaneModeSystemSetting(on);
                }
            }

            @Override
            protected void changeStateFromPress(boolean buttonOn) {
                if (!mHasTelephony) return;

                // In ECM mode airplane state cannot be changed
                if (!(Boolean.parseBoolean(
                        SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE)))) {
                    mState = buttonOn ? State.TurningOn : State.TurningOff;
                    mAirplaneState = mState;
                }
            }

            public boolean showDuringKeyguard() {
                return true;
            }

            public boolean showBeforeProvisioning() {
                return false;
            }
        };
        onAirplaneModeChanged();

        mItems = new ArrayList<Action>();
        String[] defaultActions = mContext.getResources().getStringArray(
                com.android.internal.R.array.config_globalActionsList);

        ArraySet<String> addedKeys = new ArraySet<String>();
        for (int i = 0; i < defaultActions.length; i++) {
            String actionKey = defaultActions[i];
            if (addedKeys.contains(actionKey)) {
                // If we already have added this, don't add it again.
                continue;
            }
            if (GLOBAL_ACTION_KEY_POWER.equals(actionKey)) {
                mItems.add(new PowerAction());
            } else if (GLOBAL_ACTION_KEY_AIRPLANE.equals(actionKey)) {
                mItems.add(mAirplaneModeOn);
            } else if (GLOBAL_ACTION_KEY_BUGREPORT.equals(actionKey)) {
                if (Settings.Global.getInt(mContext.getContentResolver(),
                        Settings.Global.BUGREPORT_IN_POWER_MENU, 0) != 0 && isCurrentUserOwner()) {
                    mItems.add(new BugReportAction());
                }
            } else if (GLOBAL_ACTION_KEY_SILENT.equals(actionKey)) {
                if (mShowSilentToggle) {
                    mItems.add(mSilentModeAction);
                }
            } else if (GLOBAL_ACTION_KEY_USERS.equals(actionKey)) {
                if (SystemProperties.getBoolean("fw.power_user_switcher", false)) {
                    addUsersToMenu(mItems);
                }
            } else if (GLOBAL_ACTION_KEY_SETTINGS.equals(actionKey)) {
                mItems.add(getSettingsAction());
            } else if (GLOBAL_ACTION_KEY_LOCKDOWN.equals(actionKey)) {
                mItems.add(getLockdownAction());
            } else if (GLOBAL_ACTION_KEY_VOICEASSIST.equals(actionKey)) {
                mItems.add(getVoiceAssistAction());
            } else if (GLOBAL_ACTION_KEY_ASSIST.equals(actionKey)) {
                mItems.add(getAssistAction());
            } else if (GLOBAL_ACTION_KEY_RESTART.equals(actionKey)) {
                mItems.add(new RestartAction());
            } else if(GLOBAL_ACTION_KEY_SCREENSHOT.equals(actionKey)) {
                mItems.add(new ScreenshotAction());
            }
            else {
                Log.e(TAG, "Invalid global action key " + actionKey);
            }
            // Add here so we don't add more than one.
            addedKeys.add(actionKey);
        }

        if (mEmergencyAffordanceManager.needsEmergencyAffordance()) {
            mItems.add(getEmergencyAction());
        }

        mAdapter = new MyAdapter();

        AlertParams params = new AlertParams(mContext);
        params.mAdapter = mAdapter;
        params.mOnClickListener = this;
        params.mForceInverseBackground = true;

        GlobalActionsDialog dialog = new GlobalActionsDialog(mContext, params);
        dialog.setCanceledOnTouchOutside(false); // Handled by the custom class.

        dialog.getListView().setItemsCanFocus(true);
        dialog.getListView().setLongClickable(true);
        dialog.getListView().setOnItemLongClickListener(
                new AdapterView.OnItemLongClickListener() {
                    @Override
                    public boolean onItemLongClick(AdapterView<?> parent, View view, int position,
                            long id) {
                        final Action action = mAdapter.getItem(position);
                        if (action instanceof LongPressAction) {
                            return ((LongPressAction) action).onLongPress();
                        }
                        return false;
                    }
        });
        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);

        dialog.setOnDismissListener(this);

        return dialog;
    }

1.在Power Menu新增新的選項
在frameworks/base/core/res/res/values/config.xml,中新增新的選項。注意:如果這裡添加了,最終沒有出現,可能被overlay了,搜尋config_globalActionsList找到真正的地方再修改。

    <!-- Defines the default set of global actions. Actions may still be disabled or hidden based
         on the current state of the device.
         Each item must be one of the following strings:
         "power" = Power off
         "settings" = An action to launch settings
         "airplane" = Airplane mode toggle
         "bugreport" = Take bug report, if available
         "silent" = silent mode
         "users" = list of users
         "restart" = restart device
         -->
    <string-array translatable="false" name="config_globalActionsList">
        <item>power</item>
        <item>restart</item>
        <item>screenshot</item>
        <item>bugreport</item>
        <item>users</item>
    </string-array>

frameworks/base/services/core/core/java/com/android/server/policy/GlobalActions.java 新增宣告,字串要和config中的一直。

    //[email protected] F0020
    private static final String GLOBAL_ACTION_KEY_SCREENSHOT = "screenshot"; 

然後載入這個item

        for (int i = 0; i < defaultActions.length; i++) {
            String actionKey = defaultActions[i];
            if (addedKeys.contains(actionKey)) {
                // If we already have added this, don't add it again.
                continue;
            }
            if (GLOBAL_ACTION_KEY_POWER.equals(actionKey)) {
                mItems.add(new PowerAction());
            } else if (GLOBAL_ACTION_KEY_AIRPLANE.equals(actionKey)) {
                mItems.add(mAirplaneModeOn);
            } else if (GLOBAL_ACTION_KEY_BUGREPORT.equals(actionKey)) {
                if (Settings.Global.getInt(mContext.getContentResolver(),
                        Settings.Global.BUGREPORT_IN_POWER_MENU, 0) != 0 && isCurrentUserOwner()) {
                    mItems.add(new BugReportAction());
                }
            } else if (GLOBAL_ACTION_KEY_SILENT.equals(actionKey)) {
                if (mShowSilentToggle) {
                    mItems.add(mSilentModeAction);
                }
            } else if (GLOBAL_ACTION_KEY_USERS.equals(actionKey)) {
                if (SystemProperties.getBoolean("fw.power_user_switcher", false)) {
                    addUsersToMenu(mItems);
                }
            } else if (GLOBAL_ACTION_KEY_SETTINGS.equals(actionKey)) {
                mItems.add(getSettingsAction());
            } else if (GLOBAL_ACTION_KEY_LOCKDOWN.equals(actionKey)) {
                mItems.add(getLockdownAction());
            } else if (GLOBAL_ACTION_KEY_VOICEASSIST.equals(actionKey)) {
                mItems.add(getVoiceAssistAction());
            } else if (GLOBAL_ACTION_KEY_ASSIST.equals(actionKey)) {
                mItems.add(getAssistAction());
            } else if (GLOBAL_ACTION_KEY_RESTART.equals(actionKey)) {
                mItems.add(new RestartAction());
            } else if(GLOBAL_ACTION_KEY_SCREENSHOT.equals(actionKey)) {
                mItems.add(new ScreenshotAction());
            }
            else {
                Log.e(TAG, "Invalid global action key " + actionKey);
            }
            // Add here so we don't add more than one.
            addedKeys.add(actionKey);
        }

下面要定義ScreenshotAction類,可以按照RestartAction寫。

    // [email protected] F0020
    private final class ScreenshotAction extends SinglePressAction implements LongPressAction {
        private ScreenshotAction() {
            super(R.drawable.ic_restart, R.string.global_action_screenshot);
        }

        @Override
        public boolean onLongPress() {
            return true;
        }

        @Override
        public boolean showDuringKeyguard() {
            return true;
        }

        @Override
        public boolean showBeforeProvisioning() {
            return true;
        }

        @Override
        public void onPress() {
            //實現截圖功能,後面會講解
            mPolicy.Screenshot();
        }
    }
    // end

注意:新增完字串global_action_screenshot,如果編譯失敗,記得在frameworks/base/core/res/res/values/symbols.xml中宣告一下。

  <java-symbol type="string" name="global_action_power_off" />
  <java-symbol type="string" name="global_action_restart" />
  <java-symbol type="string" name="global_action_screenshot" />

到這裡,長按power後就會出來我們新增的screenshot選項。
這裡寫圖片描述

2.實現截圖功能
這個Screenshot()是我自己寫的,定義在PhoneWindowManager.java中,因為那裡已經有實現截圖的功能,就是組合鍵截圖。Power+VolumeDown

mPolicy.Screenshot();

frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java 在這裡新增我們的截圖功能,因為PhoneWindowManager.java已經有實現了截圖功能,所以直接拿過來用就行。

    //[email protected] F0020
    public void Screenshot() {
        mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN);
        mHandler.post(mScreenshotRunnable);
    }
    //end

在建立GlobalActions的時候記得把PhoneWindowManager物件傳過去,因為GlobalActions那裡要呼叫PhoneWindowManager中的截圖方法。在GlobalActions那再定義一個建構函式,多個PhoneWindowManager引數,這樣也不影響其他地方使用GlobalActions。

void showGlobalActionsInternal() {
        sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
        if (mGlobalActions == null) {
            //[email protected] F0020
            mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs,this);
            //mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs);
        }
        final boolean keyguardShowing = isKeyguardShowingAndNotOccluded();
        mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());
        if (keyguardShowing) {
            // since it took two seconds of long press to bring this up,
            // poke the wake lock so they have some time to see the dialog.
            mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
        }
    }