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

Commit f867e34d authored by James O'Leary's avatar James O'Leary Committed by Android (Google) Code Review
Browse files

Merge "Allow touchable regions for assist UI"

parents 97056bbb cbea84da
Loading
Loading
Loading
Loading
+21 −0
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@ import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.AsyncTask;
import android.os.Binder;
import android.os.Bundle;
@@ -173,6 +174,26 @@ public class AssistManager implements ConfigurationChangedReceiver {
        startAssistInternal(args, assistComponent, isService);
    }

    /**
     * Returns a {@code Rect} containing system UI presented on behalf of the assistant that
     * consumes touches.
     */
    @Nullable
    public Rect getTouchableRegion() {
        // intentional no-op, vendor's AssistManager implementation should override if needed.
        return null;
    }

    /** Registers a listener for changes to system UI presented on behalf of the assistant. */
    public void setAssistSysUiChangeListener(AssistSysUiChangeListener listener) {
        // intentional no-op, vendor's AssistManager implementation should override if needed.
    }

    /** Returns {@code true} if the system UI is showing UI for the assistant. */
    public boolean hasAssistUi() {
        return false;
    }

    public void hideAssist() {
        mAssistUtils.hideCurrentSession();
    }
+27 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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.assist;

/**
 * Used to notify when system UI is showing UI for the assistant.
 */
public interface AssistSysUiChangeListener {

    /** Called when the visibility of system UI for the assistant has changed. */
    void onChange(boolean isVisible);

}
+41 −102
Original line number Diff line number Diff line
@@ -37,7 +37,6 @@ import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.ScreenDecorations;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.statusbar.StatusBarState;
@@ -57,14 +56,14 @@ import java.util.Stack;
 * A implementation of HeadsUpManager for phone and car.
 */
