diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java index 002a86c697..094bced31e 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; @@ -55,9 +56,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; import android.widget.LinearLayout; @@ -68,10 +71,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; @@ -98,6 +104,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; @@ -134,6 +142,12 @@ public class NavbarButtonsViewController { private View mHomeButton; 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; @@ -321,6 +335,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, @@ -456,7 +485,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); @@ -561,6 +590,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 1198dc5b0a..a60d434dac 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -16,6 +16,7 @@ package com.android.launcher3.taskbar; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; +import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; @@ -189,21 +190,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.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( @@ -224,6 +211,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); } @@ -270,14 +278,6 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ return mViewCache; } - @Override - public boolean supportsIme() { - // Currently we don't support IME because we have FLAG_NOT_FOCUSABLE. We can remove that - // flag when opening a floating view that needs IME (such as Folder), but then that means - // Taskbar will be below IME and thus users can't click the back button. - return false; - } - @Override public View.OnClickListener getItemOnClickListener() { return this::onTaskbarIconClicked; @@ -505,6 +505,42 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ return mTaskbarHeightForIme; } + /** + * Either adds or removes {@link WindowManager.LayoutParams#FLAG_NOT_FOCUSABLE} on the taskbar + * window. + */ + public void setTaskbarWindowFocusable(boolean focusable) { + if (focusable) { + mWindowLayoutParams.flags &= ~FLAG_NOT_FOCUSABLE; + } else { + mWindowLayoutParams.flags |= FLAG_NOT_FOCUSABLE; + } + mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams); + } + + /** + * Either adds or removes {@link WindowManager.LayoutParams#FLAG_NOT_FOCUSABLE} on the taskbar + * window. If we're now focusable, also move nav buttons to a separate window above IME. + */ + public void setTaskbarWindowFocusableForIme(boolean focusable) { + if (focusable) { + mControllers.navbarButtonsViewController.moveNavButtonsToNewWindow(); + } else { + mControllers.navbarButtonsViewController.moveNavButtonsBackToTaskbarWindow(); + } + setTaskbarWindowFocusable(focusable); + } + + /** 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) { @@ -514,6 +550,17 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ } else if (tag instanceof FolderInfo) { FolderIcon folderIcon = (FolderIcon) view; Folder folder = folderIcon.getFolder(); + + folder.setOnFolderStateChangedListener(newState -> { + if (newState == Folder.STATE_OPEN) { + setTaskbarWindowFocusableForIme(true); + } else if (newState == Folder.STATE_CLOSED) { + // Defer by a frame to ensure we're no longer fullscreen and thus won't jump. + getDragLayer().post(() -> setTaskbarWindowFocusableForIme(false)); + folder.setOnFolderStateChangedListener(null); + } + }); + setTaskbarWindowFullscreen(true); getDragLayer().post(() -> { 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/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java index 7a50d0bf63..a654a561e4 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java @@ -27,6 +27,7 @@ import android.animation.ObjectAnimator; import androidx.annotation.NonNull; +import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.BaseQuickstepLauncher; import com.android.launcher3.LauncherState; import com.android.launcher3.statemanager.StateManager; @@ -256,6 +257,10 @@ import java.util.function.Supplier; if (hasAnyFlag(changedFlags, FLAG_RESUMED | FLAG_RECENTS_ANIMATION_RUNNING)) { boolean goingToLauncher = hasAnyFlag(FLAG_RESUMED | FLAG_RECENTS_ANIMATION_RUNNING); + if (goingToLauncher) { + // Handle closing open popups when going home/overview + AbstractFloatingView.closeAllOpenViews(mControllers.taskbarActivityContext); + } animatorSet.play(mTaskbarBackgroundAlpha.animateToValue(goingToLauncher ? 0 : 1) .setDuration(duration)); } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java index 2dee50688d..7af9c9dd73 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java @@ -154,6 +154,15 @@ public class TaskbarPopupController { .filter(Objects::nonNull) .collect(Collectors.toList())); container.requestFocus(); + + // Make focusable to receive back events + mControllers.taskbarActivityContext.setTaskbarWindowFocusable(true); + container.setOnCloseCallback(() -> { + mControllers.taskbarActivityContext.getDragLayer().post( + () -> mControllers.taskbarActivityContext.setTaskbarWindowFocusable(false)); + container.setOnCloseCallback(null); + }); + return container; } diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index 9f1e47f15a..bc06944679 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -1410,7 +1410,7 @@ public abstract class AbsSwipeUpHandler, .setContext(mContext) .setTaskId(runningTaskTarget.taskId) .setComponentName(taskInfo.topActivity) - .setLeash(runningTaskTarget.leash.getSurfaceControl()) + .setLeash(runningTaskTarget.leash) .setSourceRectHint( runningTaskTarget.taskInfo.pictureInPictureParams.getSourceRectHint()) .setAppBounds(taskInfo.configuration.windowConfiguration.getBounds()) diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java index 306e864372..af53bca358 100644 --- a/quickstep/src/com/android/quickstep/RecentsActivity.java +++ b/quickstep/src/com/android/quickstep/RecentsActivity.java @@ -419,7 +419,7 @@ public final class RecentsActivity extends StatefulActivity { RemoteAnimationTargets targets = new RemoteAnimationTargets( appTargets, wallpaperTargets, nonAppTargets, MODE_OPENING); for (RemoteAnimationTargetCompat app : targets.apps) { - new Transaction().setAlpha(app.leash.getSurfaceControl(), 1).apply(); + new Transaction().setAlpha(app.leash, 1).apply(); } AnimatorSet anim = new AnimatorSet(); anim.play(controller.getAnimationPlayer()); diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java index 5d9a537165..0a09e34754 100644 --- a/quickstep/src/com/android/quickstep/TaskViewUtils.java +++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java @@ -469,7 +469,7 @@ public final class TaskViewUtils { for (RemoteAnimationTargetCompat appTarget : appTargets) { final int taskId = appTarget.taskInfo != null ? appTarget.taskInfo.taskId : -1; final int mode = appTarget.mode; - final SurfaceControl leash = appTarget.leash.getSurfaceControl(); + final SurfaceControl leash = appTarget.leash; if (leash == null) { continue; } @@ -484,7 +484,7 @@ public final class TaskViewUtils { } for (int i = 0; i < nonAppTargets.length; ++i) { - final SurfaceControl leash = appTargets[i].leash.getSurfaceControl(); + final SurfaceControl leash = appTargets[i].leash; if (nonAppTargets[i].windowType == TYPE_DOCK_DIVIDER && leash != null) { openingTargets.add(leash); } @@ -611,7 +611,7 @@ public final class TaskViewUtils { boolean hasSurfaceToAnimate = false; for (int i = 0; i < nonApps.length; ++i) { final RemoteAnimationTargetCompat targ = nonApps[i]; - final SurfaceControl leash = targ.leash.getSurfaceControl(); + final SurfaceControl leash = targ.leash; if (targ.windowType == TYPE_DOCK_DIVIDER && leash != null) { auxiliarySurfaces.add(leash); hasSurfaceToAnimate = true; diff --git a/quickstep/src/com/android/quickstep/util/TransformParams.java b/quickstep/src/com/android/quickstep/util/TransformParams.java index 03d7a37748..75d6001afd 100644 --- a/quickstep/src/com/android/quickstep/util/TransformParams.java +++ b/quickstep/src/com/android/quickstep/util/TransformParams.java @@ -174,10 +174,10 @@ public class TransformParams { RemoteAnimationTargetCompat app = targets.unfilteredApps[i]; if (app.mode == targets.targetMode) { if (app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_RECENTS) { - return app.leash.getSurfaceControl(); + return app.leash; } } else { - return app.leash.getSurfaceControl(); + return app.leash; } } return null; diff --git a/res/values/attrs.xml b/res/values/attrs.xml index 6db0af3799..0b04b863b4 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -187,6 +187,10 @@ + + + + @@ -244,6 +248,8 @@ + + @@ -270,6 +276,8 @@ + + diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index adb1613e9d..11f58e7d87 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -238,9 +238,19 @@ public class CellLayout extends ViewGroup { mActivity = ActivityContext.lookupContext(context); DeviceProfile deviceProfile = mActivity.getDeviceProfile(); - mBorderSpace = mContainerType == FOLDER - ? new Point(deviceProfile.folderCellLayoutBorderSpacePx) - : new Point(deviceProfile.cellLayoutBorderSpacePx); + switch (mContainerType) { + case FOLDER: + mBorderSpace = new Point(deviceProfile.folderCellLayoutBorderSpacePx); + break; + case HOTSEAT: + mBorderSpace = new Point(deviceProfile.hotseatBorderSpace, + deviceProfile.hotseatBorderSpace); + break; + case WORKSPACE: + default: + mBorderSpace = new Point(deviceProfile.cellLayoutBorderSpacePx); + break; + } mCellWidth = mCellHeight = -1; mFixedCellWidth = mFixedCellHeight = -1; diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index ab8e5e73f0..8a5b8885ff 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -161,7 +161,7 @@ public class DeviceProfile { public final int hotseatBarSidePaddingStartPx; public final int hotseatBarSidePaddingEndPx; public final int hotseatQsbHeight; - public final int hotseatBorderSpace; + public int hotseatBorderSpace; public final float qsbBottomMarginOriginalPx; public int qsbBottomMarginPx; @@ -716,6 +716,7 @@ public class DeviceProfile { } // Hotseat + hotseatBorderSpace = pxFromDp(inv.hotseatBorderSpaces[mTypeIndex], mMetrics, scale); updateHotseatIconSize(iconSizePx); if (!isVerticalLayout) { diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java index 94325d0216..0b168a5242 100644 --- a/src/com/android/launcher3/InvariantDeviceProfile.java +++ b/src/com/android/launcher3/InvariantDeviceProfile.java @@ -794,6 +794,11 @@ public class InvariantDeviceProfile { x = a.getFloat(R.styleable.ProfileDisplayOption_minCellWidth, 0); y = a.getFloat(R.styleable.ProfileDisplayOption_minCellHeight, 0); minCellSize[INDEX_DEFAULT] = new PointF(x, y); + + x = a.getFloat(R.styleable.ProfileDisplayOption_minCellWidthLandscape, + minCellSize[INDEX_DEFAULT].x); + y = a.getFloat(R.styleable.ProfileDisplayOption_minCellHeightLandscape, + minCellSize[INDEX_DEFAULT].y); minCellSize[INDEX_LANDSCAPE] = new PointF(x, y); x = a.getFloat(R.styleable.ProfileDisplayOption_minCellWidthTwoPanelPortrait, @@ -896,7 +901,9 @@ public class InvariantDeviceProfile { horizontalMargin[INDEX_DEFAULT] = a.getFloat( R.styleable.ProfileDisplayOption_horizontalMargin, 0); - horizontalMargin[INDEX_LANDSCAPE] = horizontalMargin[INDEX_DEFAULT]; + horizontalMargin[INDEX_LANDSCAPE] = a.getFloat( + R.styleable.ProfileDisplayOption_horizontalMarginLandscape, + horizontalMargin[INDEX_DEFAULT]); horizontalMargin[INDEX_TWO_PANEL_LANDSCAPE] = a.getFloat( R.styleable.ProfileDisplayOption_horizontalMarginTwoPanelLandscape, horizontalMargin[INDEX_DEFAULT]); @@ -906,7 +913,9 @@ public class InvariantDeviceProfile { hotseatBorderSpaces[INDEX_DEFAULT] = a.getFloat( R.styleable.ProfileDisplayOption_hotseatBorderSpace, borderSpace); - hotseatBorderSpaces[INDEX_LANDSCAPE] = hotseatBorderSpaces[INDEX_DEFAULT]; + hotseatBorderSpaces[INDEX_LANDSCAPE] = a.getFloat( + R.styleable.ProfileDisplayOption_hotseatBorderSpaceLandscape, + hotseatBorderSpaces[INDEX_DEFAULT]); hotseatBorderSpaces[INDEX_TWO_PANEL_LANDSCAPE] = a.getFloat( R.styleable.ProfileDisplayOption_hotseatBorderSpaceTwoPanelLandscape, hotseatBorderSpaces[INDEX_DEFAULT]); diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index b062b9176f..225460dc98 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -229,7 +229,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; } diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index daef68226d..1e6342cc8b 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -57,6 +57,7 @@ import android.view.animation.AnimationUtils; import android.view.inputmethod.EditorInfo; import android.widget.TextView; +import androidx.annotation.IntDef; import androidx.annotation.Nullable; import androidx.core.content.res.ResourcesCompat; @@ -101,6 +102,8 @@ import com.android.launcher3.views.BaseDragLayer; import com.android.launcher3.views.ClipPathView; import com.android.launcher3.widget.PendingAddShortcutInfo; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -130,10 +133,13 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo */ private static final int MIN_CONTENT_DIMEN = 5; - static final int STATE_NONE = -1; - static final int STATE_SMALL = 0; - static final int STATE_ANIMATING = 1; - static final int STATE_OPEN = 2; + public static final int STATE_CLOSED = 0; + public static final int STATE_ANIMATING = 1; + public static final int STATE_OPEN = 2; + + @Retention(RetentionPolicy.SOURCE) + @IntDef({STATE_CLOSED, STATE_ANIMATING, STATE_OPEN}) + public @interface FolderState {} /** * Time for which the scroll hint is shown before automatically changing page. @@ -198,13 +204,12 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo @ViewDebug.ExportedProperty(category = "launcher", mapping = { - @ViewDebug.IntToString(from = STATE_NONE, to = "STATE_NONE"), - @ViewDebug.IntToString(from = STATE_SMALL, to = "STATE_SMALL"), + @ViewDebug.IntToString(from = STATE_CLOSED, to = "STATE_CLOSED"), @ViewDebug.IntToString(from = STATE_ANIMATING, to = "STATE_ANIMATING"), @ViewDebug.IntToString(from = STATE_OPEN, to = "STATE_OPEN"), }) - @Thunk - int mState = STATE_NONE; + private int mState = STATE_CLOSED; + private OnFolderStateChangedListener mOnFolderStateChangedListener; @ViewDebug.ExportedProperty(category = "launcher") private boolean mRearrangeOnClose = false; boolean mItemsInvalidated = false; @@ -277,19 +282,15 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo mPageIndicator = findViewById(R.id.folder_page_indicator); mFolderName = findViewById(R.id.folder_name); mFolderName.setTextSize(TypedValue.COMPLEX_UNIT_PX, dp.folderLabelTextSizePx); - if (mActivityContext.supportsIme()) { - mFolderName.setOnBackKeyListener(this); - mFolderName.setOnFocusChangeListener(this); - mFolderName.setOnEditorActionListener(this); - mFolderName.setSelectAllOnFocus(true); - mFolderName.setInputType(mFolderName.getInputType() - & ~InputType.TYPE_TEXT_FLAG_AUTO_CORRECT - | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS - | InputType.TYPE_TEXT_FLAG_CAP_WORDS); - mFolderName.forceDisableSuggestions(true); - } else { - mFolderName.setEnabled(false); - } + mFolderName.setOnBackKeyListener(this); + mFolderName.setOnFocusChangeListener(this); + mFolderName.setOnEditorActionListener(this); + mFolderName.setSelectAllOnFocus(true); + mFolderName.setInputType(mFolderName.getInputType() + & ~InputType.TYPE_TEXT_FLAG_AUTO_CORRECT + | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS + | InputType.TYPE_TEXT_FLAG_CAP_WORDS); + mFolderName.forceDisableSuggestions(true); mFooter = findViewById(R.id.folder_footer); mFooterHeight = getResources().getDimensionPixelSize(R.dimen.folder_label_height); @@ -561,7 +562,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo a.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { - mState = STATE_ANIMATING; + setState(STATE_ANIMATING); mCurrentAnimator = a; } @@ -686,7 +687,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo @Override public void onAnimationEnd(Animator animation) { - mState = STATE_OPEN; + setState(STATE_OPEN); announceAccessibilityChanges(); AccessibilityManagerCompat.sendFolderOpenedEventToTest(getContext()); @@ -862,7 +863,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo } mSuppressFolderDeletion = false; clearDragInfo(); - mState = STATE_SMALL; + setState(STATE_CLOSED); mContent.setCurrentPage(0); } @@ -1655,4 +1656,21 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo return windowBottomPx - folderBottomPx; } + + private void setState(@FolderState int newState) { + mState = newState; + if (mOnFolderStateChangedListener != null) { + mOnFolderStateChangedListener.onFolderStateChanged(mState); + } + } + + public void setOnFolderStateChangedListener(@Nullable OnFolderStateChangedListener listener) { + mOnFolderStateChangedListener = listener; + } + + /** Listener that can be registered via {@link Folder#setOnFolderStateChangedListener} */ + public interface OnFolderStateChangedListener { + /** See {@link Folder.FolderState} */ + void onFolderStateChanged(@FolderState int newState); + } } diff --git a/src/com/android/launcher3/popup/ArrowPopup.java b/src/com/android/launcher3/popup/ArrowPopup.java index b1a41093f5..f06e5cecd5 100644 --- a/src/com/android/launcher3/popup/ArrowPopup.java +++ b/src/com/android/launcher3/popup/ArrowPopup.java @@ -48,7 +48,7 @@ import android.view.ViewTreeObserver; import android.view.animation.Interpolator; import android.widget.FrameLayout; -import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.InsettableFrameLayout; @@ -120,7 +120,7 @@ public abstract class ArrowPopup private final GradientDrawable mRoundedTop; private final GradientDrawable mRoundedBottom; - private Runnable mOnCloseCallback = () -> { }; + @Nullable private Runnable mOnCloseCallback = null; // The rect string of the view that the arrow is attached to, in screen reference frame. protected int mArrowColor; @@ -766,7 +766,6 @@ public abstract class ArrowPopup } } - protected void animateClose() { if (!mIsOpen) { return; @@ -816,7 +815,9 @@ public abstract class ArrowPopup mDeferContainerRemoval = false; getPopupContainer().removeView(this); getPopupContainer().removeView(mArrow); - mOnCloseCallback.run(); + if (mOnCloseCallback != null) { + mOnCloseCallback.run(); + } if (mColorExtractors != null) { mColorExtractors.forEach(e -> e.setListener(null)); } @@ -825,7 +826,7 @@ public abstract class ArrowPopup /** * Callback to be called when the popup is closed */ - public void setOnCloseCallback(@NonNull Runnable callback) { + public void setOnCloseCallback(@Nullable Runnable callback) { mOnCloseCallback = callback; } diff --git a/src/com/android/launcher3/util/UiThreadHelper.java b/src/com/android/launcher3/util/UiThreadHelper.java index ac5368c8b4..a1f31fe459 100644 --- a/src/com/android/launcher3/util/UiThreadHelper.java +++ b/src/com/android/launcher3/util/UiThreadHelper.java @@ -28,7 +28,6 @@ import android.os.Message; import android.view.View; import android.view.inputmethod.InputMethodManager; -import com.android.launcher3.BaseActivity; import com.android.launcher3.views.ActivityContext; /** @@ -56,7 +55,7 @@ public class UiThreadHelper { STATS_LOGGER_KEY, Message.obtain( HANDLER.get(root.getContext()), - () -> BaseActivity.fromContext(root.getContext()) + () -> ActivityContext.lookupContext(root.getContext()) .getStatsLogManager() .logger() .log(LAUNCHER_ALLAPPS_KEYBOARD_CLOSED) diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java index e09eff6f94..c0f6316b95 100644 --- a/src/com/android/launcher3/views/ActivityContext.java +++ b/src/com/android/launcher3/views/ActivityContext.java @@ -124,13 +124,6 @@ public interface ActivityContext { return true; } - /** - * Returns whether we can show the IME for elements hosted by this ActivityContext. - */ - default boolean supportsIme() { - return true; - } - /** * Called just before logging the given item. */