platform
rk3288 + Android 7.1
Requirement specification
1. Add the power button in the navigation bar and click it to open the shutdown menu. 2. Add sleep item in shutdown menu item and click backward sleep
Related documents
//modify 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 //Newly added 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
Realization
Navigation bar add key
Prepare the corresponding icon and related characters:
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>
Add key 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"
/>
Where, keyCode is the key value. If you do not want to process it as a key, set it to 0.
|-- frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
public static final String POWER = "power"; //Load button @Override protected void onFinishInflate() { super.onFinishInflate(); inflateChildren(); clearViews(); inflateLayout(getDefaultLayout()); } //Default button 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); } //Add navigation bar @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); } //Add the POWER button below. layout.power.xml is also used here }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; }
The default key layout is defined as "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>
Add power's individual changes:
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>
The add touch event setting button is visible:
|-- frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
//Click to process: 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); } //Add monitor and use key to see. 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)); //Add to collection. 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)); } //For external calls public ButtonDispatcher getPowerButton() { return mButtonDisatchers.get(R.id.power); }
At this point, the key is added. After clicking, the broadcast android.intent.action.power menu will be sent.
Design sketch:
Add sleep item in shutdown menu
Prepare resources:
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>
Delete build intermediate file:
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
Recompile
mmm frameworks/base/core/res/ -j4
Add sleep interface
|-- 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); //increase public void sleep(); }
Implementation of 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); } //Implement sleep, call PowerManager.goToSleep. @Override public void sleep(){ mPowerManager.goToSleep(SystemClock.uptimeMillis()); } ... }
Add sleep item
|-- frameworks/base/services/core/java/com/android/server/policy/GlobalActions.java
private static final String GLOBAL_ACTION_KEY_RESTART = "restart"; //Increase sleep private static final String GLOBAL_ACTION_KEY_SLEEP = "sleep"; private GlobalActionsDialog createDialog() { ... //Load config ﹣ globalactionslist character array from xml 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()); //Create and add a 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; ... } //Add 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() { //Call the new sleep function mWindowManagerFuncs.sleep(); } }
Receive broadcast from system UI and call up 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); //Register broadcast listening: 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); }
Complete, above: