Powerkey长按弹出关机界面

**

1.流程

**
上层按键的处理一般都在base\services\core\java\com\android\server\policy\PhoneWindowManager.java的interceptKeyBeforeQueueing进行拦截。下面是长按电源键时弹出关机界面的流程。
在这里插入图片描述
主要函数

private void powerLongPress() {
		//获取配置文件中config_longPressOnPowerBehavior值,一般都是默认为LONG_PRESS_POWER_GLOBAL_ACTIONS:
		//config_longPressOnPowerBehavior在文件./frameworks/base/core/res/res/values/config.xml中配置。
        final int behavior = getResolvedLongPressOnPowerBehavior();
        switch (behavior) {
        case LONG_PRESS_POWER_NOTHING:
            break;
        case LONG_PRESS_POWER_GLOBAL_ACTIONS:
            mPowerKeyHandled = true;
            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
            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;
        case LONG_PRESS_POWER_GO_TO_VOICE_ASSIST:
            mPowerKeyHandled = true;
            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
            final boolean keyguardActive = mKeyguardDelegate == null
                    ? false
                    : mKeyguardDelegate.isShowing();
            if (!keyguardActive) {
                Intent intent = new Intent(Intent.ACTION_VOICE_ASSIST);
                if (mAllowStartActivityForLongPressOnPowerDuringSetup) {
                    mContext.startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
                } else {
                    startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
                }
            }
            break;
        }
    }

在powerwindosmanager中最终会调用到base\services\core\java\com\android\server\policy\GlobalActions.java中的showDialog方法,主要实现如下


public void showDialog(boolean keyguardShowing, boolean deviceProvisioned) {
    if (DEBUG) Slog.d(TAG, "showDialog " + keyguardShowing + " " + deviceProvisioned);
    if (mGlobalActionsProvider != null && mGlobalActionsProvider.isGlobalActionsDisabled()) {
        return;
    }
    mKeyguardShowing = keyguardShowing;
    mDeviceProvisioned = deviceProvisioned;
    mShowing = true;
    //根据mGlobalActionsAvailable判断调用,这里分析else的流程
    //也就是mLegacyGlobalActions.showDialog
    if (mGlobalActionsAvailable) {
        //主要显示System ui的关机界面
        mHandler.postDelayed(mShowTimeout, 5000);
        mGlobalActionsProvider.showGlobalActions();
    } else {
        //框架层系统的显示界面
        // SysUI isn't alive, show legacy menu.
        ensureLegacyCreated();
        mLegacyGlobalActions.showDialog(mKeyguardShowing, mDeviceProvisioned);
    }
}

上面的流程最终会调用到base\services\core\java\com\android\server\policy\LegacyGlobalActions.java中的createDialog来进行创建Dialog的显示内容。这是重点,下面只显示本人认为的核心代码部分。

private ActionsDialog createDialog() {
 	.....

	//(1)这里从配置文件中的配置列表config_globalActionsList获取客户定制的dialog显示功能,
	//我们常见的Dialog界面主要包括关机,重启,锁屏功能等等
    mItems = new ArrayList<Action>();
    String[] defaultActions = mContext.getResources().getStringArray(
            com.android.internal.R.array.config_globalActionsList);

	//(2)然后根据配置的功能通过mItems.add添加到一个Item里面。
    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(mContext, mWindowManagerFuncs));
        } 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(mContext, mWindowManagerFuncs));
        } 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());
    }

   //(3)将包含界面信息的mItem放到一个adapter对象里面,
   //在用户点击对应控件的时候就会回调对应控件对象的onPress检测对应选项的按下进行对应的功能逻辑处理
    mAdapter = new ActionsAdapter(mContext, mItems,
            () -> mDeviceProvisioned, () -> mKeyguardShowing);

    ......

    return dialog; //返回创建好的dialog
}

如上:config_globalActionsList的配置在./frameworks/base/core/res/res/values/config.xml文件,下面主要包括关机,重启,锁屏等等。

<string-array translatable="false" name="config_globalActionsList">
    <!--关机-->
    <item>power</item> 
    <!--重启-->
    <item>restart</item>
    <!--锁屏-->
    <item>lockdown</item>
    <item>logout</item>
    <item>bugreport</item>
    <!--截屏-->
    <item>screenshot</item>
    <item>emergency</item>
</string-array>

上面mItems.add(new PowerAction(mContext, mWindowManagerFuncs));中把关机控件的逻辑主要通过PowerAction.java来实现,base\services\core\java\com\android\server\policy\PowerAction.java。主要实现接口是onPress,在点击用户点击关机控件的时候便会回调onPress接口

public final class PowerAction extends SinglePressAction implements LongPressAction {
    private final Context mContext;
    private final WindowManagerPolicy.WindowManagerFuncs mWindowManagerFuncs;

    public PowerAction(Context context,
            WindowManagerPolicy.WindowManagerFuncs windowManagerFuncs) {
        super(R.drawable.ic_lock_power_off, R.string.global_action_power_off);
        mContext = context;
        mWindowManagerFuncs = windowManagerFuncs;
    }

    @Override
    public boolean onLongPress() {
        UserManager um = mContext.getSystemService(UserManager.class);
        if (!um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
            mWindowManagerFuncs.rebootSafeMode(true);
            return true;
        }
        return false;
    }

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

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

    //关机Dialog界面中关机控件按下的逻辑
    @Override
    public void onPress() {
        // shutdown by making sure radio and power are handled accordingly.
        mWindowManagerFuncs.shutdown(false /* confirm */);//调用系统的关机api实现关机
    }
}

