今天要说的是为Android设备添加重启、飞行模式、静音模式按钮,客户需求中需要添加这项功能,在长按电源键弹出的菜单中没有这些选项,谨以此文记录自己添加这个功能的过程。
首先找到长按电源键弹出的对话框,在frameworks\base\policy\src\com\android\internal\policy\impl\GlobalActions.java文件中,修改createDialog()方法。
1. /**
2. * Create the global actions dialog.
3. * @return A new dialog.
4. */
5. private AlertDialog createDialog() {
6. // Simple toggle style if there's no vibrator, otherwise use a tri-state
7. if (!mHasVibrator) {
8. new SilentModeToggleAction();
9. else {
10. new SilentModeTriStateAction(mContext, mAudioManager, mHandler);
11. }
12. new ToggleAction(
13. R.drawable.ic_lock_airplane_mode,
14. R.drawable.ic_lock_airplane_mode_off,
15. R.string.global_actions_toggle_airplane_mode,
16. R.string.global_actions_airplane_mode_on_status,
17. R.string.global_actions_airplane_mode_off_status) {
18.
19. void onToggle(boolean on) {
20. if (mHasTelephony && Boolean.parseBoolean(
21. SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE))) {
22. true;
23. // Launch ECM exit dialog
24. Intent ecmDialogIntent =
25. new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null);
26. ecmDialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
27. mContext.startActivity(ecmDialogIntent);
28. else {
29. changeAirplaneModeSystemSetting(on);
30. }
31. }
32.
33. @Override
34. protected void changeStateFromPress(boolean buttonOn) {
35. if (!mHasTelephony) return;
36.
37. // In ECM mode airplane state cannot be changed
38. if (!(Boolean.parseBoolean(
39. SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE)))) {
40. mState = buttonOn ? State.TurningOn : State.TurningOff;
41. mAirplaneState = mState;
42. }
43. }
44.
45. public boolean showDuringKeyguard() {
46. return true;
47. }
48.
49. public boolean showBeforeProvisioning() {
50. return false;
51. }
52. };
53. onAirplaneModeChanged();
54.
55. new ArrayList<Action>();
56.
57. // first: power off
58. mItems.add(
59. new SinglePressAction(
60. com.android.internal.R.drawable.ic_lock_power_off,
61. R.string.global_action_power_off) {
62.
63. public void onPress() {
64. // shutdown by making sure radio and power are handled accordingly.
65. mWindowManagerFuncs.shutdown();
66. }
67.
68. public boolean onLongPress() {
69. mWindowManagerFuncs.rebootSafeMode();
70. return true;
71. }
72.
73. public boolean showDuringKeyguard() {
74. return true;
75. }
76.
77. public boolean showBeforeProvisioning() {
78. return true;
79. }
80. });
81.
82. //edited by ouyang started
83. // next: reboot
84. mItems.add(
85. new SinglePressAction(
86. com.android.internal.R.drawable.ic_lock_power_off,
87. R.string.global_action_reboot) {
88.
89. public void onPress() {
90. //reboot
91. true);
92. }
93.
94. public boolean onLongPress() {
95. mWindowManagerFuncs.rebootSafeMode();
96. return true;
97. }
98.
99. public boolean showDuringKeyguard() {
100. return true;
101. }
102.
103. public boolean showBeforeProvisioning() {
104. return true;
105. }
106. });
107. //edited by ouyang ended
108.
109.
110. // next: airplane mode
111. mItems.add(mAirplaneModeOn);
112.
113. // last: silent mode
114. if (SHOW_SILENT_TOGGLE) {
115. mItems.add(mSilentModeAction);
116. }
117.
118. //edited by ouyang ended
119. List<UserInfo> users = mContext.getPackageManager().getUsers();
120. if (users.size() > 1) {
121. UserInfo currentUser;
122. try {
123. currentUser = ActivityManagerNative.getDefault().getCurrentUser();
124. catch (RemoteException re) {
125. null;
126. }
127. for (final UserInfo user : users) {
128. boolean isCurrentUser = currentUser == null
129. 0 : (currentUser.id == user.id);
130. new SinglePressAction(
131. com.android.internal.R.drawable.ic_menu_cc,
132. null ? : "Primary")
133. " \u2714" : "")) {
134. public void onPress() {
135. try {
136. ActivityManagerNative.getDefault().switchUser(user.id);
137. getWindowManager().lockNow();
138. catch (RemoteException re) {
139. "Couldn't switch user " + re);
140. }
141. }
142.
143. public boolean showDuringKeyguard() {
144. return true;
145. }
146.
147. public boolean showBeforeProvisioning() {
148. return false;
149. }
150. };
151. mItems.add(switchToUser);
152. }
153. }
154.
155. new MyAdapter();
156.
157. final AlertDialog.Builder ab = new AlertDialog.Builder(mContext);
158.
159. this)
160. true);
161.
162. final AlertDialog dialog = ab.create();
163. true);
164. true);
165. dialog.getListView().setOnItemLongClickListener(
166. new AdapterView.OnItemLongClickListener() {
167. @Override
168. public boolean onItemLongClick(AdapterView<?> parent, View view, int position,
169. long id) {
170. return mAdapter.getItem(position).onLongPress();
171. }
172. });
173. dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
174.
175. this);
176.
177. return dialog;
178. }
在GlobalActionsDialog方法可以看 mItems.add这个方法是添加菜单选项的,该菜单的添加的第一个选项就是关机选项。可以仿照关机的Item添加一个重启的选项,如上面的代码所示;这样就解决了在长按的电源键弹出的对话框中添加一个重启选项了。当然这仅仅是添加一个显示而已,接下来就为这个选项添加逻辑控制代码了。
在上面的代码中使用的mWindowManagerFuncs.reboot方法和R.string.global_action_reboot资源(资源的添加放到最后说),默认是不存在的,所以需要在自己手动添加。
2、首先在找到WindowManagerFuncs这个所在的位置,在frameworks\base\core\java\android\view\WindowManagerPolicy.java中
1. public interface WindowManagerFuncs {
2. public static final int LID_ABSENT = -1;
3. public static final int LID_CLOSED = 0;
4. public static final int LID_OPEN = 1;
5.
6. /**
7. * Ask the window manager to re-evaluate the system UI flags.
8. */
9. public void reevaluateStatusBarVisibility();
10.
11. /**
12. * Add a fake window to the window manager. This window sits
13. * at the top of the other windows and consumes events.
14. */
15. public FakeWindow addFakeWindow(Looper looper,
16. InputEventReceiver.Factory inputEventReceiverFactory,
17. int windowType, int layoutParamsFlags, boolean canReceiveKeys,
18. boolean hasFocus, boolean touchFullscreen);
19.
20. /**
21. * Returns a code that describes the current state of the lid switch.
22. */
23. public int getLidState();
24.
25. /**
26. * Creates an input channel that will receive all input from the input dispatcher.
27. */
28. public InputChannel monitorInput(String name);
29.
30. /**
31. * Switch the keyboard layout for the given device.
32. * Direction should be +1 or -1 to go to the next or previous keyboard layout.
33. */
34. public void switchKeyboardLayout(int deviceId, int direction);
35.
36. public void shutdown();
37. public void rebootSafeMode();
38. //edited by ouyang
39. public void reboot(boolean confirm);
40. }
添加reboot方法。但这只是添加接口而已,它的具体实现在呢?找了许久在frameworks\base\services\java\com\android\server\wm\windowManagerService.java中找到了这个接口的实现。
在该类中加入reboot()方法,该方法调用ShutdownThread的reboot方法
1. // Called by window manager policy. Not exposed externally.
2. @Override
3. public void shutdown() {
4. true);
5. }
6. //edited by ouyang start
7. public void reboot(boolean confirm){
8. hutdownThread.reboot(mContext,null,confirm);
9. }
10. //edited by ouyang end
同样在仿照关机的原理添加reboot的具体实现代码,既然在ShutdownThread这个类中提供了shutdown和rebootSafeMode的方法,那按理也应该有reboot的方法,或者类似reboot的方法。找到Shutdown.java文件,在frameworks\base\services\java\com\android\server\power\ShutdownThread.java中,
1. /**
2. * Request a clean shutdown, waiting for subsystems to clean up their
3. * state etc. Must be called from a Looper thread in which its UI
4. * is shown.
5. *
6. * @param context Context used to display the shutdown progress dialog.
7. * @param reason code to pass to the kernel (e.g. "recovery"), or null.
8. * @param confirm true if user confirmation is needed before shutting down.
9. */
10. public static void reboot(final Context context, String reason, boolean confirm) {
11. true;
12. false;
13. mRebootReason = reason;
14. shutdownInner(context, confirm);
15. }
其中提供了一个静态的reboot方法,所以在windowManagerService.java中的reboot实现中直接调用ShutdownThread中reboot即可。
public static void reboot(final Context context, String reason, boolean confirm);有三个参数,后两个参数解释如下: reason 如果值为是null,正常重启;如果是recovery,系统重启进入recovery mode ;confirm为true显示关机提示框,需要用户【确认】;false不显示提示框,直接关机。
到此重启功能基本上可以使用了(除资源还没有添加之外),但是此时选择重启选项时,其提示还是不够关机的提示,所以还要修改选择“重启”时的对话框的提示。
在frameworks\base\services\java\com\android\server\pm\ShutdownThread.java中
方法
1. static void shutdownInner(final Context context, boolean confirm) {
2. // ensure that only one thread is trying to power down.
3. // any additional calls are just returned
4. synchronized (sIsStartedGuard) {
5. if (sIsStarted) {
6. "Request to shutdown already running, returning.");
7. return;
8. }
9. }
10.
11. final int longPressBehavior = context.getResources().getInteger(
12. com.android.internal.R.integer.config_longPressOnPowerBehavior);
13. //edited by ouyang started
14. final int resourceId =mReboot
15. ?com.android.internal.R.string.reboot_confirm
16. :(mRebootSafeMode
17. ? com.android.internal.R.string.reboot_safemode_confirm
18. 2
19. ? com.android.internal.R.string.shutdown_confirm_question
20. : com.android.internal.R.string.shutdown_confirm));
21. //edited by ouyang ended
22.
23. /**
24. //resource code
25. final int resourceId = mRebootSafeMode
26. ? com.android.internal.R.string.reboot_safemode_confirm
27. : (longPressBehavior == 2
28. ? com.android.internal.R.string.shutdown_confirm_question
29. : com.android.internal.R.string.shutdown_confirm);
30. */
31. "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior);
32.
33. if (confirm) {
34. final CloseDialogReceiver closer = new CloseDialogReceiver(context);
35. final AlertDialog dialog = new AlertDialog.Builder(context)
36. /*
37. //source code
38. .setTitle(mRebootSafeMode
39. ? com.android.internal.R.string.reboot_safemode_title
40. : com.android.internal.R.string.power_off)
41. .setMessage(resourceId)
42. */
43. //edited by ouyang started
44. .setTitle(mReboot
45. ?com.android.internal.R.string.global_action_reboot
46. :(mRebootSafeMode
47. ? com.android.internal.R.string.reboot_safemode_title
48. : com.android.internal.R.string.power_off))
49. .setMessage(resourceId)
50. //edited by ouyang ended
51.
52. new DialogInterface.OnClickListener() {
53. public void onClick(DialogInterface dialog, int which) {
54. beginShutdownSequence(context);
55. }
56. })
57. null)
58. .create();
59. closer.dialog = dialog;
60. dialog.setOnDismissListener(closer);
61. dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
62. dialog.show();
63. else {
64. beginShutdownSequence(context);
65. }
66. }
至此关于代码部分的改动全部完成,接下就添加需要添加使用到的资源了,就是其中使用的字符串,为了简单起见就添加了英文和简体中文:
在对应的资源文件中添加:
frameworks\base\core\res\res\values\strings.xml
1. <!--edited by ouyang started-->
2. <string name="global_action_reboot">Reboot</string>
3. <string name="reboot_confirm">Do you want to reboot your device?</string>
4. <!--edited by ouyang started-->
frameworks\base\core\res\res\values-zh-rCN\ strings.xml
1. <!--edited by ouyang started-->
2. <string name="global_action_reboot">重启</string>
3. <string name="reboot_confirm">您要重新启动您的设备吗?</string>
4. <!--edited by ouyang started-->
现在已经添加了好这些资源,但是现在还不能使用,此时编译会出现找不到该资源的错误,还需要在frameworks\base\core\res\res\values\public.xml文件中进行资源声明:
1. <java-symbol type="string" name="global_action_reboot" />
2. <java-symbol type="string" name="reboot_confirm" />
最后还得确认 framework/base/core/res/res/values/config.xml文件中的config_longPressOnPowerBehavior属性变成1
最后重新编译即可