package com.android.launcher3; import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY; import android.annotation.TargetApi; import android.content.Context; import android.graphics.Canvas; import android.graphics.Rect; import android.os.Build; import android.util.AttributeSet; import android.view.ViewDebug; import android.view.WindowInsets; import com.android.launcher3.graphics.SysUiScrim; import com.android.launcher3.statemanager.StatefulActivity; import com.android.launcher3.util.window.WindowManagerProxy; 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 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) { mActivity.handleConfigurationChanged(mActivity.getResources().getConfiguration()); insets = WindowManagerProxy.INSTANCE.get(getContext()) .normalizeWindowInsets(getContext(), insets, mTempRect); handleSystemWindowInsets(mTempRect); return insets; } @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); } }