From 678ddce11c131a8e6746fe386c40ddc8d99d01c3 Mon Sep 17 00:00:00 2001 From: Yohei Yukawa Date: Thu, 27 Jan 2022 10:29:33 -0800 Subject: [PATCH] Support light navigation bar In order to propagate the information about whether the IME is using light navigation bar or not from android.view.ViewRootImpl to android.inputmethodservice.NavigationBarController within the same process, this CL adds a new @hide method to Window.Callback as follows. ViewRootImpl#performTraversals() -> DecorView#onSystemBarAppearanceChanged() -> Window.Callback#onSystemBarAppearanceChanged() -> SoftInputWindow#onSystemBarAppearanceChanged() -> NavigationBarController#onSystemBarAppearanceChanged() Button color transition will be implemented in a subsequent CL. Bug: 215549533 Test: Manually tested with ThemedNavBarKeyboard sample 1. Build aosp_coral-userdebug and flash it 2. adb root 3. adb shell setprop \ persist.sys.ime.can_render_gestural_nav_buttons true 4. adb reboot 5. make -j ThemedNavBarKeyboard 6. adb install -r \ $OUT/system/app/ThemedNavBarKeyboard/ThemedNavBarKeyboard.apk 7. adb shell ime enable \ com.example.android.themednavbarkeyboard/.ThemedNavBarKeyboard 8. adb shell ime set \ com.example.android.themednavbarkeyboard/.ThemedNavBarKeyboard 9. Open the Dialer app 10. Focus in the top edit field. 11. Tap "EXTENDED LIGHT NAVIGARION BAR" mode 12. Make sure that the navigation button color is optimized for light navigation bar. 13. Tap "STANDARD LIGHT NAVIGARION BAR" mode 14. Make sure that the navigation button color is optimized for light navigation bar. Change-Id: I08566034bebfafff6777ce0152cd6ca1f66f6cad --- .../InputMethodService.java | 2 +- .../NavigationBarController.java | 47 +++++++++++++++++++ .../inputmethodservice/SoftInputWindow.java | 16 +++++-- core/java/android/view/Window.java | 11 +++++ .../android/view/WindowCallbackWrapper.java | 5 ++ .../android/internal/policy/DecorView.java | 6 +++ 6 files changed, 82 insertions(+), 5 deletions(-) diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 4fd375177d6f9..da6babd8c8276 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -475,7 +475,7 @@ public class InputMethodService extends AbstractInputMethodService { private InputMethodPrivilegedOperations mPrivOps = new InputMethodPrivilegedOperations(); @NonNull - private final NavigationBarController mNavigationBarController = + final NavigationBarController mNavigationBarController = new NavigationBarController(this); @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java index a6e475aeb813c..2484cf079c562 100644 --- a/core/java/android/inputmethodservice/NavigationBarController.java +++ b/core/java/android/inputmethodservice/NavigationBarController.java @@ -17,7 +17,9 @@ package android.inputmethodservice; import static android.content.Intent.ACTION_OVERLAY_CHANGED; +import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS; +import android.annotation.FloatRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.StatusBarManager; @@ -40,6 +42,7 @@ import android.view.ViewParent; import android.view.ViewTreeObserver; import android.view.Window; import android.view.WindowInsets; +import android.view.WindowInsetsController.Appearance; import android.view.WindowManagerPolicyConstants; import android.widget.FrameLayout; @@ -68,6 +71,9 @@ final class NavigationBarController { default void onDestroy() { } + default void onSystemBarAppearanceChanged(@Appearance int appearance) { + } + default String toDebugString() { return "No-op implementation"; } @@ -100,6 +106,10 @@ final class NavigationBarController { mImpl.onDestroy(); } + void onSystemBarAppearanceChanged(@Appearance int appearance) { + mImpl.onSystemBarAppearanceChanged(appearance); + } + String toDebugString() { return mImpl.toDebugString(); } @@ -120,6 +130,9 @@ final class NavigationBarController { @Nullable private BroadcastReceiver mSystemOverlayChangedReceiver; + @Appearance + private int mAppearance; + Impl(@NonNull InputMethodService inputMethodService) { mService = inputMethodService; } @@ -187,6 +200,8 @@ final class NavigationBarController { } mNavigationBarFrame.setBackground(null); + + setIconTintInternal(calculateTargetDarkIntensity(mAppearance)); } private void uninstallNavigationBarFrameIfNecessary() { @@ -388,10 +403,42 @@ final class NavigationBarController { } } + @Override + public void onSystemBarAppearanceChanged(@Appearance int appearance) { + if (mDestroyed) { + return; + } + + mAppearance = appearance; + + if (mNavigationBarFrame == null) { + return; + } + + final float targetDarkIntensity = calculateTargetDarkIntensity(mAppearance); + setIconTintInternal(targetDarkIntensity); + } + + private void setIconTintInternal(float darkIntensity) { + final NavigationBarView navigationBarView = + mNavigationBarFrame.findViewByPredicate(NavigationBarView.class::isInstance); + if (navigationBarView == null) { + return; + } + navigationBarView.setDarkIntensity(darkIntensity); + } + + @FloatRange(from = 0.0f, to = 1.0f) + private static float calculateTargetDarkIntensity(@Appearance int appearance) { + final boolean lightNavBar = (appearance & APPEARANCE_LIGHT_NAVIGATION_BARS) != 0; + return lightNavBar ? 1.0f : 0.0f; + } + @Override public String toDebugString() { return "{mRenderGesturalNavButtons=" + mRenderGesturalNavButtons + " mNavigationBarFrame=" + mNavigationBarFrame + + " mAppearance=0x" + Integer.toHexString(mAppearance) + "}"; } } diff --git a/core/java/android/inputmethodservice/SoftInputWindow.java b/core/java/android/inputmethodservice/SoftInputWindow.java index 6c8eb41d8724f..0893d2aad7408 100644 --- a/core/java/android/inputmethodservice/SoftInputWindow.java +++ b/core/java/android/inputmethodservice/SoftInputWindow.java @@ -23,7 +23,6 @@ import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.IntDef; import android.app.Dialog; -import android.content.Context; import android.graphics.Rect; import android.os.Debug; import android.os.IBinder; @@ -32,6 +31,7 @@ import android.util.proto.ProtoOutputStream; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; +import android.view.WindowInsetsController; import android.view.WindowManager; import java.lang.annotation.Retention; @@ -47,6 +47,7 @@ final class SoftInputWindow extends Dialog { private final KeyEvent.DispatcherState mDispatcherState; private final Rect mBounds = new Rect(); + private final InputMethodService mService; @Retention(SOURCE) @IntDef(value = {WindowState.TOKEN_PENDING, WindowState.TOKEN_SET, @@ -120,7 +121,7 @@ final class SoftInputWindow extends Dialog { /** * Create a SoftInputWindow that uses a custom style. * - * @param context The Context in which the DockWindow should run. In + * @param service The {@link InputMethodService} in which the DockWindow should run. In * particular, it uses the window manager and theme from this context * to present its UI. * @param theme A style resource describing the theme to use for the window. @@ -129,8 +130,10 @@ final class SoftInputWindow extends Dialog { * using styles. This theme is applied on top of the current theme in * context. If 0, the default dialog theme will be used. */ - SoftInputWindow(Context context, int theme, KeyEvent.DispatcherState dispatcherState) { - super(context, theme); + SoftInputWindow(InputMethodService service, int theme, + KeyEvent.DispatcherState dispatcherState) { + super(service, theme); + mService = service; mDispatcherState = dispatcherState; } @@ -261,6 +264,11 @@ final class SoftInputWindow extends Dialog { } } + @Override + public void onSystemBarAppearanceChanged(@WindowInsetsController.Appearance int appearance) { + mService.mNavigationBarController.onSystemBarAppearanceChanged(appearance); + } + void dumpDebug(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); mBounds.dumpDebug(proto, BOUNDS); diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index aa9ea19b025e0..039b50a637be4 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -607,6 +607,17 @@ public abstract class Window { * @param hasCapture True if the window has pointer capture. */ default public void onPointerCaptureChanged(boolean hasCapture) { }; + + /** + * Called from + * {@link com.android.internal.policy.DecorView#onSystemBarAppearanceChanged(int)}. + * + * @param appearance The newly applied appearance. + * @hide + */ + default void onSystemBarAppearanceChanged( + @WindowInsetsController.Appearance int appearance) { + } } /** @hide */ diff --git a/core/java/android/view/WindowCallbackWrapper.java b/core/java/android/view/WindowCallbackWrapper.java index 02c8945d9fce1..115e9e891408b 100644 --- a/core/java/android/view/WindowCallbackWrapper.java +++ b/core/java/android/view/WindowCallbackWrapper.java @@ -163,5 +163,10 @@ public class WindowCallbackWrapper implements Window.Callback { public void onPointerCaptureChanged(boolean hasCapture) { mWrapped.onPointerCaptureChanged(hasCapture); } + + @Override + public void onSystemBarAppearanceChanged(@WindowInsetsController.Appearance int appearance) { + mWrapped.onSystemBarAppearanceChanged(appearance); + } } diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index d7eeb7b8dec0d..a50282ee0aa6d 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -1034,6 +1034,12 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind @Override public void onSystemBarAppearanceChanged(@WindowInsetsController.Appearance int appearance) { updateColorViews(null /* insets */, true /* animate */); + if (mWindow != null) { + final Window.Callback callback = mWindow.getCallback(); + if (callback != null) { + callback.onSystemBarAppearanceChanged(appearance); + } + } } @Override