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

Commit 81cb088a authored by Vinit Nayak's avatar Vinit Nayak Committed by Android (Google) Code Review
Browse files

Merge "Migrate NavigationBarView to use RegionSamplingHelper"

parents f0631bdb 3f35db5f
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -297,6 +297,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);
    }

@@ -799,13 +822,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) {
@@ -836,6 +853,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);
@@ -984,6 +1019,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);
@@ -1130,7 +1173,7 @@ public class NavigationBarView extends FrameLayout implements

        mContextualButtonGroup.dump(pw);
        mRecentsOnboarding.dump(pw);
        mTintController.dump(pw);
        mRegionSamplingHelper.dump(pw);
        mEdgeBackGestureHandler.dump(pw);
    }

Loading