今天要说的是为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方法。 但这只是添加接口而已,它的具体实现在呢?找了许久在 frameworks\base\services\java\com\android\server\wm\windowManagerService.java中找到了这个接口的实现。
在该类中加入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){ ShutdownThread.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-->frameworks\base\core\res\res\values-zh-rCN\ strings.xml
<!--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
最后重新编译即可
==================================================================================================
作者:欧阳鹏 欢迎转载,与人分享是进步的源泉!
转载请保留原文地址:http://blog.csdn.net/ouyang_peng
==================================================================================================