2.定制

从上面来看,我们一般可以对关机页面的功能进行删减,添加,以及控件界面的调整等。
(1)删减
只要在./frameworks/base/core/res/res/values/config.xml文件进行调整便好了,例如只保留关机跟重启功能。

     <string-array translatable="false" name="config_globalActionsList">
         <item>power</item>
         <item>restart</item>
-        <item>lockdown</item>
-        <item>logout</item>
-        <item>bugreport</item>
-        <item>screenshot</item>
-        <item>emergency</item>
     </string-array>

(2)调整界面显示
这个关机界面弹窗的ui可以修改文件
./frameworks/base/core/res/res/layout/global_actions_item.xml

<!-- RelativeLayouts have an issue enforcing minimum heights, so just
     work around this for now with LinearLayouts. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:minHeight="?android:attr/listPreferredItemHeight"
    android:orientation="horizontal"
    android:paddingStart="8dip"
    android:paddingEnd="16dip"
    android:paddingTop="6dip"
    android:paddingBottom="6dip"
    >
    <ImageView android:id="@+id/icon"
        android:layout_width="56dp"
        android:layout_height="56dp"
        android:layout_gravity="center"
        android:layout_marginEnd="8dip"
        android:scaleType="center"/>

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_marginEnd="8dip"
        >
        <TextView android:id="@+id/message"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="top|start"
            android:gravity="center_vertical|start"
            android:textAppearance="?android:attr/textAppearanceLarge"
            />
        <TextView android:id="@+id/status"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom|start"
            android:minHeight="26dp"
            android:textAppearance="?android:attr/textAppearanceSmall"
            />
    </LinearLayout>
</LinearLayout>

3.添加新的功能
我们可以自己添加定制化选项,例如添加一个demo log选项。
1.修改文件,添加dmeo log匹配项
文件:
frameworks/base/services/core/java/com/android/server/policy/LegacyGlobalActions.java

 import com.android.internal.widget.LockPatternUtils;
 import com.android.server.policy.PowerAction;
 import com.android.server.policy.RestartAction;
+import com.android.server.policy.LogAction;
 import com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs;

 import android.app.ActivityManager;
@@ -95,6 +96,7 @@ class LegacyGlobalActions implements DialogInterface.OnDismissListener, DialogIn
     private static final String GLOBAL_ACTION_KEY_VOICEASSIST = "voiceassist";
     private static final String GLOBAL_ACTION_KEY_ASSIST = "assist";
     private static final String GLOBAL_ACTION_KEY_RESTART = "restart";
+    private static final String GLOBAL_ACTION_LOG = "log";

     private final Context mContext;
     private final WindowManagerFuncs mWindowManagerFuncs;
@@ -304,7 +306,9 @@ class LegacyGlobalActions implements DialogInterface.OnDismissListener, DialogIn
                 mItems.add(getAssistAction());
             } else if (GLOBAL_ACTION_KEY_RESTART.equals(actionKey)) {
                 mItems.add(new RestartAction(mContext, mWindowManagerFuncs));
-            } else {
+            }else if (GLOBAL_ACTION_LOG.equals(actionKey)) {
+                mItems.add(new LogAction(mContext, mWindowManagerFuncs));
+            }else {
                 Log.e(TAG, "Invalid global action key " + actionKey);
             }
             // Add here so we don't add more than one.

2.添加选型功能实现,编写一个对象LogAction,继承自SinglePressAction,重写onPress。onPress就是按下控件时触发的逻辑。

package com.android.server.policy;

import android.content.Context;
import android.os.UserManager;
import com.android.internal.globalactions.LongPressAction;
import com.android.internal.globalactions.SinglePressAction;
import com.android.internal.R;
import com.android.server.policy.WindowManagerPolicy;
import android.util.Slog;

public final class LogAction extends SinglePressAction implements LongPressAction {
    private final Context mContext;
    private final WindowManagerPolicy.WindowManagerFuncs mWindowManagerFuncs;

    public LogAction(Context context,
            WindowManagerPolicy.WindowManagerFuncs windowManagerFuncs) {
           //R.drawable.ic_lock_power_off是关机画面的小图标,我们这里也用它
           //R.string.global_action_power_off是关机画面的标题,
           //这里为了方便也用关机,但是新加的选项一般要标题跟图标都要改变,
           //可以在res相关文件中重新添加一个,这里为了方便跟关机的内容一样,
           //只是按下的时候处理逻辑仅仅打印一个log。
        super(R.drawable.ic_lock_power_off, R.string.global_action_power_off);
        mContext = context;
        mWindowManagerFuncs = windowManagerFuncs;
    }

    @Override
    public boolean onLongPress() {
        Slog.e("demo","power onLongPress in log action.");
        return false;
    }

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

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

    @Override
    public void onPress() {
        //当控件按下的时候触发逻辑,仅仅只是打印一个log,表明已经控件按下了。
        Slog.e("demo","power onPress in log action.");
    }
}

3.在config文件中添加对应action,
frameworks/base/core/res/res/values/config.xml

     <string-array translatable="false" name="config_globalActionsList">
         <item>power</item>
         <item>restart</item>
+        <item>log</item>

4.编译烧录到板子上:
如图红色框中的部分,只有点击一下就会打印如下Log
(这里为了快速验证所以用了power off的图标以及标题)

05-20 11:19:31.909   453   470 E demo    : power onPress in log action.

在这里插入图片描述

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值