public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
        ViewTreeObserver.OnComputeInternalInsetsListener, VisualStabilityManager.Callback,
        OnHeadsUpChangedListener, ConfigurationController.ConfigurationListener, StateListener {
        VisualStabilityManager.Callback, OnHeadsUpChangedListener,
        ConfigurationController.ConfigurationListener, StateListener {
    private static final String TAG = "HeadsUpManagerPhone";

    private final View mStatusBarWindowView;
    private final NotificationGroupManager mGroupManager;
    private final StatusBar mBar;
    private final VisualStabilityManager mVisualStabilityManager;
    private final StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
    private boolean mReleaseOnExpandFinish;

    private int mStatusBarHeight;
@@ -78,13 +77,9 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
    private boolean mIsExpanded;
    private int[] mTmpTwoArray = new int[2];
    private boolean mHeadsUpGoingAway;
    private boolean mWaitingOnCollapseWhenGoingAway;
    private boolean mBubbleGoingAway;
    private boolean mIsObserving;
    private int mStatusBarState;

    private AnimationStateHandler mAnimationStateHandler;
    private BubbleController mBubbleController = Dependency.get(BubbleController.class);

    private final Pools.Pool<HeadsUpEntryPhone> mEntryPool = new Pools.Pool<HeadsUpEntryPhone>() {
        private Stack<HeadsUpEntryPhone> mPoolObjects = new Stack<>();
@@ -107,14 +102,17 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
    ///////////////////////////////////////////////////////////////////////////////////////////////
    //  Constructor:

    public HeadsUpManagerPhone(@NonNull final Context context, @NonNull View statusBarWindowView,
            @NonNull NotificationGroupManager groupManager, @NonNull StatusBar bar,
    public HeadsUpManagerPhone(@NonNull final Context context,
                               @NonNull View statusBarWindowView,
                               @NonNull NotificationGroupManager groupManager,
                               @NonNull StatusBar bar,
                               @NonNull VisualStabilityManager visualStabilityManager) {
        super(context);

        mStatusBarWindowView = statusBarWindowView;
        mStatusBarTouchableRegionManager = new StatusBarTouchableRegionManager(context, this, bar,
                statusBarWindowView);
        mGroupManager = groupManager;
        mBar = bar;
        mVisualStabilityManager = visualStabilityManager;

        initResources();
@@ -125,16 +123,10 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
                if (Log.isLoggable(TAG, Log.WARN)) {
                    Log.w(TAG, "onHeadsUpPinnedModeChanged");
                }
                updateTouchableRegionListener();
                mStatusBarTouchableRegionManager.updateTouchableRegion();
            }
        });
        Dependency.get(StatusBarStateController.class).addCallback(this);
        mBubbleController.setBubbleStateChangeListener((hasBubbles) -> {
            if (!hasBubbles) {
                mBubbleGoingAway = true;
            }
            updateTouchableRegionListener();
        });
    }

    public void setAnimationStateHandler(AnimationStateHandler handler) {
@@ -209,14 +201,10 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
        if (isExpanded != mIsExpanded) {
            mIsExpanded = isExpanded;
            if (isExpanded) {
                // make sure our state is sane
                mWaitingOnCollapseWhenGoingAway = false;
                mHeadsUpGoingAway = false;
                updateTouchableRegionListener();
            }
            if (mBubbleController.hasBubbles() || !mIsExpanded) {
                updateTouchableRegionListener();
            }
            mStatusBarTouchableRegionManager.setIsStatusBarExpanded(isExpanded);
            mStatusBarTouchableRegionManager.updateTouchableRegion();
        }
    }

@@ -233,14 +221,20 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
        if (headsUpGoingAway != mHeadsUpGoingAway) {
            mHeadsUpGoingAway = headsUpGoingAway;
            if (!headsUpGoingAway) {
                waitForStatusBarLayout();
                mStatusBarTouchableRegionManager.updateTouchableRegionAfterLayout();
            } else {
                mStatusBarTouchableRegionManager.updateTouchableRegion();
            }
        }
            updateTouchableRegionListener();
    }

    public boolean isHeadsUpGoingAway() {
        return mHeadsUpGoingAway;
    }

    /**
     * Notifies that a remote input textbox in notification gets active or inactive.
     *
     * @param entry             The entry of the target notification.
     * @param remoteInputActive True to notify active, False to notify inactive.
     */
