**
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.