Android 按键、关机界面的定制
在平常的项目中我们会有很多定制 按键和一些UI的需求,这里就以定制power键和关机界面为例来聊一下遇到这类需求要如何去开发。
1.按键的定制
首先要想定制按键需要知道Android系统的input系统的相关知识,知道按键事件的分发流程后就知道在那一步去实现我们的需求了。这里简单阐述一下Android的按键事件的分发流程。
当我们按下按键时,底层硬件就会发送消息给EventHub,接着input reader线程会移植循环读取EventHub的信息,当EventHub接收到底层的按键事件时,input reader就能接收到这个事件,接着input reader将这个事件加入到input dispatcher的分发队列中,接着唤醒input dispatcher线程去分发事件。但是在加入分发队列前会执行一个拦截动作,这个动作可以让系统在按键事件分发前对这个事件做一些特殊的处理,比如直接消费这个按键事件等等,使这个事件无法得到分发。
目前Android的按键事件分发前的拦截操作都在下面这个类中实现:
/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
相关的方法是interceptKeyBeforeQueueing
在这个interceptKeyBeforeQueueing 函数中会对各种按键做不同的操作,比如:Back键,Power键等等。这里以Power键为例:
/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
6020 @Override
6021 public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
6022 if (!mSystemBooted) {
6023 // If we have not yet booted, don't let key events do anything.
6024 return 0;
6025 }
...
// Handle special keys.
6108 switch (keyCode) {
...
6260 case KeyEvent.KEYCODE_POWER: {
6261 // Any activity on the power button stops the accessibility shortcut
6262 cancelPendingAccessibilityShortcutAction();
6263 result &= ~ACTION_PASS_TO_USER;
6264 isWakeKey = false; // wake-up will be handled separately
6265 if (down) {
6266 interceptPowerKeyDown(event, interactive);
6267 } else {
6268 interceptPowerKeyUp(event, interactive, canceled);
6269 }
6270 break;
6271 }
}
可以看到上面将 当power键被按下时调用interceptPowerKeyDown函数,抬起时调用interceptPowerKeyUp函数,下面分别来看这两个函数
1.1 Power键按下 interceptPowerKeyDown
/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
1318 private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {
1319 // Hold a wake lock until the power key is released.
...
1380
1381 // If the power key has still not yet been handled, then detect short
1382 // press, long press, or multi press and decide what to do.
1383 mPowerKeyHandled = hungUp || mScreenshotChordVolumeDownKeyTriggered
1384 || mA11yShortcutChordVolumeUpKeyTriggered || gesturedServiceIntercepted;
1385 if (!mPowerKeyHandled) {
1386 if (interactive) {
//如果当前是系统和屏幕是唤醒的状态
1387 // When interactive, we're already awake.
1388 // Wait for a long press or for the button to be released to decide what to do.
1389 if (hasLongPressOnPowerBehavior()) {
//如果定义了长按Power键的行为 这里一般走else分支
1390 if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
1391 powerLongPress();
1392 } else {
//这里发送一个延时500ms执行的消息
1393 Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);
1394 msg.setAsynchronous(true);
1395 mHandler.sendMessageDelayed(msg,
1396 ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
1397 //如果定义了超长power键的行为的话 同时也会发送一个3500ms的延时消息
1398 if (hasVeryLongPressOnPowerBehavior()) {
1399 Message longMsg = mHandler.obtainMessage(MSG_POWER_VERY_LONG_PRESS);
1400 longMsg.setAsynchronous(true);
//mVeryLongPressTimeout 默认为3500
1401 mHandler.sendMessageDelayed(longMsg, mVeryLongPressTimeout);
1402 }
1403 }
1404 }
1405 } else {
//如果当前的系统和屏幕是休眠的
//唤醒屏幕
1406 wakeUpFromPowerKey(event.getDownTime());
1407 //mSupportLongPressPowerWhenNonInteractive是定义是否需要在休眠状态下启用长按power键的功能 默认为false
1408 if (mSupportLongPressPowerWhenNonInteractive && hasLongPressOnPowerBehavior()) {
//和上面一样走else 分支 执行的操作也是一样的 发送延时消息
1409 if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
1410 powerLongPress();
1411 } else {
1412 Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);
1413 msg.setAsynchronous(true);
1414 mHandler.sendMessageDelayed(msg,
1415 ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
1416
1417 if (hasVeryLongPressOnPowerBehavior()) {
1418 Message longMsg = mHandler.obtainMessage(MSG_POWER_VERY_LONG_PRESS);
1419 longMsg.setAsynchronous(true);
1420 mHandler.sendMessageDelayed(longMsg, mVeryLongPressTimeout);
1421 }
1422 }
1423
1424 mBeganFromNonInteractive = true;
1425 } else {
1426 final int maxCount = getMaxMultiPressPowerCount();
1427
1428 if (maxCount <= 1) {
1429 mPowerKeyHandled = true;
1430 } else {
1431 mBeganFromNonInteractive = true;
1432 }
1433 }
1434 }
1435 }
1436 }
从上面代码可以看到当Power键按下的处理主要分为两种情况:
- 如果当前系统是唤醒的状态,那么并且定义了长按Power键的行为那么就直接发送延时消息
- 如果当前系统是休眠状态,那么就先唤醒系统和屏幕,接着根据mSupportLongPressPowerWhenNonInteractive来决定是否发送延时消息。
下面我们来看一下 Power键抬起的动作:
1.2 Power键抬起 interceptPowerKeyUp
1438 private void interceptPowerKeyUp(KeyEvent event, boolean interactive, boolean canceled) {
1439 final boolean handled = canceled || mPowerKeyHandled;
1440 mScreenshotChordPowerKeyTriggered = false;
1441 cancelPendingScreenshotChordAction();
//取消按下Power键时发送的延时消息
1442 cancelPendingPowerKeyAction();
1443
1444 if (!handled) {
1445 // Figure out how to handle the key now that it has been released.
//power键按下抬起的次数 +1
1446 mPowerKeyPressCounter += 1;
1447
1448 final int maxCount = getMaxMultiPressPowerCount();
1449 final long eventTime = event.getDownTime();
1450 if (mPowerKeyPressCounter < maxCount) {
1451 // This could be a multi-press. Wait a little bit longer to confirm.
1452 // Continue holding the wake lock.
1453 Message msg = mHandler.obtainMessage(MSG_POWER_DELAYED_PRESS,
1454 interactive ? 1 : 0, mPowerKeyPressCounter, eventTime);
1455 msg.setAsynchronous(true);
1456 mHandler.sendMessageDelayed(msg, ViewConfiguration.getMultiPressTimeout());
1457 return;
1458 }
1459
1460 // No other actions. Handle it immediately.
//处理短按Power键的流程
1461 powerPress(eventTime, interactive, mPowerKeyPressCounter);
1462 }
1463
1464 // Done. Reset our state.
1465 finishPowerKeyPress();
1466 }
1476 private void cancelPendingPowerKeyAction() {
1477 if (!mPowerKeyHandled) {
1478 mPowerKeyHandled = true;
1479 mHandler.removeMessages(MSG_POWER_LONG_PRESS);
1480 }
1481 if (hasVeryLongPressOnPowerBehavior()) {
1482 mHandler.removeMessages(MSG_POWER_VERY_LONG_PRESS);
1483 }
1484 }
从上面我们可以知道Power键的长按行为是通过handler发送延时message来实现的,主要有两个延时message
- MSG_POWER_LONG_PRESS 默认从按下Power键后的500ms后就执行 如果在500ms内power键抬起就会取消
- MSG_POWER_VERY_LONG_PRESS 默认从按下Power键后的3500ms后执行 如果在3500ms内power键抬起就会取消
接着看一下是这两个延时message如果执行会做些什么操作:
/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
854 private class PolicyHandler extends Handler {
855 @Override
856 public void handleMessage(Message msg) {
857 switch (msg.what) {
...
906 case MSG_POWER_LONG_PRESS:
907 powerLongPress();
908 break;
909 case MSG_POWER_VERY_LONG_PRESS:
910 powerVeryLongPress();
911 break;
...
}
可以看到分别调用了powerLongPress和powerVeryLongPress。
1.3 powerLongPress
/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
1605 private void powerLongPress() {
//长按Power键 500ms后的行为 behavior默认为1
1606 final int behavior = getResolvedLongPressOnPowerBehavior();
1607 switch (behavior) {
//1.什么也不做
1608 case LONG_PRESS_POWER_NOTHING:
1609 break;
//2.通用的行为,长按Power键弹出菜单对话框
1610 case LONG_PRESS_POWER_GLOBAL_ACTIONS:
1611 mPowerKeyHandled = true;
1612 performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
1613 showGlobalActionsInternal();
1614 break;
//3.走关机流程
1615 case LONG_PRESS_POWER_SHUT_OFF:
1616 case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM:
1617 mPowerKeyHandled = true;
1618 performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
1619 sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
1620 mWindowManagerFuncs.shutdown(behavior == LONG_PRESS_POWER_SHUT_OFF);
1621 break;
//4.跳转到语音帮助
1622 case LONG_PRESS_POWER_GO_TO_VOICE_ASSIST:
1623 mPowerKeyHandled = true;
1624 performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
1625 final boolean keyguardActive = mKeyguardDelegate == null
1626 ? false
1627 : mKeyguardDelegate.isShowing();
1628 if (!keyguardActive) {
1629 Intent intent = new Intent(Intent.ACTION_VOICE_ASSIST);
1630 if (mAllowStartActivityForLongPressOnPowerDuringSetup) {
1631 mContext.startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
1632 } else {
1633 startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
1634 }
1635 }
1636 break;
1637 }
1638 }
1.4 powerVeryLongPress
/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
1640 private void powerVeryLongPress() {
// 长按Power键超过3500ms
1641 switch (mVeryLongPressOnPowerBehavior) {
//1.什么也不做
1642 case VERY_LONG_PRESS_POWER_NOTHING:
1643 break;
//2.弹出通用的菜单框
1644 case VERY_LONG_PRESS_POWER_GLOBAL_ACTIONS:
1645 mPowerKeyHandled = true;
1646 performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
1647 showGlobalActionsInternal();
1648 break;
1649 }
1650 }
关于Power键长按的行为在系统中已经定义的差不多了,可以通过修改配置文件来实现相应的功能:
/frameworks/base/core/res/res/values/config.xml
731 <!-- If this is true, long press on power button will be available from the non-interactive state -->
732 <bool name="config_supportLongPressPowerWhenNonInteractive">false</bool>
977 <!-- Control the behavior when the user long presses the power button.
978 0 - Nothing
979 1 - Global actions menu
980 2 - Power off (with confirmation)
981 3 - Power off (without confirmation)
982 4 - Go to voice assist
983 -->
984 <integer name="config_longPressOnPowerBehavior">1</integer>
985
986 <!-- Control the behavior when the user long presses the power button for a long time.
987 0 - Nothing
988 1 - Global actions menu
989 -->
990 <integer name="config_veryLongPressOnPowerBehavior">0</integer>
1.5 powerPress
/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
1493 private void powerPress(long eventTime, boolean interactive, int count) {
//短按Power键的行为
1494 if (mScreenOnEarly && !mScreenOnFully) {
1495 Slog.i(TAG, "Suppressed redundant power key press while "
1496 + "already in the process of turning the screen on.");
1497 return;
1498 }
1499 Slog.d(TAG, "powerPress: eventTime=" + eventTime + " interactive=" + interactive
1500 + " count=" + count + " beganFromNonInteractive=" + mBeganFromNonInteractive +
1501 " mShortPressOnPowerBehavior=" + mShortPressOnPowerBehavior);
1502
1503 if (count == 2) {
//双击Power键
1504 powerMultiPressAction(eventTime, interactive, mDoublePressOnPowerBehavior);
1505 } else if (count == 3) {
//三击Power键
1506 powerMultiPressAction(eventTime, interactive, mTriplePressOnPowerBehavior);
1507 } else if (interactive && !mBeganFromNonInteractive) {
//短按Power键的行为
1508 switch (mShortPressOnPowerBehavior) {
//1.什么也不做
1509 case SHORT_PRESS_POWER_NOTHING:
1510 break;
//休眠
1511 case SHORT_PRESS_POWER_GO_TO_SLEEP:
1512 goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);
1513 break;
1514 case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP:
1515 goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,
1516 PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
1517 break;
//返回launcher并休眠
1518 case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME:
1519 goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,
1520 PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
1521 launchHomeFromHotKey();
1522 break;
//返回Launcher
1523 case SHORT_PRESS_POWER_GO_HOME:
1524 shortPressPowerGoHome();
1525 break;
//关闭输入法并休眠
1526 case SHORT_PRESS_POWER_CLOSE_IME_OR_GO_HOME: {
1527 if (mDismissImeOnBackKeyPressed) {
1528 if (mInputMethodManagerInternal == null) {
1529 mInputMethodManagerInternal =
1530 LocalServices.getService(InputMethodManagerInternal.class);
1531 }
1532 if (mInputMethodManagerInternal != null) {
1533 mInputMethodManagerInternal.hideCurrentInputMethod();
1534 }
1535 } else {
1536 shortPressPowerGoHome();
1537 }
1538 break;
1539 }
1540 }
1541 }
1542 }
短按Power键的配置文件:
/frameworks/base/core/res/res/values/config.xml
1002 <!-- Control the behavior when the user short presses the power button.
1003 0 - Nothing
1004 1 - Go to sleep (doze)
1005 2 - Really go to sleep (don't doze)
1006 3 - Really go to sleep and go home (don't doze)
1007 4 - Go to home
1008 5 - Dismiss IME if shown. Otherwise go to home
1009 -->
1010 <integer name="config_shortPressOnPowerBehavior">1</integer>
1011
如果你想要自定义短按Power键的行为也可以在上面新增。
2.关机界面的定制
此次是定制长按Power键弹出的菜单的内容,修改关机页面并且去掉其他的选项。
接上面的1.3小节powerLongPress,当定义config_longPressOnPowerBehavior为1时会调用showGlobalActionsInternal弹出菜单栏:关机,重启,截屏等等。
/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
1829 void showGlobalActionsInternal() {
1830 if (mGlobalActions == null) {
1831 mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs);
1832 }
1833 final boolean keyguardShowing = isKeyguardShowingAndNotOccluded();
1834 mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());
1835 if (keyguardShowing) {
1836 // since it took two seconds of long press to bring this up,
1837 // poke the wake lock so they have some time to see the dialog.
1838 mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
1839 }
1840 }
主要是新建一个GlobalActions接着调用showDialog
/frameworks/base/services/core/java/com/android/server/policy/GlobalActions.java
59 public void showDialog(boolean keyguardShowing, boolean deviceProvisioned) {
60 if (DEBUG) Slog.d(TAG, "showDialog " + keyguardShowing + " " + deviceProvisioned);
61 if (mGlobalActionsProvider != null && mGlobalActionsProvider.isGlobalActionsDisabled()) {
62 return;
63 }
64 mKeyguardShowing = keyguardShowing;
65 mDeviceProvisioned = deviceProvisioned;
66 mShowing = true;
//这里的mGlobalActionsAvailable和StatusBar有关如果直接去掉StatusBar那么会走下面的mLegacyGlobalActions
67 if (mGlobalActionsAvailable) {
68 mHandler.postDelayed(mShowTimeout, 5000);
69 mGlobalActionsProvider.showGlobalActions();
70 } else {
71 // SysUI isn't alive, show legacy menu.
72 ensureLegacyCreated();
73 mLegacyGlobalActions.showDialog(mKeyguardShowing, mDeviceProvisioned);
74 }
75 }
76
这里主要是调用mGlobalActionsProvider.showGlobalActions来弹出菜单选项框,这里的mGlobalActionsProvider是调用
mGlobalActionsProvider = LocalServices.getService(GlobalActionsProvider.class);获取到的,这个服务是在StatusBarManagerService中添加的如下:
/frameworks/base/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
411 private final GlobalActionsProvider mGlobalActionsProvider = new GlobalActionsProvider() {
412 @Override
413 public boolean isGlobalActionsDisabled() {
414 return (mDisabled2 & DISABLE2_GLOBAL_ACTIONS) != 0;
415 }
416
417 @Override
418 public void setGlobalActionsListener(GlobalActionsProvider.GlobalActionsListener listener) {
419 mGlobalActionListener = listener;
420 mGlobalActionListener.onGlobalActionsAvailableChanged(mBar != null);
421 }
422
423 @Override
424 public void showGlobalActions() {
425 if (mBar != null) {
426 try {
427 mBar.showGlobalActionsMenu();
428 } catch (RemoteException ex) {}
429 }
430 }
431 };
160 public StatusBarManagerService(Context context, WindowManagerService windowManager) {
161 mContext = context;
162 mWindowManager = windowManager;
163
164 LocalServices.addService(StatusBarManagerInternal.class, mInternalService);
165 LocalServices.addService(GlobalActionsProvider.class, mGlobalActionsProvider);
166 }
167
showGlobalActions则是接着调用了IStatusBar的showGlobalActionsMenu经过层层调用最终调用到了GlobalActionsImpl的 showGlobalActions
/frameworks/base/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
67 public void showGlobalActions(GlobalActionsManager manager) {
68 if (mDisabled) return;
69 if (mGlobalActions == null) {
70 mGlobalActions = new GlobalActionsDialog(mContext, manager);
71 }
72 mGlobalActions.showDialog(mKeyguardMonitor.isShowing(),
73 mDeviceProvisionedController.isDeviceProvisioned());
74 }
主要是新建一个GlobalActionsDialog接着调用showDialog来显示对话框
/frameworks/base/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
207 public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {
208 mKeyguardShowing = keyguardShowing;
209 mDeviceProvisioned = isDeviceProvisioned;
210 if (mDialog != null) {
211 mDialog.dismiss();
212 mDialog = null;
213 // Show delayed, so that the dismiss of the previous dialog completes
214 mHandler.sendEmptyMessage(MESSAGE_SHOW);
215 } else {
216 handleShow();
217 }
218 }
240 private void handleShow() {
241 awakenIfNecessary();
//创建对话框 我们可以在这里替换掉对话框来实现定制关机对话框的效果
242 mDialog = createDialog();
243 prepareDialog();
244 //如果只有一种选项的话那么就直接执行
245 // If we only have 1 item and it's a simple press action, just do this action.
246 if (mAdapter.getCount() == 1
247 && mAdapter.getItem(0) instanceof SinglePressAction
248 && !(mAdapter.getItem(0) instanceof LongPressAction)) {
249 ((SinglePressAction) mAdapter.getItem(0)).onPress();
250 } else {
//否则弹出对话框供用户选择
251 WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes();
252 attrs.setTitle("ActionsDialog");
253 attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
254 mDialog.getWindow().setAttributes(attrs);
255 mDialog.show();
256 mWindowManagerFuncs.onGlobalActionsShown();
257 }
258 }
259
如果想要定制关机对话框那么在上面的createDialog将系统的菜单选项给替换掉就可以了,接着如果用户点击确认关机那么会走关机流程,接着会调用
GlobalActionsImpl的showShutdownUi来显示关机动画。
/frameworks/base/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
76 @Override
77 public void showShutdownUi(boolean isReboot, String reason) {
78 GradientDrawable background = new GradientDrawable(mContext);
79 background.setAlpha((int) (SHUTDOWN_SCRIM_ALPHA * 255));
80 //创建一个dialog
81 Dialog d = new Dialog(mContext,
82 com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions);
83 // Window initialization
84 Window window = d.getWindow();
//设置关机dialog的window的属性
85 window.requestFeature(Window.FEATURE_NO_TITLE);
86 window.getAttributes().systemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
87 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
88 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
89 // Inflate the decor view, so the attributes below are not overwritten by the theme.
90 window.getDecorView();
91 window.getAttributes().width = ViewGroup.LayoutParams.MATCH_PARENT;
92 window.getAttributes().height = ViewGroup.LayoutParams.MATCH_PARENT;
93 window.getAttributes().layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
94 window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
95 window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
96 window.addFlags(
97 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
98 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
99 | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
100 | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
101 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
102 | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
103 window.setBackgroundDrawable(background);
104 window.setWindowAnimations(R.style.Animation_Toast);
105 //给关机dialog设置layout xml文件
106 d.setContentView(R.layout.shutdown_dialog);
107 d.setCancelable(false);
108
109 int color = Utils.getColorAttr(mContext, com.android.systemui.R.attr.wallpaperTextColor);
110 boolean onKeyguard = mContext.getSystemService(
111 KeyguardManager.class).isKeyguardLocked();
112
113 ProgressBar bar = d.findViewById(R.id.progress);
114 bar.getIndeterminateDrawable().setTint(color);
//关机信息
115 TextView message = d.findViewById(R.id.text1);
116 message.setTextColor(color);
117 if (isReboot) message.setText(R.string.reboot_to_reset_message);
118
119 Point displaySize = new Point();
120 mContext.getDisplay().getRealSize(displaySize);
121 GradientColors colors = Dependency.get(SysuiColorExtractor.class).getColors(
122 onKeyguard ? WallpaperManager.FLAG_LOCK : WallpaperManager.FLAG_SYSTEM);
123 background.setColors(colors, false);
124 background.setScreenSize(displaySize.x, displaySize.y);
125
126 d.show();
127 }
从上面可以知道关机动画也是一个dialog,当我们需要定制关机动画时就是替换上面代码的d.setContentView(R.layout.shutdown_dialog);将这部分代码的layout xml名字改成我们自己的即可。