diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java index a4ecd8b13d..6ae8c9d31e 100644 --- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java @@ -36,6 +36,7 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_N import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING; +import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_REGION; import android.animation.ArgbEvaluator; import android.animation.ObjectAnimator; @@ -53,9 +54,11 @@ import android.util.Property; import android.view.Gravity; import android.view.MotionEvent; import android.view.View; +import android.view.View.OnAttachStateChangeListener; import android.view.View.OnClickListener; import android.view.View.OnHoverListener; import android.view.ViewGroup; +import android.view.WindowManager; import android.widget.FrameLayout; import android.widget.ImageView; @@ -65,10 +68,13 @@ import com.android.launcher3.Utilities; import com.android.launcher3.anim.AlphaUpdateListener; import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarButton; import com.android.launcher3.util.MultiValueAlpha; +import com.android.launcher3.util.TouchController; +import com.android.launcher3.views.BaseDragLayer; import com.android.quickstep.AnimatedFloat; import com.android.systemui.shared.rotation.FloatingRotationButton; import com.android.systemui.shared.rotation.RotationButton; import com.android.systemui.shared.rotation.RotationButtonController; +import com.android.systemui.shared.system.ViewTreeObserverWrapper; import java.util.ArrayList; import java.util.function.IntPredicate; @@ -95,6 +101,8 @@ public class NavbarButtonsViewController { private static final int MASK_IME_SWITCHER_VISIBLE = FLAG_SWITCHER_SUPPORTED | FLAG_IME_VISIBLE; + private static final String NAV_BUTTONS_SEPARATE_WINDOW_TITLE = "Taskbar Nav Buttons"; + private final ArrayList mPropertyHolders = new ArrayList<>(); private final ArrayList mAllButtons = new ArrayList<>(); private int mState; @@ -130,6 +138,12 @@ public class NavbarButtonsViewController { private View mBackButton; private FloatingRotationButton mFloatingRotationButton; + // Variables for moving nav buttons to a separate window above IME + private boolean mAreNavButtonsInSeparateWindow = false; + private BaseDragLayer mSeparateWindowParent; // Initialized in init. + private final ViewTreeObserverWrapper.OnComputeInsetsListener mSeparateWindowInsetsComputer = + this::onComputeInsetsForSeparateWindow; + public NavbarButtonsViewController(TaskbarActivityContext context, FrameLayout navButtonsView) { mContext = context; mNavButtonsView = navButtonsView; @@ -258,6 +272,21 @@ public class NavbarButtonsViewController { R.id.notifications_button); } + // Initialize things needed to move nav buttons to separate window. + mSeparateWindowParent = new BaseDragLayer(mContext, null, 0) { + @Override + public void recreateControllers() { + mControllers = new TouchController[0]; + } + + @Override + protected boolean canFindActiveController() { + // We don't have any controllers, but we don't want any floating views such as + // folder to intercept, either. This ensures nav buttons can always be pressed. + return false; + } + }; + mSeparateWindowParent.recreateControllers(); } private void initButtons(ViewGroup navContainer, ViewGroup endContainer, @@ -393,7 +422,7 @@ public class NavbarButtonsViewController { /** * Adds the bounds corresponding to all visible buttons to provided region */ - public void addVisibleButtonsRegion(TaskbarDragLayer parent, Region outRegion) { + public void addVisibleButtonsRegion(BaseDragLayer parent, Region outRegion) { int count = mAllButtons.size(); for (int i = 0; i < count; i++) { View button = mAllButtons.get(i); @@ -498,6 +527,59 @@ public class NavbarButtonsViewController { if (mFloatingRotationButton != null) { mFloatingRotationButton.hide(); } + + moveNavButtonsBackToTaskbarWindow(); + } + + /** + * Moves mNavButtonsView from TaskbarDragLayer to a placeholder BaseDragLayer on a new window. + */ + public void moveNavButtonsToNewWindow() { + if (mAreNavButtonsInSeparateWindow) { + return; + } + + mSeparateWindowParent.addOnAttachStateChangeListener(new OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(View view) { + ViewTreeObserverWrapper.addOnComputeInsetsListener( + mSeparateWindowParent.getViewTreeObserver(), mSeparateWindowInsetsComputer); + } + + @Override + public void onViewDetachedFromWindow(View view) { + mSeparateWindowParent.removeOnAttachStateChangeListener(this); + ViewTreeObserverWrapper.removeOnComputeInsetsListener( + mSeparateWindowInsetsComputer); + } + }); + + mAreNavButtonsInSeparateWindow = true; + mContext.getDragLayer().removeView(mNavButtonsView); + mSeparateWindowParent.addView(mNavButtonsView); + WindowManager.LayoutParams windowLayoutParams = mContext.createDefaultWindowLayoutParams(); + windowLayoutParams.setTitle(NAV_BUTTONS_SEPARATE_WINDOW_TITLE); + mContext.addWindowView(mSeparateWindowParent, windowLayoutParams); + + } + + /** + * Moves mNavButtonsView from its temporary window and reattaches it to TaskbarDragLayer. + */ + public void moveNavButtonsBackToTaskbarWindow() { + if (!mAreNavButtonsInSeparateWindow) { + return; + } + + mAreNavButtonsInSeparateWindow = false; + mContext.removeWindowView(mSeparateWindowParent); + mSeparateWindowParent.removeView(mNavButtonsView); + mContext.getDragLayer().addView(mNavButtonsView); + } + + private void onComputeInsetsForSeparateWindow(ViewTreeObserverWrapper.InsetsInfo insetsInfo) { + addVisibleButtonsRegion(mSeparateWindowParent, insetsInfo.touchableRegion); + insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION); } private class RotationButtonListener implements RotationButton.RotationButtonUpdatesCallback { diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index bef62584ae..e48b26be76 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -187,22 +187,7 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ public void init(TaskbarSharedState sharedState) { mLastRequestedNonFullscreenHeight = getDefaultTaskbarWindowHeight(); - mWindowLayoutParams = new WindowManager.LayoutParams( - MATCH_PARENT, - mLastRequestedNonFullscreenHeight, - TYPE_NAVIGATION_BAR_PANEL, - WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_SLIPPERY, - PixelFormat.TRANSLUCENT); - mWindowLayoutParams.setTitle(WINDOW_TITLE); - mWindowLayoutParams.packageName = getPackageName(); - mWindowLayoutParams.gravity = Gravity.BOTTOM; - mWindowLayoutParams.setFitInsetsTypes(0); - mWindowLayoutParams.receiveInsetsIgnoringZOrder = true; - mWindowLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; - mWindowLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; - mWindowLayoutParams.privateFlags = - WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; + mWindowLayoutParams = createDefaultWindowLayoutParams(); WindowManagerWrapper wmWrapper = WindowManagerWrapper.getInstance(); wmWrapper.setProvidesInsetsTypes( @@ -223,6 +208,27 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ mWindowManager.addView(mDragLayer, mWindowLayoutParams); } + /** Creates LayoutParams for adding a view directly to WindowManager as a new window */ + public WindowManager.LayoutParams createDefaultWindowLayoutParams() { + WindowManager.LayoutParams windowLayoutParams = new WindowManager.LayoutParams( + MATCH_PARENT, + mLastRequestedNonFullscreenHeight, + TYPE_NAVIGATION_BAR_PANEL, + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_SLIPPERY, + PixelFormat.TRANSLUCENT); + windowLayoutParams.setTitle(WINDOW_TITLE); + windowLayoutParams.packageName = getPackageName(); + windowLayoutParams.gravity = Gravity.BOTTOM; + windowLayoutParams.setFitInsetsTypes(0); + windowLayoutParams.receiveInsetsIgnoringZOrder = true; + windowLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; + windowLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; + windowLayoutParams.privateFlags = + WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; + return windowLayoutParams; + } + public void onConfigurationChanged(@Config int configChanges) { mControllers.onConfigurationChanged(configChanges); } @@ -497,17 +503,29 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ /** * Either adds or removes {@link WindowManager.LayoutParams#FLAG_NOT_FOCUSABLE} on the taskbar - * window. + * window. If we're now focusable, also move nav buttons to a separate window above IME. */ public void setTaskbarWindowFocusableForIme(boolean focusable) { if (focusable) { mWindowLayoutParams.flags &= ~FLAG_NOT_FOCUSABLE; + mControllers.navbarButtonsViewController.moveNavButtonsToNewWindow(); } else { mWindowLayoutParams.flags |= FLAG_NOT_FOCUSABLE; + mControllers.navbarButtonsViewController.moveNavButtonsBackToTaskbarWindow(); } mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams); } + /** Adds the given view to WindowManager with the provided LayoutParams (creates new window). */ + public void addWindowView(View view, WindowManager.LayoutParams windowLayoutParams) { + mWindowManager.addView(view, windowLayoutParams); + } + + /** Removes the given view from WindowManager. See {@link #addWindowView}. */ + public void removeWindowView(View view) { + mWindowManager.removeViewImmediate(view); + } + protected void onTaskbarIconClicked(View view) { Object tag = view.getTag(); if (tag instanceof Task) { diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java index b42a60ceea..df004ef6e4 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java @@ -15,17 +15,22 @@ */ package com.android.launcher3.taskbar; +import static android.view.KeyEvent.ACTION_UP; +import static android.view.KeyEvent.KEYCODE_BACK; + import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; import android.util.AttributeSet; +import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.R; import com.android.launcher3.testing.TestLogging; import com.android.launcher3.testing.TestProtocol; @@ -187,4 +192,17 @@ public class TaskbarDragLayer extends BaseDragLayer { TestLogging.recordMotionEvent(TestProtocol.SEQUENCE_MAIN, "Touch event", ev); return super.dispatchTouchEvent(ev); } + + /** Called while Taskbar window is focusable, e.g. when pressing back while a folder is open */ + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + if (event.getAction() == ACTION_UP && event.getKeyCode() == KEYCODE_BACK) { + AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity); + if (topView != null && topView.onBackPressed()) { + // Handled by the floating view. + return true; + } + } + return super.dispatchKeyEvent(event); + } } diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index ac194d2077..5bcc2ad26d 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -228,7 +228,7 @@ public final class Utilities { offsetPoints(coord, v.getLeft(), v.getTop()); scale *= v.getScaleX(); - v = (View) v.getParent(); + v = v.getParent() instanceof View ? (View) v.getParent() : null; } return scale; }