diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java index ff68d2be087ef..97a277b5236a0 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java @@ -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(); } diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistSysUiChangeListener.java b/packages/SystemUI/src/com/android/systemui/assist/AssistSysUiChangeListener.java new file mode 100644 index 0000000000000..d03afb6e4ebd1 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistSysUiChangeListener.java @@ -0,0 +1,27 @@ +/* + * 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); + +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java index de0e194ef90c0..9844d8e5a67ad 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java @@ -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 mEntryPool = new Pools.Pool() { private Stack 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, - @NonNull VisualStabilityManager visualStabilityManager) { + 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,15 +221,21 @@ 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 entry The entry of the target notification. * @param remoteInputActive True to notify active, False to notify inactive. */ public void setRemoteInputActive( @@ -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(); } /////////////////////////////////////////////////////////////////////////////////////////////// @@ -401,14 +378,15 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, @Override protected boolean shouldHeadsUpBecomePinned(NotificationEntry entry) { - return mStatusBarState != StatusBarState.KEYGUARD && !mIsExpanded - || super.shouldHeadsUpBecomePinned(entry); + return mStatusBarState != StatusBarState.KEYGUARD && !mIsExpanded + || super.shouldHeadsUpBecomePinned(entry); } @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: @@ -490,7 +429,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, } public void setEntry(@NonNull final NotificationEntry entry) { - Runnable removeHeadsUpRunnable = () -> { + Runnable removeHeadsUpRunnable = () -> { if (!mVisualStabilityManager.isReorderingAllowed()) { mEntriesToRemoveWhenReorderingAllowed.add(entry); mVisualStabilityManager.addReorderingAllowedCallback( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java new file mode 100644 index 0000000000000..603c969a9de9c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java @@ -0,0 +1,173 @@ +/* + * 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); + } +}