/*
|
* 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();
|
|
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()
|
|| 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);
|
}
|
}
|
|
@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);
|
}
|
}
|