Migrate NavigationBarView to use RegionSamplingHelper

RegionSamplingHelper was a dupe of NavBarTintController.
Consolidate, extend, and migrate to use
RegionSamplingHelper going forward.

fixes: 138601292
Test: Tested phone with apps that have both light and
dark backgrounds, tested locking/unlocking, tested
livetime dynamic changes with the camera app with light
and dark backgrounds, tested orientation changes

Change-Id: Icbc34cdac5fce0befd0dfbde14e47c73fe22ed6e
This commit is contained in:
Vinit Nayak
2019-08-08 17:31:48 -07:00
parent 115eead20a
commit 3f35db5fd7
8 changed files with 118 additions and 257 deletions

View File

@@ -295,6 +295,7 @@ public class EdgeBackGestureHandler implements DisplayListener {
return mSamplingRect;
}
});
mRegionSamplingHelper.setWindowVisible(true);
}
}

View File

@@ -1,210 +0,0 @@
/*
* 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 static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Handler;
import android.view.CompositionSamplingListener;
import android.view.View;
import com.android.systemui.R;
import com.android.systemui.shared.system.QuickStepContract;
import java.io.PrintWriter;
/**
* Updates the nav bar tint based on the color of the content behind the nav bar.
*/
public class NavBarTintController implements View.OnAttachStateChangeListener,
View.OnLayoutChangeListener {
public static final int MIN_COLOR_ADAPT_TRANSITION_TIME = 400;
public static final int DEFAULT_COLOR_ADAPT_TRANSITION_TIME = 1700;
private final Handler mHandler = new Handler();
private final NavigationBarView mNavigationBarView;
private final LightBarTransitionsController mLightBarController;
private int mNavBarMode = NAV_BAR_MODE_3BUTTON;
private boolean mWindowVisible;
private final CompositionSamplingListener mSamplingListener;
private final Runnable mUpdateSamplingListener = this::updateSamplingListener;
private final Rect mSamplingBounds = new Rect();
private boolean mSamplingEnabled = false;
private boolean mSamplingListenerRegistered = false;
private float mLastMedianLuma;
private float mCurrentMedianLuma;
private boolean mUpdateOnNextDraw;
private final int mNavBarHeight;
private final int mNavColorSampleMargin;
// Passing the threshold of this luminance value will make the button black otherwise white
private final float mLuminanceThreshold;
private final float mLuminanceChangeThreshold;
public NavBarTintController(NavigationBarView navigationBarView,
LightBarTransitionsController lightBarController) {
mSamplingListener = new CompositionSamplingListener(
navigationBarView.getContext().getMainExecutor()) {
@Override
public void onSampleCollected(float medianLuma) {
updateTint(medianLuma);
}
};
mNavigationBarView = navigationBarView;
mNavigationBarView.addOnAttachStateChangeListener(this);
mNavigationBarView.addOnLayoutChangeListener(this);
mLightBarController = lightBarController;
final Resources res = navigationBarView.getResources();
mNavBarHeight = res.getDimensionPixelSize(R.dimen.navigation_bar_height);
mNavColorSampleMargin =
res.getDimensionPixelSize(R.dimen.navigation_handle_sample_horizontal_margin);
mLuminanceThreshold = res.getFloat(R.dimen.navigation_luminance_threshold);
mLuminanceChangeThreshold = res.getFloat(R.dimen.navigation_luminance_change_threshold);
}
void onDraw() {
if (mUpdateOnNextDraw) {
mUpdateOnNextDraw = false;
requestUpdateSamplingListener();
}
}
void start() {
if (!isEnabled(mNavigationBarView.getContext(), mNavBarMode)) {
return;
}
mSamplingEnabled = true;
// Defer calling updateSamplingListener since we may have just reinflated prior to this
requestUpdateSamplingListener();
}
void stop() {
mSamplingEnabled = false;
requestUpdateSamplingListener();
}
@Override
public void onViewAttachedToWindow(View view) {
requestUpdateSamplingListener();
}
@Override
public void onViewDetachedFromWindow(View view) {
// Defer calling updateSamplingListener the attach info has not yet been reset
requestUpdateSamplingListener();
}
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
mSamplingBounds.setEmpty();
// TODO: Extend this to 2/3 button layout as well
View view = mNavigationBarView.getHomeHandle().getCurrentView();
if (view != null) {
int[] pos = new int[2];
view.getLocationOnScreen(pos);
Point displaySize = new Point();
view.getContext().getDisplay().getRealSize(displaySize);
final Rect samplingBounds = new Rect(pos[0] - mNavColorSampleMargin,
displaySize.y - mNavBarHeight, pos[0] + view.getWidth() + mNavColorSampleMargin,
displaySize.y);
if (!samplingBounds.equals(mSamplingBounds)) {
mSamplingBounds.set(samplingBounds);
requestUpdateSamplingListener();
}
}
}
private void requestUpdateSamplingListener() {
mHandler.removeCallbacks(mUpdateSamplingListener);
mHandler.post(mUpdateSamplingListener);
}
private void updateSamplingListener() {
if (mSamplingListenerRegistered) {
mSamplingListenerRegistered = false;
CompositionSamplingListener.unregister(mSamplingListener);
}
if (mSamplingEnabled && mWindowVisible && !mSamplingBounds.isEmpty()
&& mNavigationBarView.isAttachedToWindow()) {
if (!mNavigationBarView.getViewRootImpl().getSurfaceControl().isValid()) {
// The view may still be attached, but the surface backing the window can be
// destroyed, so wait until the next draw to update the listener again
mUpdateOnNextDraw = true;
return;
}
mSamplingListenerRegistered = true;
CompositionSamplingListener.register(mSamplingListener, DEFAULT_DISPLAY,
mNavigationBarView.getViewRootImpl().getSurfaceControl(),
mSamplingBounds);
}
}
private void updateTint(float medianLuma) {
mLastMedianLuma = medianLuma;
// If the difference between the new luma and the current luma is larger than threshold
// then apply the current luma, this is to prevent small changes causing colors to flicker
if (Math.abs(mCurrentMedianLuma - mLastMedianLuma) > mLuminanceChangeThreshold) {
if (medianLuma > mLuminanceThreshold) {
// Black
mLightBarController.setIconsDark(true /* dark */, true /* animate */);
} else {
// White
mLightBarController.setIconsDark(false /* dark */, true /* animate */);
}
mCurrentMedianLuma = medianLuma;
}
}
public void setWindowVisible(boolean visible) {
mWindowVisible = visible;
requestUpdateSamplingListener();
}
public void onNavigationModeChanged(int mode) {
mNavBarMode = mode;
}
void dump(PrintWriter pw) {
pw.println("NavBarTintController:");
pw.println(" navBar isAttached: " + mNavigationBarView.isAttachedToWindow());
pw.println(" navBar isScValid: " + (mNavigationBarView.isAttachedToWindow()
? mNavigationBarView.getViewRootImpl().getSurfaceControl().isValid()
: "false"));
pw.println(" mSamplingListenerRegistered: " + mSamplingListenerRegistered);
pw.println(" mSamplingBounds: " + mSamplingBounds);
pw.println(" mLastMedianLuma: " + mLastMedianLuma);
pw.println(" mCurrentMedianLuma: " + mCurrentMedianLuma);
pw.println(" mWindowVisible: " + mWindowVisible);
}
public static boolean isEnabled(Context context, int navBarMode) {
return context.getDisplayId() == DEFAULT_DISPLAY
&& QuickStepContract.isGesturalMode(navBarMode);
}
}

