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键按下的处理主要分为两种情况:

  1. 如果当前系统是唤醒的状态,那么并且定义了长按Power键的行为那么就直接发送延时消息
  2. 如果当前系统是休眠状态,那么就先唤醒系统和屏幕,接着根据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

  1. MSG_POWER_LONG_PRESS 默认从按下Power键后的500ms后就执行 如果在500ms内power键抬起就会取消
  2. 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名字改成我们自己的即可。