今天要说的是为Android设备添加重启、飞行模式、静音模式按钮,客户需求中需要添加这项功能,在长按电源键弹出的菜单中没有这些选项,谨以此文记录自己添加这个功能的过程。
首先找到长按电源键弹出的对话框,在frameworks\base\policy\src\com\android\internal\policy\impl\GlobalActions.java文件中,修改createDialog()方法。
- /**
- * Create the global actions dialog.
- * @return A new dialog.
- */
- private AlertDialog createDialog() {
- // Simple toggle style if there's no vibrator, otherwise use a tri-state
- if (!mHasVibrator) {
- mSilentModeAction = new SilentModeToggleAction();
- } else {
- mSilentModeAction = new SilentModeTriStateAction(mContext, mAudioManager, mHandler);
- }
- mAirplaneModeOn = new ToggleAction(
- R.drawable.ic_lock_airplane_mode,
- R.drawable.ic_lock_airplane_mode_off,
- R.string.global_actions_toggle_airplane_mode,
- R.string.global_actions_airplane_mode_on_status,
- R.string.global_actions_airplane_mode_off_status) {
- void onToggle(boolean on) {
- if (mHasTelephony && Boolean.parseBoolean(
- SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE))) {
- mIsWaitingForEcmExit = true;
- // Launch ECM exit dialog
- Intent ecmDialogIntent =
- new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null);
- ecmDialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- mContext.startActivity(ecmDialogIntent);
- } else {
- changeAirplaneModeSystemSetting(on);
- }
- }
- @Override
- protected void changeStateFromPress(boolean buttonOn) {
- if (!mHasTelephony) return;
- // In ECM mode airplane state cannot be changed
- if (!(Boolean.parseBoolean(
- SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE)))) {
- mState = buttonOn ? State.TurningOn : State.TurningOff;
- mAirplaneState = mState;
- }
- }
- public boolean showDuringKeyguard() {
- return true;
- }
- public boolean showBeforeProvisioning() {
- return false;
- }
- };
- onAirplaneModeChanged();
- mItems = new ArrayList<Action>();
- // first: power off
- mItems.add(
- new SinglePressAction(
- com.android.internal.R.drawable.ic_lock_power_off,
- R.string.global_action_power_off) {
- public void onPress() {
- // shutdown by making sure radio and power are handled accordingly.
- mWindowManagerFuncs.shutdown();
- }
- public boolean onLongPress() {
- mWindowManagerFuncs.rebootSafeMode();
- return true;
- }
- public boolean showDuringKeyguard() {
- return true;
- }
- public boolean showBeforeProvisioning() {
- return true;
- }
- });
- //edited by ouyang started
- // next: reboot
- mItems.add(
- new SinglePressAction(
- com.android.internal.R.drawable.ic_lock_power_off,
- R.string.global_action_reboot) {
- public void onPress() {
- //reboot
- mWindowManagerFuncs.reboot(true);
- }
- public boolean onLongPress() {
- mWindowManagerFuncs.rebootSafeMode();
- return true;
- }
- public boolean showDuringKeyguard() {
- return true;
- }
- public boolean showBeforeProvisioning() {
- return true;
- }
- });
- //edited by ouyang ended
- // next: airplane mode
- mItems.add(mAirplaneModeOn);
- // last: silent mode
- if (SHOW_SILENT_TOGGLE) {
- mItems.add(mSilentModeAction);
- }
- //edited by ouyang ended
- List<UserInfo> users = mContext.getPackageManager().getUsers();
- if (users.size() > 1) {
- UserInfo currentUser;
- try {
- currentUser = ActivityManagerNative.getDefault().getCurrentUser();
- } catch (RemoteException re) {
- currentUser = null;
- }
- for (final UserInfo user : users) {
- boolean isCurrentUser = currentUser == null
- ? user.id == 0 : (currentUser.id == user.id);
- SinglePressAction switchToUser = new SinglePressAction(
- com.android.internal.R.drawable.ic_menu_cc,
- (user.name != null ? user.name : "Primary")
- + (isCurrentUser ? " \u2714" : "")) {
- public void onPress() {
- try {
- ActivityManagerNative.getDefault().switchUser(user.id);
- getWindowManager().lockNow();
- } catch (RemoteException re) {
- Log.e(TAG, "Couldn't switch user " + re);
- }
- }
- public boolean showDuringKeyguard() {
- return true;
- }
- public boolean showBeforeProvisioning() {
- return false;
- }
- };
- mItems.add(switchToUser);
- }
- }
- mAdapter = new MyAdapter();
- final AlertDialog.Builder ab = new AlertDialog.Builder(mContext);
- ab.setAdapter(mAdapter, this)
- .setInverseBackgroundForced(true);
- final AlertDialog dialog = ab.create();
- dialog.getListView().setItemsCanFocus(true);
- dialog.getListView().setLongClickable(true);
- dialog.getListView().setOnItemLongClickListener(
- new AdapterView.OnItemLongClickListener() {
- @Override
- public boolean onItemLongClick(AdapterView<?> parent, View view, int position,
- long id) {
- return mAdapter.getItem(position).onLongPress();
- }
- });
- dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
- dialog.setOnDismissListener(this);
- return dialog;
- }
在GlobalActionsDialog方法可以看 mItems.add这个方法是添加菜单选项的,该菜单的添加的第一个选项就是关机选项。可以仿照关机的Item添加一个重启的选项,如上面的代码所示;这样就解决了在长按的电源键弹出的对话框中添加一个重启选项了。当然这仅仅是添加一个显示而已,接下来就为这个选项添加逻辑控制代码了。
在上面的代码中使用的mWindowManagerFuncs.reboot方法和R.string.global_action_reboot资源(资源的添加放到最后说),默认是不存在的,所以需要在自己手动添加。
2、首先在找到WindowManagerFuncs这个所在的位置,在frameworks\base\core\java\android\view\WindowManagerPolicy.java中
- public interface WindowManagerFuncs {
- public static final int LID_ABSENT = -1;
- public static final int LID_CLOSED = 0;
- public static final int LID_OPEN = 1;
- /**
- * Ask the window manager to re-evaluate the system UI flags.
- */
- public void reevaluateStatusBarVisibility();
- /**
- * Add a fake window to the window manager. This window sits
- * at the top of the other windows and consumes events.
- */
- public FakeWindow addFakeWindow(Looper looper,
- InputEventReceiver.Factory inputEventReceiverFactory,
- String name, int windowType, int layoutParamsFlags, boolean canReceiveKeys,
- boolean hasFocus, boolean touchFullscreen);
- /**
- * Returns a code that describes the current state of the lid switch.
- */
- public int getLidState();
- /**
- * Creates an input channel that will receive all input from the input dispatcher.
- */
- public InputChannel monitorInput(String name);
- /**
- * Switch the keyboard layout for the given device.
- * Direction should be +1 or -1 to go to the next or previous keyboard layout.
- */
- public void switchKeyboardLayout(int deviceId, int direction);
- public void shutdown();
- public void rebootSafeMode();
- //edited by ouyang
- public void reboot(boolean confirm);
- }
在该类中加入reboot()方法,该方法调用ShutdownThread的reboot方法
- // Called by window manager policy. Not exposed externally.
- @Override
- public void shutdown() {
- ShutdownThread.shutdown(mContext, true);
- }
- //edited by ouyang start
- public void reboot(boolean confirm){
- hutdownThread.reboot(mContext,null,confirm);
- }
- //edited by ouyang end
同样在仿照关机的原理添加reboot的具体实现代码,既然在ShutdownThread这个类中提供了shutdown和rebootSafeMode的方法,那按理也应该有reboot的方法,或者类似reboot的方法。找到Shutdown.java文件,在frameworks\base\services\java\com\android\server\power\ShutdownThread.java中,
- /**
- * Request a clean shutdown, waiting for subsystems to clean up their
- * state etc. Must be called from a Looper thread in which its UI
- * is shown.
- *
- * @param context Context used to display the shutdown progress dialog.
- * @param reason code to pass to the kernel (e.g. "recovery"), or null.
- * @param confirm true if user confirmation is needed before shutting down.
- */
- public static void reboot(final Context context, String reason, boolean confirm) {
- mReboot = true;
- mRebootSafeMode = false;
- mRebootReason = reason;
- shutdownInner(context, confirm);
- }
其中提供了一个静态的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中
修改shutdownInner方法
- static void shutdownInner(final Context context, boolean confirm) {
- // ensure that only one thread is trying to power down.
- // any additional calls are just returned
- synchronized (sIsStartedGuard) {
- if (sIsStarted) {
- Log.d(TAG, "Request to shutdown already running, returning.");
- return;
- }
- }
- final int longPressBehavior = context.getResources().getInteger(
- com.android.internal.R.integer.config_longPressOnPowerBehavior);
- //edited by ouyang started
- final int resourceId =mReboot
- ?com.android.internal.R.string.reboot_confirm
- :(mRebootSafeMode
- ? com.android.internal.R.string.reboot_safemode_confirm
- : (longPressBehavior == 2
- ? com.android.internal.R.string.shutdown_confirm_question
- : com.android.internal.R.string.shutdown_confirm));
- //edited by ouyang ended
- /**
- //resource code
- final int resourceId = mRebootSafeMode
- ? com.android.internal.R.string.reboot_safemode_confirm
- : (longPressBehavior == 2
- ? com.android.internal.R.string.shutdown_confirm_question
- : com.android.internal.R.string.shutdown_confirm);
- */
- Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior);
- if (confirm) {
- final CloseDialogReceiver closer = new CloseDialogReceiver(context);
- final AlertDialog dialog = new AlertDialog.Builder(context)
- /*
- //source code
- .setTitle(mRebootSafeMode
- ? com.android.internal.R.string.reboot_safemode_title
- : com.android.internal.R.string.power_off)
- .setMessage(resourceId)
- */
- //edited by ouyang started
- .setTitle(mReboot
- ?com.android.internal.R.string.global_action_reboot
- :(mRebootSafeMode
- ? com.android.internal.R.string.reboot_safemode_title
- : com.android.internal.R.string.power_off))
- .setMessage(resourceId)
- //edited by ouyang ended
- .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- beginShutdownSequence(context);
- }
- })
- .setNegativeButton(com.android.internal.R.string.no, null)
- .create();
- closer.dialog = dialog;
- dialog.setOnDismissListener(closer);
- dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- dialog.show();
- } else {
- beginShutdownSequence(context);
- }
- }
至此关于代码部分的改动全部完成,接下就添加需要添加使用到的资源了,就是其中使用的字符串,为了简单起见就添加了英文和简体中文:
在对应的资源文件中添加:
frameworks\base\core\res\res\values\strings.xml
- <!--edited by ouyang started-->
- <string name="global_action_reboot">Reboot</string>
- <string name="reboot_confirm">Do you want to reboot your device?</string>
- <!--edited by ouyang started-->
- <!--edited by ouyang started-->
- <string name="global_action_reboot">重启</string>
- <string name="reboot_confirm">您要重新启动您的设备吗?</string>
- <!--edited by ouyang started-->
现在已经添加了好这些资源,但是现在还不能使用,此时编译会出现找不到该资源的错误,还需要在frameworks\base\core\res\res\values\public.xml文件中进行资源声明:
- <java-symbol type="string" name="global_action_reboot" />
- <java-symbol type="string" name="reboot_confirm" />
最后还得确认 framework/base/core/res/res/values/config.xml文件中的config_longPressOnPowerBehavior属性变成1
最后重新编译即可