@@ -295,23 +289,23 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
        dumpInternal(fd, pw, args);
    }

    ///////////////////////////////////////////////////////////////////////////////////////////////
    //  ViewTreeObserver.OnComputeInternalInsetsListener overrides:

    /**
     * Overridden from TreeObserver.
     * Update touch insets to include any area needed for touching a heads up notification.
     *
     * @param info Insets that will include heads up notification touch area after execution.
     */
    @Override
    public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
        if (mIsExpanded || mBar.isBouncerShowing()) {
            // The touchable region is always the full area when expanded
            return;
        }
        if (hasPinnedHeadsUp()) {
    @Nullable
    public void updateTouchableRegion(ViewTreeObserver.InternalInsetsInfo info) {
        info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);

        if (!hasPinnedHeadsUp()) {
            info.touchableRegion.set(0, 0, mStatusBarWindowView.getWidth(), mStatusBarHeight);
            updateRegionForNotch(info.touchableRegion);
        } else {
            NotificationEntry topEntry = getTopEntry();
            if (topEntry.isChildInGroup()) {
                final NotificationEntry groupSummary
                        = mGroupManager.getGroupSummary(topEntry.notification);
                final NotificationEntry groupSummary =
                        mGroupManager.getGroupSummary(topEntry.notification);
                if (groupSummary != null) {
                    topEntry = groupSummary;
                }
@@ -321,23 +315,8 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
            int minX = mTmpTwoArray[0];
            int maxX = mTmpTwoArray[0] + topRow.getWidth();
            int height = topRow.getIntrinsicHeight();

            info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
            info.touchableRegion.set(minX, 0, maxX, mHeadsUpInset + height);
        } else {
            setCollapsedTouchableInsets(info);
        }
        Rect r = mBubbleController.getTouchableRegion();
        if (r != null) {
            info.touchableRegion.union(r);
        }
        mBubbleGoingAway = false;
    }

    private void setCollapsedTouchableInsets(ViewTreeObserver.InternalInsetsInfo info) {
        info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
        info.touchableRegion.set(0, 0, mStatusBarWindowView.getWidth(), mStatusBarHeight);
        updateRegionForNotch(info.touchableRegion);
    }

    private void updateRegionForNotch(Region region) {
@@ -364,9 +343,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,

    @Override
    public void onConfigChanged(Configuration newConfig) {
        Resources resources = mContext.getResources();
        mStatusBarHeight = resources.getDimensionPixelSize(
                com.android.internal.R.dimen.status_bar_height);
        initResources();
    }

    ///////////////////////////////////////////////////////////////////////////////////////////////
@@ -408,7 +385,8 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
    @Override
    protected void dumpInternal(FileDescriptor fd, PrintWriter pw, String[] args) {
        super.dumpInternal(fd, pw, args);
        pw.print("  mBarState="); pw.println(mStatusBarState);
        pw.print("  mBarState=");
        pw.println(mStatusBarState);
    }

    ///////////////////////////////////////////////////////////////////////////////////////////////
@@ -438,45 +416,6 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
        return headsUpEntry == null || headsUpEntry != topEntry || super.canRemoveImmediately(key);
    }

    /**
     * We need to wait on the whole panel to collapse, before we can remove the touchable region
     * listener.
     */
    private void waitForStatusBarLayout() {
        mWaitingOnCollapseWhenGoingAway = true;
        mStatusBarWindowView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
            @Override
            public void onLayoutChange(View v, int left, int top, int right, int bottom,
                    int oldLeft,
                    int oldTop, int oldRight, int oldBottom) {
                if (mStatusBarWindowView.getHeight() <= mStatusBarHeight) {
                    mStatusBarWindowView.removeOnLayoutChangeListener(this);
                    mWaitingOnCollapseWhenGoingAway = false;
                    updateTouchableRegionListener();
                }
            }
        });
    }

    // TODO: some kind of TouchableRegionManager to deal with this, HeadsUpManager is not really
    // the right place
    private void updateTouchableRegionListener() {
        boolean shouldObserve = hasPinnedHeadsUp() || mHeadsUpGoingAway
                || mBubbleController.hasBubbles() || mBubbleGoingAway
                || mWaitingOnCollapseWhenGoingAway
                || mStatusBarWindowView.getRootWindowInsets().getDisplayCutout() != null;
        if (shouldObserve == mIsObserving) {
            return;
        }
        if (shouldObserve) {
            mStatusBarWindowView.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
            mStatusBarWindowView.requestLayout();
        } else {
            mStatusBarWindowView.getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
        }
        mIsObserving = shouldObserve;
    }

    ///////////////////////////////////////////////////////////////////////////////////////////////
    //  HeadsUpEntryPhone:

+173 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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.annotation.NonNull;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;

import com.android.systemui.Dependency;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;

/**
 * Manages what parts of the status bar are touchable. Clients are primarily UI that displays in the
 * status bar even though the UI doesn't look like part of the status bar.
 */
