今天要说的是为Android设备添加重启、飞行模式、静音模式按钮,客户需求中需要添加这项功能,在长按电源键弹出的菜单中没有这些选项,谨以此文记录自己添加这个功能的过程。

首先找到长按电源键弹出的对话框,在frameworks\base\policy\src\com\android\internal\policy\impl\GlobalActions.java文件中,修改createDialog()方法。

android 代码设置设备静音 android设置静音模式_Boo

android 代码设置设备静音 android设置静音模式_Boo_02

android 代码设置设备静音 android设置静音模式_android_03





    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中


    android 代码设置设备静音 android设置静音模式_Boo_04


    android 代码设置设备静音 android设置静音模式_android 代码设置设备静音_05



    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中找到了这个接口的实现。


    android 代码设置设备静音 android设置静音模式_android_06






    android 代码设置设备静音 android设置静音模式_Boo_07


    在该类中加入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中,


    android 代码设置设备静音 android设置静音模式_android_08




    android 代码设置设备静音 android设置静音模式_android_09


    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中


    方法


    android 代码设置设备静音 android设置静音模式_java_10



    android 代码设置设备静音 android设置静音模式_java_11



    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


    android 代码设置设备静音 android设置静音模式_java_12



    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" />


    android 代码设置设备静音 android设置静音模式_android 代码设置设备静音_13


    最后还得确认 framework/base/core/res/res/values/config.xml文件中的config_longPressOnPowerBehavior属性变成1


    android 代码设置设备静音 android设置静音模式_Boo_14




    android 代码设置设备静音 android设置静音模式_java_15




    最后重新编译即可