平台
rk3288 + Android 7.1
需求说明
1. 导航栏增加电源按键, 点击后打开关机菜单 2. 关机菜单项增加休眠项, 点击后进休眠
相关文件
//修改 modified: frameworks/base/core/java/android/view/WindowManagerPolicy.java modified: frameworks/base/core/res/res/values/config.xml modified: frameworks/base/core/res/res/values/strings.xml modified: frameworks/base/core/res/res/values/symbols.xml modified: frameworks/base/packages/SystemUI/res/values-sw600dp/config.xml modified: frameworks/base/packages/SystemUI/res/values-sw900dp/config.xml modified: frameworks/base/packages/SystemUI/res/values/config.xml modified: frameworks/base/packages/SystemUI/res/values/strings.xml modified: frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java modified: frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java modified: frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java modified: frameworks/base/services/core/java/com/android/server/policy/GlobalActions.java modified: frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java modified: frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java //新增 frameworks/base/core/res/res/drawable/ic_sleep.xml frameworks/base/packages/SystemUI/res/drawable-nodpi/ic_sysbar_power.png frameworks/base/packages/SystemUI/res/layout/power.xml
实现
导航栏增加按键
准备好对应的图标和相关的字符:
frameworks/base/packages/SystemUI/res/drawable-nodpi/ic_sysbar_power.png
|-- frameworks/base/packages/SystemUI/res/values/strings.xml
<string name="accessibility_power" translatable="false">Power</string>
增加按键layout.
|-- frameworks/base/packages/SystemUI/res/layout/power.xml
<com.android.systemui.statusbar.policy.KeyButtonView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res-auto" android:id="@+id/power" android:layout_width="@dimen/navigation_key_width" android:layout_height="match_parent" android:layout_weight="0" android:src="@drawable/ic_sysbar_power" systemui:keyCode="0" android:scaleType="center" android:contentDescription="@string/accessibility_power" android:paddingStart="@dimen/navigation_key_padding" android:paddingEnd="@dimen/navigation_key_padding" />
其中, keyCode是按键值, 若不想处理为按键, 则置为0
|-- frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
public static final String POWER = "power"; //加载按键 @Override protected void onFinishInflate() { super.onFinishInflate(); inflateChildren(); clearViews(); inflateLayout(getDefaultLayout()); } //默认按键 protected String getDefaultLayout() { return mContext.getString(R.string.config_navBarLayout); } protected void inflateLayout(String newLayout) { mCurrentLayout = newLayout; if (newLayout == null) { newLayout = getDefaultLayout(); } String[] sets = newLayout.split(GRAVITY_SEPARATOR, 3); String[] start = sets[0].split(BUTTON_SEPARATOR); String[] center = sets[1].split(BUTTON_SEPARATOR); String[] end = sets[2].split(BUTTON_SEPARATOR); // Inflate these in start to end order or accessibility traversal will be messed up. inflateButtons(start, (ViewGroup) mRot0.findViewById(R.id.ends_group), isRot0Landscape); inflateButtons(start, (ViewGroup) mRot90.findViewById(R.id.ends_group), !isRot0Landscape); LinearLayout centerLayout = (LinearLayout)mRot0.findViewById(R.id.center_group); mIsReverseInflateRot0 = !isRot0Landscape && !isSw600Dp() && mCurrentOrientation == Configuration.ORIENTATION_LANDSCAPE; if(mIsReverseInflateRot0) centerLayout.setOrientation(LinearLayout.VERTICAL); inflateButtons(center, centerLayout, isRot0Landscape); mIsReverseInflateRot0 = false; centerLayout = (LinearLayout)mRot90.findViewById(R.id.center_group); mIsReverseInflateRot90 = !isRot0Landscape && !isSw600Dp() && mCurrentOrientation == Configuration.ORIENTATION_PORTRAIT; if(mIsReverseInflateRot90) centerLayout.setOrientation(LinearLayout.HORIZONTAL); inflateButtons(center, centerLayout, !isRot0Landscape); mIsReverseInflateRot90 = false; addGravitySpacer((LinearLayout) mRot0.findViewById(R.id.ends_group)); addGravitySpacer((LinearLayout) mRot90.findViewById(R.id.ends_group)); inflateButtons(end, (ViewGroup) mRot0.findViewById(R.id.ends_group), isRot0Landscape); inflateButtons(end, (ViewGroup) mRot90.findViewById(R.id.ends_group), !isRot0Landscape); } //加入导航栏 @Nullable protected View inflateButton(String buttonSpec, ViewGroup parent, boolean landscape) { LayoutInflater inflater = landscape ? mLandscapeInflater : mLayoutInflater; float size = extractSize(buttonSpec); String button = extractButton(buttonSpec); View v = null; if (HOME.equals(button)) { v = inflater.inflate(R.layout.home, parent, false); if (landscape && isSw600Dp()) { setupLandButton(v); } } else if (BACK.equals(button)) { v = inflater.inflate(R.layout.back, parent, false); if (landscape && isSw600Dp()) { setupLandButton(v); } } else if (RECENT.equals(button)) { v = inflater.inflate(R.layout.recent_apps, parent, false); if (landscape && isSw600Dp()) { setupLandButton(v); } } else if (SCREENSHOT.equals(button)) { v = inflater.inflate(R.layout.screenshot, parent, false); if (landscape && isSw600Dp()) { setupLandButton(v); } //下面增加POWER按键, layout.power.xml也是在这里使用 }else if (POWER.equals(button)) { v = inflater.inflate(R.layout.power, parent, false); if (landscape && isSw600Dp()) { setupLandButton(v); } } else if (VOLUME_ADD.equals(button)) { v = inflater.inflate(R.layout.volume_add, parent, false); if (landscape && isSw600Dp()) { setupLandButton(v); } } else if (VOLUME_SUB.equals(button)) { v = inflater.inflate(R.layout.volume_sub, parent, false); if (landscape && isSw600Dp()) { setupLandButton(v); } } else if (MENU_IME.equals(button)) { v = inflater.inflate(R.layout.menu_ime, parent, false); } else if (NAVSPACE.equals(button)) { v = inflater.inflate(R.layout.nav_key_space, parent, false); } else if (CLIPBOARD.equals(button)) { v = inflater.inflate(R.layout.clipboard, parent, false); } else if (button.startsWith(KEY)) { String uri = extractImage(button); int code = extractKeycode(button); v = inflater.inflate(R.layout.custom_key, parent, false); ((KeyButtonView) v).setCode(code); if (uri != null) { ((KeyButtonView) v).loadAsync(uri); } } else { return null; } if (size != 0) { ViewGroup.LayoutParams params = v.getLayoutParams(); params.width = (int) (params.width * size); } if(mIsReverseInflateRot0 || mIsReverseInflateRot90){ setupVerticalButton(v); } parent.addView(v); addToDispatchers(v); View lastView = landscape ? mLastLandscape : mLastPortrait; if (lastView != null) { v.setAccessibilityTraversalAfter(lastView.getId()); } if (landscape) { mLastLandscape = v; } else { mLastPortrait = v; } return v; }
默认按键布局config_navBarLayout定义:
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java: return mContext.getString(R.string.config_navBarLayout); frameworks/base/packages/SystemUI/src/com/android/systemui/tuner/NavBarTuner.java: navLayout = context.getString(R.string.config_navBarLayout); frameworks/base/packages/SystemUI/res/values/config.xml: <string name="config_navBarLayout" translatable="false">space;volume_sub,back,home,recent,volume_add,screenshot;menu_ime</string> frameworks/base/packages/SystemUI/res/values-sw600dp/config.xml: <string name="config_navBarLayout" translatable="false">space;volume_sub,back,home,recent,volume_add,screenshot;menu_ime</string> frameworks/base/packages/SystemUI/res/values-sw900dp/config.xml: <string name="config_navBarLayout" translatable="false">space;volume_sub,back,home,recent,volume_add,screenshot;menu_ime</string>
增加power的个改:
diff --git a/frameworks/base/packages/SystemUI/res/values-sw600dp/config.xml b/frameworks/base/packages/SystemUI/res/values-sw600dp/config.xml old mode 100644 new mode 100755 index aa03ab2..ef41fee --- a/frameworks/base/packages/SystemUI/res/values-sw600dp/config.xml +++ b/frameworks/base/packages/SystemUI/res/values-sw600dp/config.xml @@ -34,7 +34,7 @@ <bool name="config_keyguardUserSwitcher">true</bool> <!-- Nav bar button default ordering/layout --> - <string name="config_navBarLayout" translatable="false">space;volume_sub,back,home,recent,volume_add,screenshot;menu_ime</string> + <string name="config_navBarLayout" translatable="false">space;volume_sub,back,home,recent,volume_add,screenshot,power;menu_ime</string> <!-- Animation duration when using long press on recents to dock --> <integer name="long_press_dock_anim_duration">290</integer> diff --git a/frameworks/base/packages/SystemUI/res/values-sw900dp/config.xml b/frameworks/base/packages/SystemUI/res/values-sw900dp/config.xml old mode 100644 new mode 100755 index 016f7e5..c992349 --- a/frameworks/base/packages/SystemUI/res/values-sw900dp/config.xml +++ b/frameworks/base/packages/SystemUI/res/values-sw900dp/config.xml @@ -19,6 +19,6 @@ <resources> <!-- Nav bar button default ordering/layout --> - <string name="config_navBarLayout" translatable="false">space;volume_sub,back,home,recent,volume_add,screenshot;menu_ime</string> + <string name="config_navBarLayout" translatable="false">space;volume_sub,back,home,recent,volume_add,screenshot,power;menu_ime</string> </resources> diff --git a/frameworks/base/packages/SystemUI/res/values/config.xml b/frameworks/base/packages/SystemUI/res/values/config.xml old mode 100644 new mode 100755 index da5f4bf..59e7161 --- a/frameworks/base/packages/SystemUI/res/values/config.xml +++ b/frameworks/base/packages/SystemUI/res/values/config.xml @@ -280,7 +280,7 @@ <string name="config_systemUIFactoryComponent" translatable="false">com.android.systemui.SystemUIFactory</string> <!-- Nav bar button default ordering/layout --> - <string name="config_navBarLayout" translatable="false">space;volume_sub,back,home,recent,volume_add,screenshot;menu_ime</string> + <string name="config_navBarLayout" translatable="false">space;volume_sub,back,home,recent,volume_add,screenshot,power;menu_ime</string> <bool name="quick_settings_show_full_alarm">false</bool>
添加触摸事件设置按键可见:
|-- frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
//点击处理: private View.OnClickListener mPowerClickListener = new View.OnClickListener(){ public void onClick(View v){ Intent intent = new Intent("android.intent.action.POWER_MENU"); mContext.sendBroadcast(intent); } }; private void prepareNavigationBarView() { mNavigationBarView.reorient(); ButtonDispatcher recentsButton = mNavigationBarView.getRecentsButton(); recentsButton.setOnClickListener(mRecentsClickListener); recentsButton.setOnTouchListener(mRecentsPreloadOnTouchListener); recentsButton.setLongClickable(true); recentsButton.setOnLongClickListener(this::handleLongPressBackRecents); ButtonDispatcher backButton = mNavigationBarView.getBackButton(); backButton.setLongClickable(true); backButton.setOnLongClickListener(this::handleLongPressBackRecents); ButtonDispatcher homeButton = mNavigationBarView.getHomeButton(); homeButton.setOnTouchListener(mHomeActionListener); homeButton.setOnLongClickListener(mLongPressHomeListener); ButtonDispatcher screenshotButton=mNavigationBarView.getScreenshotButton(); screenshotButton.setOnClickListener(mScreenshotClickListener); screenshotButton.setOnTouchListener(mScreenshotTouchListener); screenshotButton.setVisibility(View.VISIBLE); boolean isShow=Settings.System.getInt(mContext.getContentResolver(), Settings.System.SCREENSHOT_BUTTON_SHOW, 1)==1; if(isShow){ screenshotButton.setVisibility(View.VISIBLE); }else{ screenshotButton.setVisibility(View.GONE); } //添加监听并使用按键可见. ButtonDispatcher powerButton=mNavigationBarView.getPowerButton(); powerButton.setOnClickListener(mPowerClickListener); //powerButton.setOnTouchListener(mPowerTouchListener); powerButton.setVisibility(View.VISIBLE); ... }
|-- frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
public NavigationBarView(Context context, AttributeSet attrs) { super(context, attrs); mDisplay = ((WindowManager) context.getSystemService( Context.WINDOW_SERVICE)).getDefaultDisplay(); mVertical = false; mShowMenu = false; mGestureHelper = new NavigationBarGestureHelper(context); mConfiguration = new Configuration(); mConfiguration.updateFrom(context.getResources().getConfiguration()); updateIcons(context, Configuration.EMPTY, mConfiguration); mBarTransitions = new NavigationBarTransitions(this); mButtonDisatchers.put(R.id.back, new ButtonDispatcher(R.id.back)); mButtonDisatchers.put(R.id.home, new ButtonDispatcher(R.id.home)); mButtonDisatchers.put(R.id.recent_apps, new ButtonDispatcher(R.id.recent_apps)); mButtonDisatchers.put(R.id.menu, new ButtonDispatcher(R.id.menu)); mButtonDisatchers.put(R.id.ime_switcher, new ButtonDispatcher(R.id.ime_switcher)); mButtonDisatchers.put(R.id.screenshot, new ButtonDispatcher(R.id.screenshot)); //添加到集合. mButtonDisatchers.put(R.id.power, new ButtonDispatcher(R.id.power)); mButtonDisatchers.put(R.id.volume_add, new ButtonDispatcher(R.id.volume_add)); mButtonDisatchers.put(R.id.volume_sub, new ButtonDispatcher(R.id.volume_sub)); } //供外部调用 public ButtonDispatcher getPowerButton() { return mButtonDisatchers.get(R.id.power); }
至此, 按键添加完成, 在点击后, 会发送广播 android.intent.action.POWER_MENU
效果图:
关机菜单增加休眠项
准备资源:
frameworks/base/core/res/res/drawable/ic_sleep.xml
|-- frameworks/base/core/res/res/values/strings.xml
diff --git a/frameworks/base/core/res/res/values/strings.xml b/frameworks/base/core/res/res/values/strings.xml old mode 100644 new mode 100755 index 4172864..89d45d7 --- a/frameworks/base/core/res/res/values/strings.xml +++ b/frameworks/base/core/res/res/values/strings.xml @@ -496,6 +496,9 @@ <!-- label for item that turns off power in phone options dialog --> <string name="global_action_power_off">Power off</string> + + <string name="global_action_sleep">Sleep</string> <!-- label for item that restarts phone in phone options dialog --> <!-- TODO: promote to separate string-->
frameworks/base/core/res/res/values/symbols.xml
diff --git a/frameworks/base/core/res/res/values/symbols.xml b/frameworks/base/core/res/res/values/symbols.xml old mode 100644 new mode 100755 index 81d06af..02815a2 --- a/frameworks/base/core/res/res/values/symbols.xml +++ b/frameworks/base/core/res/res/values/symbols.xml @@ -1607,6 +1607,7 @@ <java-symbol type="string" name="faceunlock_multiple_failures" /> <java-symbol type="string" name="global_action_power_off" /> <java-symbol type="string" name="global_action_restart" /> + <java-symbol type="string" name="global_action_sleep" /> <java-symbol type="string" name="global_actions_airplane_mode_off_status" /> <java-symbol type="string" name="global_actions_airplane_mode_on_status" /> <java-symbol type="string" name="global_actions_toggle_airplane_mode" /> @@ -2734,6 +2735,7 @@ <java-symbol type="array" name="config_defaultFirstUserRestrictions" /> <java-symbol type="drawable" name="ic_restart" /> + <java-symbol type="drawable" name="ic_sleep" />
frameworks/base/core/res/res/values/config.xml
diff --git a/frameworks/base/core/res/res/values/config.xml b/frameworks/base/core/res/res/values/config.xml old mode 100644 new mode 100755 index e4839d7..935110a --- a/frameworks/base/core/res/res/values/config.xml +++ b/frameworks/base/core/res/res/values/config.xml @@ -2209,6 +2209,7 @@ <string-array translatable="false" name="config_globalActionsList"> <item>power</item> <item>restart</item> + <item>sleep</item> <item>bugreport</item> <item>users</item> </string-array>
删除生成中间文件:
rm -rf out/target/product/rk3288/system/framework/framework-res.apk out/target/product/rk3288/obj/APPS/framework-res_intermediates out/target/product/rk3288/obj/NOTICE_FILES/src/system/framework/framework-res.apk.txt out/target/product/rk3288/obj/PACKAGING/target_files_intermediates/rk3288-target_files-eng.anson/SYSTEM/framework/framework-res.apk out/target/common/obj/APPS/framework-res_intermediates
重新编译
mmm frameworks/base/core/res/ -j4
增加sleep接口
|-- 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; public static final int CAMERA_LENS_COVER_ABSENT = -1; public static final int CAMERA_LENS_UNCOVERED = 0; public static final int CAMERA_LENS_COVERED = 1; /** * Ask the window manager to re-evaluate the system UI flags. */ public void reevaluateStatusBarVisibility(); /** * Add a input consumer which will consume all input events going to any window below it. */ public InputConsumer addInputConsumer(Looper looper, InputEventReceiver.Factory inputEventReceiverFactory); /** * Returns a code that describes the current state of the lid switch. */ public int getLidState(); /** * Lock the device now. */ public void lockDeviceNow(); /** * Returns a code that descripbes whether the camera lens is covered or not. */ public int getCameraLensCoverState(); /** * Switch the input method, to be precise, input method subtype. * * @param forwardDirection {@code true} to rotate in a forward direction. */ public void switchInputMethod(boolean forwardDirection); public void shutdown(boolean confirm); public void reboot(boolean confirm); public void rebootSafeMode(boolean confirm); //增加 public void sleep(); }
实现sleep:
|-- frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs { ... // Called by window manager policy. Not exposed externally. @Override public void shutdown(boolean confirm) { ShutdownThread.shutdown(mContext, PowerManager.SHUTDOWN_USER_REQUESTED, confirm); } // Called by window manager policy. Not exposed externally. @Override public void reboot(boolean confirm) { ShutdownThread.reboot(mContext, PowerManager.SHUTDOWN_USER_REQUESTED, confirm); } // Called by window manager policy. Not exposed externally. @Override public void rebootSafeMode(boolean confirm) { ShutdownThread.rebootSafeMode(mContext, confirm); } //实现sleep, 调用PowerManager.goToSleep. @Override public void sleep(){ mPowerManager.goToSleep(SystemClock.uptimeMillis()); } ... }
增加sleep项
|-- frameworks/base/services/core/java/com/android/server/policy/GlobalActions.java
private static final String GLOBAL_ACTION_KEY_RESTART = "restart"; //增加sleep private static final String GLOBAL_ACTION_KEY_SLEEP = "sleep"; private GlobalActionsDialog createDialog() { ... //从xml中加载config_globalActionsList字符数组 mItems = new ArrayList<Action>(); String[] defaultActions = mContext.getResources().getStringArray( com.android.internal.R.array.config_globalActionsList); ArraySet<String> addedKeys = new ArraySet<String>(); for (int i = 0; i < defaultActions.length; i++) { String actionKey = defaultActions[i]; if (addedKeys.contains(actionKey)) { // If we already have added this, don't add it again. continue; } if (GLOBAL_ACTION_KEY_POWER.equals(actionKey)) { mItems.add(new PowerAction()); } else if (GLOBAL_ACTION_KEY_AIRPLANE.equals(actionKey)) { mItems.add(mAirplaneModeOn); } else if (GLOBAL_ACTION_KEY_BUGREPORT.equals(actionKey)) { if (Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.BUGREPORT_IN_POWER_MENU, 0) != 0 && isCurrentUserOwner()) { mItems.add(new BugReportAction()); } } else if (GLOBAL_ACTION_KEY_SILENT.equals(actionKey)) { if (mShowSilentToggle) { mItems.add(mSilentModeAction); } } else if (GLOBAL_ACTION_KEY_USERS.equals(actionKey)) { if (SystemProperties.getBoolean("fw.power_user_switcher", false)) { addUsersToMenu(mItems); } } else if (GLOBAL_ACTION_KEY_SETTINGS.equals(actionKey)) { mItems.add(getSettingsAction()); } else if (GLOBAL_ACTION_KEY_LOCKDOWN.equals(actionKey)) { mItems.add(getLockdownAction()); } else if (GLOBAL_ACTION_KEY_VOICEASSIST.equals(actionKey)) { mItems.add(getVoiceAssistAction()); } else if (GLOBAL_ACTION_KEY_ASSIST.equals(actionKey)) { mItems.add(getAssistAction()); } else if (GLOBAL_ACTION_KEY_RESTART.equals(actionKey)) { mItems.add(new RestartAction()); //创建并添加SleepAction } else if (GLOBAL_ACTION_KEY_SLEEP.equals(actionKey)){ mItems.add(new SleepAction()); } else { Log.e(TAG, "Invalid global action key " + actionKey); } // Add here so we don't add more than one. addedKeys.add(actionKey); } if (mEmergencyAffordanceManager.needsEmergencyAffordance()) { mItems.add(getEmergencyAction()); } mAdapter = new MyAdapter(); AlertParams params = new AlertParams(mContext); params.mAdapter = mAdapter; ... } //增加SleepAction. private final class SleepAction extends SinglePressAction implements LongPressAction { private SleepAction() { super(R.drawable.ic_sleep, R.string.global_action_sleep); } @Override public boolean onLongPress() { return false; } @Override public boolean showDuringKeyguard() { return true; } @Override public boolean showBeforeProvisioning() { return true; } @Override public void onPress() { //调用新增sleep函数 mWindowManagerFuncs.sleep(); } }
接收从SystemUI发送过来的广播并调起GlobalActions.
|-- frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
/** {@inheritDoc} */ @Override public void init(Context context, IWindowManager windowManager, WindowManagerFuncs windowManagerFuncs) { mContext = context; mWindowManager = windowManager; mWindowManagerFuncs = windowManagerFuncs; ... // register for dream-related broadcasts filter = new IntentFilter(); filter.addAction(Intent.ACTION_DREAMING_STARTED); filter.addAction(Intent.ACTION_DREAMING_STOPPED); context.registerReceiver(mDreamReceiver, filter); filter = new IntentFilter(); filter.addAction(Intent.ACTION_SHUTDOWN); context.registerReceiver(mShutdownanimationReceiver, filter); // register for multiuser-relevant broadcasts filter = new IntentFilter(Intent.ACTION_USER_SWITCHED); context.registerReceiver(mMultiuserReceiver, filter); //注册广播监听: android.intent.action.POWER_MENU IntentFilter ifPower = new IntentFilter("android.intent.action.POWER_MENU"); context.registerReceiver(new BroadcastReceiver(){ @Override public void onReceive(Context context, Intent intent) { //show global actions dialog showGlobalActionsInternal(); } }, ifPower); }
完成, 上图: