Loading packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java +6 −2 Original line number Original line Diff line number Diff line Loading @@ -76,11 +76,14 @@ public class LightBarController implements BatteryController.BatteryStateChangeC private final Rect mLastDockedBounds = new Rect(); private final Rect mLastDockedBounds = new Rect(); private boolean mQsCustomizing; private boolean mQsCustomizing; private final Context mContext; public LightBarController(Context ctx) { public LightBarController(Context ctx) { mDarkModeColor = Color.valueOf(ctx.getColor(R.color.dark_mode_icon_color_single_tone)); mDarkModeColor = Color.valueOf(ctx.getColor(R.color.dark_mode_icon_color_single_tone)); mStatusBarIconController = Dependency.get(DarkIconDispatcher.class); mStatusBarIconController = Dependency.get(DarkIconDispatcher.class); mBatteryController = Dependency.get(BatteryController.class); mBatteryController = Dependency.get(BatteryController.class); mBatteryController.addCallback(this); mBatteryController.addCallback(this); mContext = ctx; } } public void setNavigationBar(LightBarTransitionsController navigationBar) { public void setNavigationBar(LightBarTransitionsController navigationBar) { Loading Loading @@ -217,8 +220,9 @@ public class LightBarController implements BatteryController.BatteryStateChangeC private void updateNavigation() { private void updateNavigation() { if (mNavigationBarController != null) { if (mNavigationBarController != null) { mNavigationBarController.setIconsDark( if (!NavBarTintController.isEnabled(mContext)) { mNavigationLight, animateChange()); mNavigationBarController.setIconsDark(mNavigationLight, animateChange()); } } } } } Loading packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java +20 −3 Original line number Original line Diff line number Diff line Loading @@ -16,12 +16,16 @@ package com.android.systemui.statusbar.phone; package com.android.systemui.statusbar.phone; import static com.android.systemui.statusbar.phone.NavBarTintController.MIN_COLOR_ADAPT_TRANSITION_TIME; import static com.android.systemui.statusbar.phone.NavBarTintController.NAV_COLOR_TRANSITION_TIME_SETTING; import android.animation.ValueAnimator; import android.animation.ValueAnimator; import android.content.Context; import android.content.Context; import android.os.Bundle; import android.os.Bundle; import android.os.Handler; import android.os.Handler; import android.os.SystemClock; import android.os.SystemClock; import android.util.MathUtils; import android.util.MathUtils; import android.provider.Settings; import android.util.TimeUtils; import android.util.TimeUtils; import com.android.systemui.Dependency; import com.android.systemui.Dependency; Loading @@ -42,13 +46,14 @@ import java.io.PrintWriter; public class LightBarTransitionsController implements Dumpable, Callbacks, public class LightBarTransitionsController implements Dumpable, Callbacks, StatusBarStateController.StateListener { StatusBarStateController.StateListener { public static final long DEFAULT_TINT_ANIMATION_DURATION = 120; public static final int DEFAULT_TINT_ANIMATION_DURATION = 120; private static final String EXTRA_DARK_INTENSITY = "dark_intensity"; private static final String EXTRA_DARK_INTENSITY = "dark_intensity"; private final Handler mHandler; private final Handler mHandler; private final DarkIntensityApplier mApplier; private final DarkIntensityApplier mApplier; private final KeyguardMonitor mKeyguardMonitor; private final KeyguardMonitor mKeyguardMonitor; private final StatusBarStateController mStatusBarStateController; private final StatusBarStateController mStatusBarStateController; private NavBarTintController mColorAdaptionController; private boolean mTransitionDeferring; private boolean mTransitionDeferring; private long mTransitionDeferringStartTime; private long mTransitionDeferringStartTime; Loading @@ -67,6 +72,8 @@ public class LightBarTransitionsController implements Dumpable, Callbacks, } } }; }; private final Context mContext; public LightBarTransitionsController(Context context, DarkIntensityApplier applier) { public LightBarTransitionsController(Context context, DarkIntensityApplier applier) { mApplier = applier; mApplier = applier; mHandler = new Handler(); mHandler = new Handler(); Loading @@ -76,6 +83,7 @@ public class LightBarTransitionsController implements Dumpable, Callbacks, .addCallback(this); .addCallback(this); mStatusBarStateController.addCallback(this); mStatusBarStateController.addCallback(this); mDozeAmount = mStatusBarStateController.getDozeAmount(); mDozeAmount = mStatusBarStateController.getDozeAmount(); mContext = context; } } public void destroy(Context context) { public void destroy(Context context) { Loading Loading @@ -106,7 +114,7 @@ public class LightBarTransitionsController implements Dumpable, Callbacks, public void appTransitionCancelled() { public void appTransitionCancelled() { if (mTransitionPending && mTintChangePending) { if (mTransitionPending && mTintChangePending) { mTintChangePending = false; mTintChangePending = false; animateIconTint(mPendingDarkIntensity, 0 /* delay */, DEFAULT_TINT_ANIMATION_DURATION); animateIconTint(mPendingDarkIntensity, 0 /* delay */, getTintAnimationDuration()); } } mTransitionPending = false; mTransitionPending = false; } } Loading Loading @@ -146,8 +154,17 @@ public class LightBarTransitionsController implements Dumpable, Callbacks, Math.max(0, mTransitionDeferringStartTime - SystemClock.uptimeMillis()), Math.max(0, mTransitionDeferringStartTime - SystemClock.uptimeMillis()), mTransitionDeferringDuration); mTransitionDeferringDuration); } else { } else { animateIconTint(dark ? 1.0f : 0.0f, 0 /* delay */, DEFAULT_TINT_ANIMATION_DURATION); animateIconTint(dark ? 1.0f : 0.0f, 0 /* delay */, getTintAnimationDuration()); } } public long getTintAnimationDuration() { if (NavBarTintController.isEnabled(mContext)) { return Math.max(Settings.Global.getInt(mContext.getContentResolver(), NAV_COLOR_TRANSITION_TIME_SETTING, DEFAULT_TINT_ANIMATION_DURATION), MIN_COLOR_ADAPT_TRANSITION_TIME); } } return DEFAULT_TINT_ANIMATION_DURATION; } } public float getCurrentDarkIntensity() { public float getCurrentDarkIntensity() { Loading packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java 0 → 100644 +208 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.statusbar.phone; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.Color; import android.graphics.Rect; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.provider.Settings; import android.util.DisplayMetrics; import android.view.SurfaceControl; public class NavBarTintController { public static final String NAV_COLOR_TRANSITION_TIME_SETTING = "navbar_color_adapt_transition"; public static final int MIN_COLOR_ADAPT_TRANSITION_TIME = 400; private final HandlerThread mColorAdaptHandlerThread = new HandlerThread("ColorExtractThread"); private Handler mColorAdaptionHandler; // Poll time for each iteration to color sample private static final int COLOR_ADAPTION_TIMEOUT = 300; // Passing the threshold of this luminance value will make the button black otherwise white private static final float LUMINANCE_THRESHOLD = 0.3f; // The home button's icon is actually smaller than the button's size, the percentage will // cut into the button's size to determine the icon size private static final float PERCENTAGE_BUTTON_PADDING = 0.3f; // The distance from the home button to color sample around private static final int COLOR_SAMPLE_MARGIN = 20; private boolean mRunning; private final NavigationBarView mNavigationBarView; private final LightBarTransitionsController mLightBarController; private final Handler mMainHandler = new Handler(Looper.getMainLooper()); public NavBarTintController(NavigationBarView navigationBarView, LightBarTransitionsController lightBarController) { mNavigationBarView = navigationBarView; mLightBarController = lightBarController; } public void start() { if (!isEnabled(mNavigationBarView.getContext())) { return; } if (mColorAdaptionHandler == null) { mColorAdaptHandlerThread.start(); mColorAdaptionHandler = new Handler(mColorAdaptHandlerThread.getLooper()); } mColorAdaptionHandler.removeCallbacksAndMessages(null); mColorAdaptionHandler.post(this::updateTint); mRunning = true; } public void end() { if (mColorAdaptionHandler != null) { mColorAdaptionHandler.removeCallbacksAndMessages(null); } mRunning = false; } public void stop() { end(); if (mColorAdaptionHandler != null) { mColorAdaptHandlerThread.quitSafely(); } } private void updateTint() { int[] navPos = new int[2]; int[] butPos = new int[2]; if (mNavigationBarView.getHomeButton().getCurrentView() == null) { return; } // Determine the area of the home icon in the larger home button mNavigationBarView.getHomeButton().getCurrentView().getLocationInSurface(butPos); final int navWidth = mNavigationBarView.getHomeButton().getCurrentView().getWidth(); final int navHeight = mNavigationBarView.getHomeButton().getCurrentView().getHeight(); final int xPadding = (int) (PERCENTAGE_BUTTON_PADDING * navWidth); final int yPadding = (int) (PERCENTAGE_BUTTON_PADDING * navHeight); final Rect homeButtonRect = new Rect(butPos[0] + xPadding, butPos[1] + yPadding, navWidth + butPos[0] - xPadding, navHeight + butPos[1] - yPadding); if (mNavigationBarView.getCurrentView() == null || homeButtonRect.isEmpty()) { scheduleColorAdaption(); return; } mNavigationBarView.getCurrentView().getLocationOnScreen(navPos); homeButtonRect.offset(navPos[0], navPos[1]); // Apply a margin area around the button region to sample the colors, crop from screenshot final Rect cropRect = new Rect(homeButtonRect); cropRect.inset(-COLOR_SAMPLE_MARGIN, -COLOR_SAMPLE_MARGIN); if (cropRect.isEmpty()) { scheduleColorAdaption(); return; } // Determine the size of the home area Rect homeArea = new Rect(COLOR_SAMPLE_MARGIN, COLOR_SAMPLE_MARGIN, homeButtonRect.width() + COLOR_SAMPLE_MARGIN, homeButtonRect.height() + COLOR_SAMPLE_MARGIN); // Get the screenshot around the home button icon to determine the color DisplayMetrics mDisplayMetrics = new DisplayMetrics(); mNavigationBarView.getContext().getDisplay().getRealMetrics(mDisplayMetrics); final Bitmap hardBitmap = SurfaceControl .screenshot(new Rect(), mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels, mNavigationBarView.getContext().getDisplay().getRotation()); if (hardBitmap != null && cropRect.bottom <= hardBitmap.getHeight()) { final Bitmap cropBitmap = Bitmap.createBitmap(hardBitmap, cropRect.left, cropRect.top, cropRect.width(), cropRect.height()); final Bitmap softBitmap = cropBitmap.copy(Config.ARGB_8888, false); // Get the luminance value to determine if the home button should be black or white final int[] pixels = new int[softBitmap.getByteCount() / 4]; softBitmap.getPixels(pixels, 0, softBitmap.getWidth(), 0, 0, softBitmap.getWidth(), softBitmap.getHeight()); float r = 0, g = 0, blue = 0; int width = cropRect.width(); int total = 0; for (int i = 0; i < pixels.length; i += 4) { int x = i % width; int y = i / width; if (!homeArea.contains(x, y)) { r += Color.red(pixels[i]); g += Color.green(pixels[i]); blue += Color.blue(pixels[i]); total++; } } r /= total; g /= total; blue /= total; r = Math.max(Math.min(r / 255f, 1), 0); g = Math.max(Math.min(g / 255f, 1), 0); blue = Math.max(Math.min(blue / 255f, 1), 0); if (r <= 0.03928) { r /= 12.92; } else { r = (float) Math.pow((r + 0.055) / 1.055, 2.4); } if (g <= 0.03928) { g /= 12.92; } else { g = (float) Math.pow((g + 0.055) / 1.055, 2.4); } if (blue <= 0.03928) { blue /= 12.92; } else { blue = (float) Math.pow((blue + 0.055) / 1.055, 2.4); } if (r * 0.2126 + g * 0.7152 + blue * 0.0722 > LUMINANCE_THRESHOLD) { // Black mMainHandler.post( () -> mLightBarController .setIconsDark(true /* dark */, true /* animate */)); } else { // White mMainHandler.post( () -> mLightBarController .setIconsDark(false /* dark */, true /* animate */)); } cropBitmap.recycle(); hardBitmap.recycle(); } scheduleColorAdaption(); } private void scheduleColorAdaption() { mColorAdaptionHandler.removeCallbacksAndMessages(null); if (!mRunning || !isEnabled(mNavigationBarView.getContext())) { return; } mColorAdaptionHandler.postDelayed(this::updateTint, COLOR_ADAPTION_TIMEOUT); } public static boolean isEnabled(Context context) { return Settings.Global.getInt(context.getContentResolver(), NavigationPrototypeController.NAV_COLOR_ADAPT_ENABLE_SETTING, 0) == 1; } } packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +10 −0 Original line number Original line Diff line number Diff line Loading @@ -851,6 +851,16 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback if (Intent.ACTION_SCREEN_OFF.equals(action) if (Intent.ACTION_SCREEN_OFF.equals(action) || Intent.ACTION_SCREEN_ON.equals(action)) { || Intent.ACTION_SCREEN_ON.equals(action)) { notifyNavigationBarScreenOn(); notifyNavigationBarScreenOn(); if (Intent.ACTION_SCREEN_ON.equals(action)) { // Enabled and screen is on, start it again if enabled if (NavBarTintController.isEnabled(getContext())) { mNavigationBarView.getColorAdaptionController().start(); } } else { // Screen off disable it mNavigationBarView.getColorAdaptionController().end(); } } } if (Intent.ACTION_USER_SWITCHED.equals(action)) { if (Intent.ACTION_USER_SWITCHED.equals(action)) { // The accessibility settings may be different for the new user // The accessibility settings may be different for the new user Loading packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +17 −0 Original line number Original line Diff line number Diff line Loading @@ -149,6 +149,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav private RecentsOnboarding mRecentsOnboarding; private RecentsOnboarding mRecentsOnboarding; private NotificationPanelView mPanelView; private NotificationPanelView mPanelView; private NavBarTintController mColorAdaptionController; private NavigationPrototypeController mPrototypeController; private NavigationPrototypeController mPrototypeController; private NavigationGestureAction[] mDefaultGestureMap; private NavigationGestureAction[] mDefaultGestureMap; private QuickScrubAction mQuickScrubAction; private QuickScrubAction mQuickScrubAction; Loading Loading @@ -277,6 +278,15 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav public void onBackButtonVisibilityChanged(boolean visible) { public void onBackButtonVisibilityChanged(boolean visible) { getBackButton().setVisibility(visible ? VISIBLE : GONE); getBackButton().setVisibility(visible ? VISIBLE : GONE); } } @Override public void onColorAdaptChanged(boolean enabled) { if (enabled) { mColorAdaptionController.start(); } else { mColorAdaptionController.end(); } } }; }; public NavigationBarView(Context context, AttributeSet attrs) { public NavigationBarView(Context context, AttributeSet attrs) { Loading Loading @@ -334,6 +344,11 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav mPrototypeController = new NavigationPrototypeController(mHandler, mContext); mPrototypeController = new NavigationPrototypeController(mHandler, mContext); mPrototypeController.register(); mPrototypeController.register(); mPrototypeController.setOnPrototypeChangedListener(mPrototypeListener); mPrototypeController.setOnPrototypeChangedListener(mPrototypeListener); mColorAdaptionController = new NavBarTintController(this, getLightTransitionsController()); } public NavBarTintController getColorAdaptionController() { return mColorAdaptionController; } } public BarTransitions getBarTransitions() { public BarTransitions getBarTransitions() { Loading Loading @@ -1097,6 +1112,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav Dependency.get(PluginManager.class).addPluginListener(this, Dependency.get(PluginManager.class).addPluginListener(this, NavGesture.class, false /* Only one */); NavGesture.class, false /* Only one */); setUpSwipeUpOnboarding(isQuickStepSwipeUpEnabled()); setUpSwipeUpOnboarding(isQuickStepSwipeUpEnabled()); mColorAdaptionController.start(); } } @Override @Override Loading @@ -1107,6 +1123,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav mGestureHelper.destroy(); mGestureHelper.destroy(); } } mPrototypeController.unregister(); mPrototypeController.unregister(); mColorAdaptionController.stop(); setUpSwipeUpOnboarding(false); setUpSwipeUpOnboarding(false); for (int i = 0; i < mButtonDispatchers.size(); ++i) { for (int i = 0; i < mButtonDispatchers.size(); ++i) { mButtonDispatchers.valueAt(i).onDestroy(); mButtonDispatchers.valueAt(i).onDestroy(); Loading Loading
packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java +6 −2 Original line number Original line Diff line number Diff line Loading @@ -76,11 +76,14 @@ public class LightBarController implements BatteryController.BatteryStateChangeC private final Rect mLastDockedBounds = new Rect(); private final Rect mLastDockedBounds = new Rect(); private boolean mQsCustomizing; private boolean mQsCustomizing; private final Context mContext; public LightBarController(Context ctx) { public LightBarController(Context ctx) { mDarkModeColor = Color.valueOf(ctx.getColor(R.color.dark_mode_icon_color_single_tone)); mDarkModeColor = Color.valueOf(ctx.getColor(R.color.dark_mode_icon_color_single_tone)); mStatusBarIconController = Dependency.get(DarkIconDispatcher.class); mStatusBarIconController = Dependency.get(DarkIconDispatcher.class); mBatteryController = Dependency.get(BatteryController.class); mBatteryController = Dependency.get(BatteryController.class); mBatteryController.addCallback(this); mBatteryController.addCallback(this); mContext = ctx; } } public void setNavigationBar(LightBarTransitionsController navigationBar) { public void setNavigationBar(LightBarTransitionsController navigationBar) { Loading Loading @@ -217,8 +220,9 @@ public class LightBarController implements BatteryController.BatteryStateChangeC private void updateNavigation() { private void updateNavigation() { if (mNavigationBarController != null) { if (mNavigationBarController != null) { mNavigationBarController.setIconsDark( if (!NavBarTintController.isEnabled(mContext)) { mNavigationLight, animateChange()); mNavigationBarController.setIconsDark(mNavigationLight, animateChange()); } } } } } Loading
packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java +20 −3 Original line number Original line Diff line number Diff line Loading @@ -16,12 +16,16 @@ package com.android.systemui.statusbar.phone; package com.android.systemui.statusbar.phone; import static com.android.systemui.statusbar.phone.NavBarTintController.MIN_COLOR_ADAPT_TRANSITION_TIME; import static com.android.systemui.statusbar.phone.NavBarTintController.NAV_COLOR_TRANSITION_TIME_SETTING; import android.animation.ValueAnimator; import android.animation.ValueAnimator; import android.content.Context; import android.content.Context; import android.os.Bundle; import android.os.Bundle; import android.os.Handler; import android.os.Handler; import android.os.SystemClock; import android.os.SystemClock; import android.util.MathUtils; import android.util.MathUtils; import android.provider.Settings; import android.util.TimeUtils; import android.util.TimeUtils; import com.android.systemui.Dependency; import com.android.systemui.Dependency; Loading @@ -42,13 +46,14 @@ import java.io.PrintWriter; public class LightBarTransitionsController implements Dumpable, Callbacks, public class LightBarTransitionsController implements Dumpable, Callbacks, StatusBarStateController.StateListener { StatusBarStateController.StateListener { public static final long DEFAULT_TINT_ANIMATION_DURATION = 120; public static final int DEFAULT_TINT_ANIMATION_DURATION = 120; private static final String EXTRA_DARK_INTENSITY = "dark_intensity"; private static final String EXTRA_DARK_INTENSITY = "dark_intensity"; private final Handler mHandler; private final Handler mHandler; private final DarkIntensityApplier mApplier; private final DarkIntensityApplier mApplier; private final KeyguardMonitor mKeyguardMonitor; private final KeyguardMonitor mKeyguardMonitor; private final StatusBarStateController mStatusBarStateController; private final StatusBarStateController mStatusBarStateController; private NavBarTintController mColorAdaptionController; private boolean mTransitionDeferring; private boolean mTransitionDeferring; private long mTransitionDeferringStartTime; private long mTransitionDeferringStartTime; Loading @@ -67,6 +72,8 @@ public class LightBarTransitionsController implements Dumpable, Callbacks, } } }; }; private final Context mContext; public LightBarTransitionsController(Context context, DarkIntensityApplier applier) { public LightBarTransitionsController(Context context, DarkIntensityApplier applier) { mApplier = applier; mApplier = applier; mHandler = new Handler(); mHandler = new Handler(); Loading @@ -76,6 +83,7 @@ public class LightBarTransitionsController implements Dumpable, Callbacks, .addCallback(this); .addCallback(this); mStatusBarStateController.addCallback(this); mStatusBarStateController.addCallback(this); mDozeAmount = mStatusBarStateController.getDozeAmount(); mDozeAmount = mStatusBarStateController.getDozeAmount(); mContext = context; } } public void destroy(Context context) { public void destroy(Context context) { Loading Loading @@ -106,7 +114,7 @@ public class LightBarTransitionsController implements Dumpable, Callbacks, public void appTransitionCancelled() { public void appTransitionCancelled() { if (mTransitionPending && mTintChangePending) { if (mTransitionPending && mTintChangePending) { mTintChangePending = false; mTintChangePending = false; animateIconTint(mPendingDarkIntensity, 0 /* delay */, DEFAULT_TINT_ANIMATION_DURATION); animateIconTint(mPendingDarkIntensity, 0 /* delay */, getTintAnimationDuration()); } } mTransitionPending = false; mTransitionPending = false; } } Loading Loading @@ -146,8 +154,17 @@ public class LightBarTransitionsController implements Dumpable, Callbacks, Math.max(0, mTransitionDeferringStartTime - SystemClock.uptimeMillis()), Math.max(0, mTransitionDeferringStartTime - SystemClock.uptimeMillis()), mTransitionDeferringDuration); mTransitionDeferringDuration); } else { } else { animateIconTint(dark ? 1.0f : 0.0f, 0 /* delay */, DEFAULT_TINT_ANIMATION_DURATION); animateIconTint(dark ? 1.0f : 0.0f, 0 /* delay */, getTintAnimationDuration()); } } public long getTintAnimationDuration() { if (NavBarTintController.isEnabled(mContext)) { return Math.max(Settings.Global.getInt(mContext.getContentResolver(), NAV_COLOR_TRANSITION_TIME_SETTING, DEFAULT_TINT_ANIMATION_DURATION), MIN_COLOR_ADAPT_TRANSITION_TIME); } } return DEFAULT_TINT_ANIMATION_DURATION; } } public float getCurrentDarkIntensity() { public float getCurrentDarkIntensity() { Loading
packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java 0 → 100644 +208 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.statusbar.phone; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.Color; import android.graphics.Rect; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.provider.Settings; import android.util.DisplayMetrics; import android.view.SurfaceControl; public class NavBarTintController { public static final String NAV_COLOR_TRANSITION_TIME_SETTING = "navbar_color_adapt_transition"; public static final int MIN_COLOR_ADAPT_TRANSITION_TIME = 400; private final HandlerThread mColorAdaptHandlerThread = new HandlerThread("ColorExtractThread"); private Handler mColorAdaptionHandler; // Poll time for each iteration to color sample private static final int COLOR_ADAPTION_TIMEOUT = 300; // Passing the threshold of this luminance value will make the button black otherwise white private static final float LUMINANCE_THRESHOLD = 0.3f; // The home button's icon is actually smaller than the button's size, the percentage will // cut into the button's size to determine the icon size private static final float PERCENTAGE_BUTTON_PADDING = 0.3f; // The distance from the home button to color sample around private static final int COLOR_SAMPLE_MARGIN = 20; private boolean mRunning; private final NavigationBarView mNavigationBarView; private final LightBarTransitionsController mLightBarController; private final Handler mMainHandler = new Handler(Looper.getMainLooper()); public NavBarTintController(NavigationBarView navigationBarView, LightBarTransitionsController lightBarController) { mNavigationBarView = navigationBarView; mLightBarController = lightBarController; } public void start() { if (!isEnabled(mNavigationBarView.getContext())) { return; } if (mColorAdaptionHandler == null) { mColorAdaptHandlerThread.start(); mColorAdaptionHandler = new Handler(mColorAdaptHandlerThread.getLooper()); } mColorAdaptionHandler.removeCallbacksAndMessages(null); mColorAdaptionHandler.post(this::updateTint); mRunning = true; } public void end() { if (mColorAdaptionHandler != null) { mColorAdaptionHandler.removeCallbacksAndMessages(null); } mRunning = false; } public void stop() { end(); if (mColorAdaptionHandler != null) { mColorAdaptHandlerThread.quitSafely(); } } private void updateTint() { int[] navPos = new int[2]; int[] butPos = new int[2]; if (mNavigationBarView.getHomeButton().getCurrentView() == null) { return; } // Determine the area of the home icon in the larger home button mNavigationBarView.getHomeButton().getCurrentView().getLocationInSurface(butPos); final int navWidth = mNavigationBarView.getHomeButton().getCurrentView().getWidth(); final int navHeight = mNavigationBarView.getHomeButton().getCurrentView().getHeight(); final int xPadding = (int) (PERCENTAGE_BUTTON_PADDING * navWidth); final int yPadding = (int) (PERCENTAGE_BUTTON_PADDING * navHeight); final Rect homeButtonRect = new Rect(butPos[0] + xPadding, butPos[1] + yPadding, navWidth + butPos[0] - xPadding, navHeight + butPos[1] - yPadding); if (mNavigationBarView.getCurrentView() == null || homeButtonRect.isEmpty()) { scheduleColorAdaption(); return; } mNavigationBarView.getCurrentView().getLocationOnScreen(navPos); homeButtonRect.offset(navPos[0], navPos[1]); // Apply a margin area around the button region to sample the colors, crop from screenshot final Rect cropRect = new Rect(homeButtonRect); cropRect.inset(-COLOR_SAMPLE_MARGIN, -COLOR_SAMPLE_MARGIN); if (cropRect.isEmpty()) { scheduleColorAdaption(); return; } // Determine the size of the home area Rect homeArea = new Rect(COLOR_SAMPLE_MARGIN, COLOR_SAMPLE_MARGIN, homeButtonRect.width() + COLOR_SAMPLE_MARGIN, homeButtonRect.height() + COLOR_SAMPLE_MARGIN); // Get the screenshot around the home button icon to determine the color DisplayMetrics mDisplayMetrics = new DisplayMetrics(); mNavigationBarView.getContext().getDisplay().getRealMetrics(mDisplayMetrics); final Bitmap hardBitmap = SurfaceControl .screenshot(new Rect(), mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels, mNavigationBarView.getContext().getDisplay().getRotation()); if (hardBitmap != null && cropRect.bottom <= hardBitmap.getHeight()) { final Bitmap cropBitmap = Bitmap.createBitmap(hardBitmap, cropRect.left, cropRect.top, cropRect.width(), cropRect.height()); final Bitmap softBitmap = cropBitmap.copy(Config.ARGB_8888, false); // Get the luminance value to determine if the home button should be black or white final int[] pixels = new int[softBitmap.getByteCount() / 4]; softBitmap.getPixels(pixels, 0, softBitmap.getWidth(), 0, 0, softBitmap.getWidth(), softBitmap.getHeight()); float r = 0, g = 0, blue = 0; int width = cropRect.width(); int total = 0; for (int i = 0; i < pixels.length; i += 4) { int x = i % width; int y = i / width; if (!homeArea.contains(x, y)) { r += Color.red(pixels[i]); g += Color.green(pixels[i]); blue += Color.blue(pixels[i]); total++; } } r /= total; g /= total; blue /= total; r = Math.max(Math.min(r / 255f, 1), 0); g = Math.max(Math.min(g / 255f, 1), 0); blue = Math.max(Math.min(blue / 255f, 1), 0); if (r <= 0.03928) { r /= 12.92; } else { r = (float) Math.pow((r + 0.055) / 1.055, 2.4); } if (g <= 0.03928) { g /= 12.92; } else { g = (float) Math.pow((g + 0.055) / 1.055, 2.4); } if (blue <= 0.03928) { blue /= 12.92; } else { blue = (float) Math.pow((blue + 0.055) / 1.055, 2.4); } if (r * 0.2126 + g * 0.7152 + blue * 0.0722 > LUMINANCE_THRESHOLD) { // Black mMainHandler.post( () -> mLightBarController .setIconsDark(true /* dark */, true /* animate */)); } else { // White mMainHandler.post( () -> mLightBarController .setIconsDark(false /* dark */, true /* animate */)); } cropBitmap.recycle(); hardBitmap.recycle(); } scheduleColorAdaption(); } private void scheduleColorAdaption() { mColorAdaptionHandler.removeCallbacksAndMessages(null); if (!mRunning || !isEnabled(mNavigationBarView.getContext())) { return; } mColorAdaptionHandler.postDelayed(this::updateTint, COLOR_ADAPTION_TIMEOUT); } public static boolean isEnabled(Context context) { return Settings.Global.getInt(context.getContentResolver(), NavigationPrototypeController.NAV_COLOR_ADAPT_ENABLE_SETTING, 0) == 1; } }
packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +10 −0 Original line number Original line Diff line number Diff line Loading @@ -851,6 +851,16 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback if (Intent.ACTION_SCREEN_OFF.equals(action) if (Intent.ACTION_SCREEN_OFF.equals(action) || Intent.ACTION_SCREEN_ON.equals(action)) { || Intent.ACTION_SCREEN_ON.equals(action)) { notifyNavigationBarScreenOn(); notifyNavigationBarScreenOn(); if (Intent.ACTION_SCREEN_ON.equals(action)) { // Enabled and screen is on, start it again if enabled if (NavBarTintController.isEnabled(getContext())) { mNavigationBarView.getColorAdaptionController().start(); } } else { // Screen off disable it mNavigationBarView.getColorAdaptionController().end(); } } } if (Intent.ACTION_USER_SWITCHED.equals(action)) { if (Intent.ACTION_USER_SWITCHED.equals(action)) { // The accessibility settings may be different for the new user // The accessibility settings may be different for the new user Loading
packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +17 −0 Original line number Original line Diff line number Diff line Loading @@ -149,6 +149,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav private RecentsOnboarding mRecentsOnboarding; private RecentsOnboarding mRecentsOnboarding; private NotificationPanelView mPanelView; private NotificationPanelView mPanelView; private NavBarTintController mColorAdaptionController; private NavigationPrototypeController mPrototypeController; private NavigationPrototypeController mPrototypeController; private NavigationGestureAction[] mDefaultGestureMap; private NavigationGestureAction[] mDefaultGestureMap; private QuickScrubAction mQuickScrubAction; private QuickScrubAction mQuickScrubAction; Loading Loading @@ -277,6 +278,15 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav public void onBackButtonVisibilityChanged(boolean visible) { public void onBackButtonVisibilityChanged(boolean visible) { getBackButton().setVisibility(visible ? VISIBLE : GONE); getBackButton().setVisibility(visible ? VISIBLE : GONE); } } @Override public void onColorAdaptChanged(boolean enabled) { if (enabled) { mColorAdaptionController.start(); } else { mColorAdaptionController.end(); } } }; }; public NavigationBarView(Context context, AttributeSet attrs) { public NavigationBarView(Context context, AttributeSet attrs) { Loading Loading @@ -334,6 +344,11 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav mPrototypeController = new NavigationPrototypeController(mHandler, mContext); mPrototypeController = new NavigationPrototypeController(mHandler, mContext); mPrototypeController.register(); mPrototypeController.register(); mPrototypeController.setOnPrototypeChangedListener(mPrototypeListener); mPrototypeController.setOnPrototypeChangedListener(mPrototypeListener); mColorAdaptionController = new NavBarTintController(this, getLightTransitionsController()); } public NavBarTintController getColorAdaptionController() { return mColorAdaptionController; } } public BarTransitions getBarTransitions() { public BarTransitions getBarTransitions() { Loading Loading @@ -1097,6 +1112,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav Dependency.get(PluginManager.class).addPluginListener(this, Dependency.get(PluginManager.class).addPluginListener(this, NavGesture.class, false /* Only one */); NavGesture.class, false /* Only one */); setUpSwipeUpOnboarding(isQuickStepSwipeUpEnabled()); setUpSwipeUpOnboarding(isQuickStepSwipeUpEnabled()); mColorAdaptionController.start(); } } @Override @Override Loading @@ -1107,6 +1123,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav mGestureHelper.destroy(); mGestureHelper.destroy(); } } mPrototypeController.unregister(); mPrototypeController.unregister(); mColorAdaptionController.stop(); setUpSwipeUpOnboarding(false); setUpSwipeUpOnboarding(false); for (int i = 0; i < mButtonDispatchers.size(); ++i) { for (int i = 0; i < mButtonDispatchers.size(); ++i) { mButtonDispatchers.valueAt(i).onDestroy(); mButtonDispatchers.valueAt(i).onDestroy(); Loading