21970ccd29
- Override our insets in LauncherRootView to explicitly only care about nav bar size, ignoring any insets due to taskbar. - Previously we used nonOverlappingTaskbarInsets to belatedly subtract from measurements in e.g. DeviceProfile, but now we can revert most of those calculations since we effectively subtract taskbar insets at the root. Test: visual in different orientations and navigation modes, and testPressHomeOnAllAppsContextMenu to ensure REQUEST_WINDOW_INSETS still works for automated tests Fixes: 200607741 Change-Id: I8de5a268c686a1354b4beaa30e101bab6bed5af9
202 lines
7.2 KiB
Java
202 lines
7.2 KiB
Java
package com.android.launcher3;
|
|
|
|
import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
|
|
|
|
import android.annotation.TargetApi;
|
|
import android.content.Context;
|
|
import android.content.res.Resources;
|
|
import android.graphics.Canvas;
|
|
import android.graphics.Insets;
|
|
import android.graphics.Rect;
|
|
import android.os.Build;
|
|
import android.util.AttributeSet;
|
|
import android.view.ViewDebug;
|
|
import android.view.WindowInsets;
|
|
|
|
import androidx.annotation.RequiresApi;
|
|
|
|
import com.android.launcher3.graphics.SysUiScrim;
|
|
import com.android.launcher3.statemanager.StatefulActivity;
|
|
import com.android.launcher3.uioverrides.ApiWrapper;
|
|
|
|
import java.util.Collections;
|
|
import java.util.List;
|
|
|
|
public class LauncherRootView extends InsettableFrameLayout {
|
|
|
|
private final Rect mTempRect = new Rect();
|
|
|
|
private final StatefulActivity mActivity;
|
|
|
|
@ViewDebug.ExportedProperty(category = "launcher")
|
|
private static final List<Rect> SYSTEM_GESTURE_EXCLUSION_RECT =
|
|
Collections.singletonList(new Rect());
|
|
|
|
private WindowStateListener mWindowStateListener;
|
|
@ViewDebug.ExportedProperty(category = "launcher")
|
|
private boolean mDisallowBackGesture;
|
|
@ViewDebug.ExportedProperty(category = "launcher")
|
|
private boolean mForceHideBackArrow;
|
|
|
|
private final SysUiScrim mSysUiScrim;
|
|
|
|
public LauncherRootView(Context context, AttributeSet attrs) {
|
|
super(context, attrs);
|
|
mActivity = StatefulActivity.fromContext(context);
|
|
mSysUiScrim = new SysUiScrim(this);
|
|
}
|
|
|
|
private void handleSystemWindowInsets(Rect insets) {
|
|
// Update device profile before notifying the children.
|
|
mActivity.getDeviceProfile().updateInsets(insets);
|
|
boolean resetState = !insets.equals(mInsets);
|
|
setInsets(insets);
|
|
|
|
if (resetState) {
|
|
mActivity.getStateManager().reapplyState(true /* cancelCurrentAnimation */);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
|
|
if (Utilities.ATLEAST_R) {
|
|
insets = updateInsetsDueToTaskbar(insets);
|
|
Insets systemWindowInsets = insets.getInsetsIgnoringVisibility(
|
|
WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout());
|
|
mTempRect.set(systemWindowInsets.left, systemWindowInsets.top, systemWindowInsets.right,
|
|
systemWindowInsets.bottom);
|
|
} else {
|
|
mTempRect.set(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(),
|
|
insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom());
|
|
}
|
|
handleSystemWindowInsets(mTempRect);
|
|
return insets;
|
|
}
|
|
|
|
/**
|
|
* Taskbar provides nav bar and tappable insets. However, taskbar is not attached immediately,
|
|
* and can be destroyed and recreated. Thus, instead of relying on taskbar being present to
|
|
* get its insets, we calculate them ourselves so they are stable regardless of whether taskbar
|
|
* is currently attached.
|
|
*
|
|
* @param oldInsets The system-provided insets, which we are modifying.
|
|
* @return The updated insets.
|
|
*/
|
|
@RequiresApi(api = Build.VERSION_CODES.R)
|
|
private WindowInsets updateInsetsDueToTaskbar(WindowInsets oldInsets) {
|
|
if (!ApiWrapper.TASKBAR_DRAWN_IN_PROCESS) {
|
|
// 3P launchers based on Launcher3 should still be inset like normal.
|
|
return oldInsets;
|
|
}
|
|
|
|
WindowInsets.Builder updatedInsetsBuilder = new WindowInsets.Builder(oldInsets);
|
|
|
|
DeviceProfile dp = mActivity.getDeviceProfile();
|
|
Resources resources = getResources();
|
|
|
|
Insets oldNavInsets = oldInsets.getInsets(WindowInsets.Type.navigationBars());
|
|
Rect newNavInsets = new Rect(oldNavInsets.left, oldNavInsets.top, oldNavInsets.right,
|
|
oldNavInsets.bottom);
|
|
|
|
if (dp.isLandscape) {
|
|
if (dp.isTablet) {
|
|
newNavInsets.bottom = ResourceUtils.getNavbarSize(
|
|
"navigation_bar_height_landscape", resources);
|
|
} else {
|
|
int navWidth = ResourceUtils.getNavbarSize("navigation_bar_width", resources);
|
|
if (dp.isSeascape()) {
|
|
newNavInsets.left = navWidth;
|
|
} else {
|
|
newNavInsets.right = navWidth;
|
|
}
|
|
}
|
|
} else {
|
|
newNavInsets.bottom = ResourceUtils.getNavbarSize("navigation_bar_height", resources);
|
|
}
|
|
updatedInsetsBuilder.setInsets(WindowInsets.Type.navigationBars(), Insets.of(newNavInsets));
|
|
updatedInsetsBuilder.setInsetsIgnoringVisibility(WindowInsets.Type.navigationBars(),
|
|
Insets.of(newNavInsets));
|
|
|
|
mActivity.updateWindowInsets(updatedInsetsBuilder, oldInsets);
|
|
|
|
return updatedInsetsBuilder.build();
|
|
}
|
|
|
|
@Override
|
|
public void setInsets(Rect insets) {
|
|
// If the insets haven't changed, this is a no-op. Avoid unnecessary layout caused by
|
|
// modifying child layout params.
|
|
if (!insets.equals(mInsets)) {
|
|
super.setInsets(insets);
|
|
mSysUiScrim.onInsetsChanged(insets);
|
|
}
|
|
}
|
|
|
|
public void dispatchInsets() {
|
|
mActivity.getDeviceProfile().updateInsets(mInsets);
|
|
super.setInsets(mInsets);
|
|
}
|
|
|
|
public void setWindowStateListener(WindowStateListener listener) {
|
|
mWindowStateListener = listener;
|
|
}
|
|
|
|
@Override
|
|
public void onWindowFocusChanged(boolean hasWindowFocus) {
|
|
super.onWindowFocusChanged(hasWindowFocus);
|
|
if (mWindowStateListener != null) {
|
|
mWindowStateListener.onWindowFocusChanged(hasWindowFocus);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onWindowVisibilityChanged(int visibility) {
|
|
super.onWindowVisibilityChanged(visibility);
|
|
if (mWindowStateListener != null) {
|
|
mWindowStateListener.onWindowVisibilityChanged(visibility);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void dispatchDraw(Canvas canvas) {
|
|
mSysUiScrim.draw(canvas);
|
|
super.dispatchDraw(canvas);
|
|
}
|
|
|
|
@Override
|
|
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
|
super.onLayout(changed, l, t, r, b);
|
|
SYSTEM_GESTURE_EXCLUSION_RECT.get(0).set(l, t, r, b);
|
|
setDisallowBackGesture(mDisallowBackGesture);
|
|
mSysUiScrim.setSize(r - l, b - t);
|
|
}
|
|
|
|
@TargetApi(Build.VERSION_CODES.Q)
|
|
public void setForceHideBackArrow(boolean forceHideBackArrow) {
|
|
this.mForceHideBackArrow = forceHideBackArrow;
|
|
setDisallowBackGesture(mDisallowBackGesture);
|
|
}
|
|
|
|
@TargetApi(Build.VERSION_CODES.Q)
|
|
public void setDisallowBackGesture(boolean disallowBackGesture) {
|
|
if (!Utilities.ATLEAST_Q || SEPARATE_RECENTS_ACTIVITY.get()) {
|
|
return;
|
|
}
|
|
mDisallowBackGesture = disallowBackGesture;
|
|
setSystemGestureExclusionRects((mForceHideBackArrow || mDisallowBackGesture)
|
|
? SYSTEM_GESTURE_EXCLUSION_RECT
|
|
: Collections.emptyList());
|
|
}
|
|
|
|
public SysUiScrim getSysUiScrim() {
|
|
return mSysUiScrim;
|
|
}
|
|
|
|
public interface WindowStateListener {
|
|
|
|
void onWindowFocusChanged(boolean hasFocus);
|
|
|
|
void onWindowVisibilityChanged(int visibility);
|
|
}
|
|
}
|