Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3f35db5f authored by Vinit Nayak's avatar Vinit Nayak
Browse files

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
parent 115eead2
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -295,6 +295,7 @@ public class EdgeBackGestureHandler implements DisplayListener {
                            return mSamplingRect;
                        }
                    });
            mRegionSamplingHelper.setWindowVisible(true);
        }
    }

+0 −210
Original line number 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 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);
    }
}
+1 −10
Original line number Diff line number Diff line
@@ -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
+6 −4
Original line number Diff line number Diff line
@@ -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;
+70 −27
Original line number Diff line number Diff line
@@ -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);

        mNavColorSampleMargin =
                getResources()
                        .getDimensionPixelSize(R.dimen.navigation_handle_sample_horizontal_margin);

        mEdgeBackGestureHandler = new EdgeBackGestureHandler(context, mOverviewProxyService);
        mTintController = new NavBarTintController(this, getLightTransitionsController());
        mRegionSamplingHelper = new RegionSamplingHelper(this,
                new RegionSamplingHelper.SamplingCallback() {
                    @Override
                    public void onRegionDarknessChanged(boolean isRegionDark) {
                        getLightTransitionsController().setIconsDark(!isRegionDark ,
                                true /* animate */);
                    }

    public NavBarTintController getTintController() {
        return mTintController;
                    @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);
    }

Loading