View File

@@ -1063,16 +1063,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
if (Intent.ACTION_SCREEN_OFF.equals(action)
|| Intent.ACTION_SCREEN_ON.equals(action)) {
notifyNavigationBarScreenOn();
if (Intent.ACTION_SCREEN_ON.equals(action)) {
// Enabled and screen is on, start it again if enabled
if (NavBarTintController.isEnabled(getContext(), mNavBarMode)) {
mNavigationBarView.getTintController().start();
}
} else {
// Screen off disable it
mNavigationBarView.getTintController().stop();
}
mNavigationBarView.onScreenStateChanged(Intent.ACTION_SCREEN_ON.equals(action));
}
if (Intent.ACTION_USER_SWITCHED.equals(action)) {
// The accessibility settings may be different for the new user

View File

@@ -18,8 +18,7 @@ package com.android.systemui.statusbar.phone;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
import static com.android.systemui.statusbar.phone.NavBarTintController.DEFAULT_COLOR_ADAPT_TRANSITION_TIME;
import static com.android.systemui.statusbar.phone.NavBarTintController.MIN_COLOR_ADAPT_TRANSITION_TIME;
import static com.android.systemui.util.Utils.isGesturalModeOnDefaultDisplay;
import android.content.Context;
import android.graphics.Rect;
@@ -42,6 +41,9 @@ import java.util.List;
public final class NavigationBarTransitions extends BarTransitions implements
LightBarTransitionsController.DarkIntensityApplier {
public static final int MIN_COLOR_ADAPT_TRANSITION_TIME = 400;
public static final int DEFAULT_COLOR_ADAPT_TRANSITION_TIME = 1700;
/**
* Notified when the color of nav bar elements changes.
*/
@@ -124,7 +126,7 @@ public final class NavigationBarTransitions extends BarTransitions implements
@Override
public void setAutoDim(boolean autoDim) {
// Ensure we aren't in gestural nav if we are triggering auto dim
if (autoDim && NavBarTintController.isEnabled(mView.getContext(), mNavBarMode)) return;
if (autoDim && isGesturalModeOnDefaultDisplay(mView.getContext(), mNavBarMode)) return;
if (mAutoDim == autoDim) return;
mAutoDim = autoDim;
applyLightsOut(true, false);
@@ -201,7 +203,7 @@ public final class NavigationBarTransitions extends BarTransitions implements
@Override
public int getTintAnimationDuration() {
if (NavBarTintController.isEnabled(mView.getContext(), mNavBarMode)) {
if (isGesturalModeOnDefaultDisplay(mView.getContext(), mNavBarMode)) {
return Math.max(DEFAULT_COLOR_ADAPT_TRANSITION_TIME, MIN_COLOR_ADAPT_TRANSITION_TIME);
}
return LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION;

View File

@@ -16,8 +16,6 @@
package com.android.systemui.statusbar.phone;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
@@ -26,6 +24,7 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_S
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SEARCH_DISABLED;
import static com.android.systemui.shared.system.QuickStepContract.isGesturalMode;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
import static com.android.systemui.util.Utils.isGesturalModeOnDefaultDisplay;
import android.animation.LayoutTransition;
import android.animation.LayoutTransition.TransitionListener;
@@ -89,6 +88,8 @@ public class NavigationBarView extends FrameLayout implements
final static boolean SLIPPERY_WHEN_DISABLED = true;
final static boolean ALTERNATE_CAR_MODE_UI = false;
private final RegionSamplingHelper mRegionSamplingHelper;
private final int mNavColorSampleMargin;
View mCurrentView = null;
private View mVertical;
@@ -101,7 +102,7 @@ public class NavigationBarView extends FrameLayout implements
boolean mLongClickableAccessibilityButton;
int mDisabledFlags = 0;
int mNavigationIconHints = 0;
private int mNavBarMode = NAV_BAR_MODE_3BUTTON;
private int mNavBarMode;
private Rect mHomeButtonBounds = new Rect();
private Rect mBackButtonBounds = new Rect();
@@ -143,14 +144,13 @@ public class NavigationBarView extends FrameLayout implements
private FloatingRotationButton mFloatingRotationButton;
private RotationButtonController mRotationButtonController;
private NavBarTintController mTintController;
/**
* Helper that is responsible for showing the right toast when a disallowed activity operation
* occurred. In pinned mode, we show instructions on how to break out of this mode, whilst in
* fully locked mode we only show that unlocking is blocked.
*/
private ScreenPinningNotify mScreenPinningNotify;
private Rect mSamplingBounds = new Rect();
private class NavTransitionListener implements TransitionListener {
private boolean mBackTransitioning;
@@ -305,12 +305,30 @@ public class NavigationBarView extends FrameLayout implements
mButtonDispatchers.put(R.id.menu_container, mContextualButtonGroup);
mDeadZone = new DeadZone(this);
mEdgeBackGestureHandler = new EdgeBackGestureHandler(context, mOverviewProxyService);
mTintController = new NavBarTintController(this, getLightTransitionsController());
}
mNavColorSampleMargin =
getResources()
.getDimensionPixelSize(R.dimen.navigation_handle_sample_horizontal_margin);
public NavBarTintController getTintController() {
return mTintController;
mEdgeBackGestureHandler = new EdgeBackGestureHandler(context, mOverviewProxyService);
mRegionSamplingHelper = new RegionSamplingHelper(this,
new RegionSamplingHelper.SamplingCallback() {
@Override
public void onRegionDarknessChanged(boolean isRegionDark) {
getLightTransitionsController().setIconsDark(!isRegionDark ,
true /* animate */);
}
@Override
public Rect getSampledRegion(View sampledView) {
updateSamplingRect();
return mSamplingBounds;
}
@Override
public boolean isSamplingEnabled() {
return isGesturalModeOnDefaultDisplay(getContext(), mNavBarMode);
}
});
}
public NavigationBarTransitions getBarTransitions() {
@@ -326,12 +344,6 @@ public class NavigationBarView extends FrameLayout implements
updatePanelSystemUiStateFlags();
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
mTintController.onDraw();
}
public void setOnVerticalChangedListener(OnVerticalChangedListener onVerticalChangedListener) {
mOnVerticalChangedListener = onVerticalChangedListener;
notifyVerticalChangedListener(mIsVertical);
@@ -352,10 +364,10 @@ public class NavigationBarView extends FrameLayout implements
if (newMode == MODE_OPAQUE) {
// If the nav bar background is opaque, stop auto tinting since we know the icons are
// showing over a dark background
mTintController.stop();
mRegionSamplingHelper.stop();
getLightTransitionsController().setIconsDark(false /* dark */, true /* animate */);
} else {
mTintController.start();
mRegionSamplingHelper.start(mSamplingBounds);
}
}
@@ -535,8 +547,19 @@ public class NavigationBarView extends FrameLayout implements
return KeyButtonDrawable.create(mContext, icon, hasShadow);
}
/** To be called when screen lock/unlock state changes */
public void onScreenStateChanged(boolean isScreenOn) {
if (isScreenOn) {
if (isGesturalModeOnDefaultDisplay(getContext(), mNavBarMode)) {
mRegionSamplingHelper.start(mSamplingBounds);
}
} else {
mRegionSamplingHelper.stop();
}
}
public void setWindowVisible(boolean visible) {
mTintController.setWindowVisible(visible);
mRegionSamplingHelper.setWindowVisible(visible);
mRotationButtonController.onNavigationBarWindowVisibilityChange(visible);
}
@@ -794,13 +817,7 @@ public class NavigationBarView extends FrameLayout implements
mRecentsOnboarding.onNavigationModeChanged(mNavBarMode);
getRotateSuggestionButton().onNavigationModeChanged(mNavBarMode);
// Color adaption is tied with showing home handle, only avaliable if visible
mTintController.onNavigationModeChanged(mNavBarMode);
if (isGesturalMode(mNavBarMode)) {
mTintController.start();
} else {
mTintController.stop();
}
mRegionSamplingHelper.start(mSamplingBounds);
}
public void setAccessibilityButtonState(final boolean visible, final boolean longClickable) {
@@ -831,6 +848,24 @@ public class NavigationBarView extends FrameLayout implements
super.onDraw(canvas);
}
private void updateSamplingRect() {
mSamplingBounds.setEmpty();
// TODO: Extend this to 2/3 button layout as well
View view = getHomeHandle().getCurrentView();
if (view != null) {
int[] pos = new int[2];
view.getLocationOnScreen(pos);
Point displaySize = new Point();
view.getContext().getDisplay().getRealSize(displaySize);
final Rect samplingBounds = new Rect(pos[0] - mNavColorSampleMargin,
displaySize.y - getNavBarHeight(),
pos[0] + view.getWidth() + mNavColorSampleMargin,
displaySize.y);
mSamplingBounds.set(samplingBounds);
}
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
@@ -979,6 +1014,14 @@ public class NavigationBarView extends FrameLayout implements
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
private int getNavBarHeight() {
return mIsVertical
? getResources().getDimensionPixelSize(
com.android.internal.R.dimen.navigation_bar_height_landscape)
: getResources().getDimensionPixelSize(
com.android.internal.R.dimen.navigation_bar_height);
}
private void notifyVerticalChangedListener(boolean newVertical) {
if (mOnVerticalChangedListener != null) {
mOnVerticalChangedListener.onVerticalChanged(newVertical);
@@ -1125,7 +1168,7 @@ public class NavigationBarView extends FrameLayout implements
mContextualButtonGroup.dump(pw);
mRecentsOnboarding.dump(pw);
mTintController.dump(pw);
mRegionSamplingHelper.dump(pw);
mEdgeBackGestureHandler.dump(pw);
}

View File

@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.phone;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
import static android.view.Display.DEFAULT_DISPLAY;
import android.annotation.IntDef;
import android.content.ComponentCallbacks;
@@ -29,8 +29,6 @@ import android.net.Uri;
import android.os.Handler;
import android.provider.Settings;
import com.android.systemui.shared.system.QuickStepContract;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -120,8 +118,7 @@ public class NavigationPrototypeController extends ContentObserver implements Co
} else if (path.endsWith(HIDE_HOME_BUTTON_SETTING)) {
mListener.onHomeButtonVisibilityChanged(!hideHomeButton());
} else if (path.endsWith(NAV_COLOR_ADAPT_ENABLE_SETTING)) {
mListener.onColorAdaptChanged(
NavBarTintController.isEnabled(mContext, NAV_BAR_MODE_GESTURAL));
mListener.onColorAdaptChanged(mContext.getDisplayId() == DEFAULT_DISPLAY);
} else if (path.endsWith(SHOW_HOME_HANDLE_SETTING)) {
mListener.onHomeHandleVisiblilityChanged(showHomeHandle());
} else if (path.endsWith(ENABLE_ASSISTANT_GESTURE)) {

View File

@@ -29,6 +29,8 @@ import android.view.ViewTreeObserver;
import com.android.systemui.R;
import java.io.PrintWriter;
/**
* A helper class to sample regions on the screen and inspect its luminosity.
*/
@@ -62,6 +64,7 @@ public class RegionSamplingHelper implements View.OnAttachStateChangeListener,
private final float mLuminanceThreshold;
private final float mLuminanceChangeThreshold;
private boolean mFirstSamplingAfterStart;
private boolean mWindowVisible;
private SurfaceControl mRegisteredStopLayer = null;
private ViewTreeObserver.OnDrawListener mUpdateOnDraw = new ViewTreeObserver.OnDrawListener() {
@Override
@@ -148,7 +151,9 @@ public class RegionSamplingHelper implements View.OnAttachStateChangeListener,
}
private void updateSamplingListener() {
boolean isSamplingEnabled = mSamplingEnabled && !mSamplingRequestBounds.isEmpty()
boolean isSamplingEnabled = mSamplingEnabled
&& !mSamplingRequestBounds.isEmpty()
&& mWindowVisible
&& (mSampledView.isAttachedToWindow() || mFirstSamplingAfterStart);
if (isSamplingEnabled) {
ViewRootImpl viewRootImpl = mSampledView.getViewRootImpl();
@@ -216,6 +221,24 @@ public class RegionSamplingHelper implements View.OnAttachStateChangeListener,
}
}
void setWindowVisible(boolean visible) {
mWindowVisible = visible;
updateSamplingListener();
}
void dump(PrintWriter pw) {
pw.println("RegionSamplingHelper:");
pw.println(" sampleView isAttached: " + mSampledView.isAttachedToWindow());
pw.println(" sampleView isScValid: " + (mSampledView.isAttachedToWindow()
? mSampledView.getViewRootImpl().getSurfaceControl().isValid()
: "false"));
pw.println(" mSamplingListenerRegistered: " + mSamplingListenerRegistered);
pw.println(" mSamplingRequestBounds: " + mSamplingRequestBounds);
pw.println(" mLastMedianLuma: " + mLastMedianLuma);
pw.println(" mCurrentMedianLuma: " + mCurrentMedianLuma);
pw.println(" mWindowVisible: " + mWindowVisible);
}
public interface SamplingCallback {
/**
* Called when the darkness of the sampled region changes

View File

@@ -14,12 +14,16 @@
package com.android.systemui.util;
import static android.view.Display.DEFAULT_DISPLAY;
import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.view.View;
import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.CommandQueue;
import java.util.List;
@@ -110,4 +114,14 @@ public class Utils {
return pm.queryIntentActivities(homeIntent, 0).isEmpty();
}
/**
* Returns {@code true} if the navMode is that of
* {@link android.view.WindowManagerPolicyConstants#NAV_BAR_MODE_GESTURAL} AND
* the context is that of the default display
*/
public static boolean isGesturalModeOnDefaultDisplay(Context context, int navMode) {
return context.getDisplayId() == DEFAULT_DISPLAY
&& QuickStepContract.isGesturalMode(navMode);
}
}