public final class StatusBarTouchableRegionManager implements
        OnComputeInternalInsetsListener, ConfigurationListener {

    private final AssistManager mAssistManager = Dependency.get(AssistManager.class);
    private final BubbleController mBubbleController = Dependency.get(BubbleController.class);
    private final Context mContext;
    private final HeadsUpManagerPhone mHeadsUpManager;
    private boolean mIsStatusBarExpanded = false;
    private boolean mShouldAdjustInsets = false;
    private final StatusBar mStatusBar;
    private int mStatusBarHeight;
    private final View mStatusBarWindowView;
    private boolean mForceCollapsedUntilLayout = false;

    public StatusBarTouchableRegionManager(@NonNull Context context,
                                           HeadsUpManagerPhone headsUpManager,
                                           @NonNull StatusBar statusBar,
                                           @NonNull View statusBarWindowView) {
        mContext = context;
        mHeadsUpManager = headsUpManager;
        mStatusBar = statusBar;
        mStatusBarWindowView = statusBarWindowView;

        initResources();

        mAssistManager.setAssistSysUiChangeListener((isVisible) -> {
            updateTouchableRegion();
        });
        mBubbleController.setBubbleStateChangeListener((hasBubbles) -> {
            updateTouchableRegion();
        });
        Dependency.get(ConfigurationController.class).addCallback(this);
    }

    /**
     * Set the touchable portion of the status bar based on what elements are visible.
     */
    public void updateTouchableRegion() {
        boolean hasCutoutInset = (mStatusBarWindowView != null)
                && (mStatusBarWindowView.getRootWindowInsets() != null)
                && (mStatusBarWindowView.getRootWindowInsets().getDisplayCutout() != null);
        boolean shouldObserve =
                mHeadsUpManager.hasPinnedHeadsUp() || mHeadsUpManager.isHeadsUpGoingAway()
                        || mBubbleController.hasBubbles()
                        || mAssistManager.hasAssistUi()
                        || mForceCollapsedUntilLayout
                        || hasCutoutInset;
        if (shouldObserve == mShouldAdjustInsets) {
            return;
        }

        if (shouldObserve) {
            mStatusBarWindowView.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
            mStatusBarWindowView.requestLayout();
        } else {
            mStatusBarWindowView.getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
        }
        mShouldAdjustInsets = shouldObserve;
    }

    /**
     * Calls {@code updateTouchableRegion()} after a layout pass completes.
     */
    public void updateTouchableRegionAfterLayout() {
        mForceCollapsedUntilLayout = true;
        mStatusBarWindowView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
            @Override
            public void onLayoutChange(View v, int left, int top, int right, int bottom,
                                       int oldLeft,
                                       int oldTop, int oldRight, int oldBottom) {
                if (mStatusBarWindowView.getHeight() <= mStatusBarHeight) {
                    mStatusBarWindowView.removeOnLayoutChangeListener(this);
                    mForceCollapsedUntilLayout = false;
                    updateTouchableRegion();
                }
            }
        });
    }

    /**
     * Notify that the status bar panel gets expanded or collapsed.
     *
     * @param isExpanded True to notify expanded, false to notify collapsed.
     */
    public void setIsStatusBarExpanded(boolean isExpanded) {
        if (isExpanded != mIsStatusBarExpanded) {
            mIsStatusBarExpanded = isExpanded;
            if (isExpanded) {
                // make sure our state is sane
                mForceCollapsedUntilLayout = false;
            }
            updateTouchableRegion();
        }
    }

    @Override
    public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
        if (mIsStatusBarExpanded || mStatusBar.isBouncerShowing()) {
            // The touchable region is always the full area when expanded
            return;
        }

        mHeadsUpManager.updateTouchableRegion(info);

        Rect bubbleRect = mBubbleController.getTouchableRegion();
        if (bubbleRect != null) {
            info.touchableRegion.union(bubbleRect);
        }

        Rect assistRect = mAssistManager.getTouchableRegion();
        if (assistRect != null) {
            info.touchableRegion.union(assistRect);
        }
    }

    @Override
    public void onConfigChanged(Configuration newConfig) {
        initResources();
    }

    @Override
    public void onDensityOrFontScaleChanged() {
        initResources();
    }

    @Override
    public void onOverlayChanged() {
        initResources();
    }

    private void initResources() {
        Resources resources = mContext.getResources();
        mStatusBarHeight = resources.getDimensionPixelSize(
                com.android.internal.R.dimen.status_bar_height);
    }
}