Merge "Import translations. DO NOT MERGE ANYWHERE" into tm-dev
diff --git a/Android.bp b/Android.bp
index b3027bc..0a55675 100644
--- a/Android.bp
+++ b/Android.bp
@@ -228,7 +228,7 @@
],
}
-// Common source files used to build go launcher
+// Common source files used to build go launcher except go/src files
filegroup {
name: "launcher-go-src-no-build-config",
srcs: [
@@ -236,8 +236,6 @@
"src/**/*.kt",
"quickstep/src/**/*.java",
"quickstep/src/**/*.kt",
- "go/src/**/*.java",
- "go/src/**/*.kt",
"go/quickstep/src/**/*.java",
"go/quickstep/src/**/*.kt",
],
diff --git a/go/src/com/android/launcher3/util/AbsGridOccupancy.java b/go/src/com/android/launcher3/util/AbsGridOccupancy.java
new file mode 100644
index 0000000..4a46bd1
--- /dev/null
+++ b/go/src/com/android/launcher3/util/AbsGridOccupancy.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2022 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.launcher3.util;
+
+/**
+ * Defines method to find the next vacant cell on a grid.
+ * This uses the default top-down, left-right approach and can be over-written through
+ * code swaps in different launchers.
+ */
+public abstract class AbsGridOccupancy {
+
+ /**
+ * Find the first vacant cell, if there is one.
+ *
+ * @param vacantOut Holds the x and y coordinate of the vacant cell
+ * @param spanX Horizontal cell span.
+ * @param spanY Vertical cell span.
+ *
+ * @return true if a vacant cell was found
+ */
+ protected boolean findVacantCell(int[] vacantOut, boolean[][] cells, int countX, int countY,
+ int spanX, int spanY) {
+ for (int y = 0; (y + spanY) <= countY; y++) {
+ for (int x = 0; (x + spanX) <= countX; x++) {
+ boolean available = !cells[x][y];
+ out:
+ for (int i = x; i < x + spanX; i++) {
+ for (int j = y; j < y + spanY; j++) {
+ available = available && !cells[i][j];
+ if (!available) break out;
+ }
+ }
+ if (available) {
+ vacantOut[0] = x;
+ vacantOut[1] = y;
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+}
diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto
index cf854ed..10eedc8 100644
--- a/protos/launcher_atom.proto
+++ b/protos/launcher_atom.proto
@@ -76,6 +76,9 @@
// Represents the apps list sorted alphabetically inside the all-apps view.
message AllAppsContainer {
+ oneof ParentContainer {
+ TaskBarContainer taskbar_container = 1;
+ }
}
message WidgetsContainer {
@@ -83,6 +86,9 @@
// Represents the predicted apps row(top row) in the all-apps view.
message PredictionContainer {
+ oneof ParentContainer {
+ TaskBarContainer taskbar_container = 1;
+ }
}
// Represents the apps container within search results.
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 5b912ad..097609f 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -1161,7 +1161,7 @@
new LauncherAnimationRunner(mHandler, mWallpaperOpenTransitionRunner,
false /* startAtFrontOfQueue */), mLauncher.getIApplicationThread());
mLauncherOpenTransition.addHomeOpenCheck(mLauncher.getComponentName());
- SystemUiProxy.INSTANCE.getNoCreate().registerRemoteTransition(mLauncherOpenTransition);
+ SystemUiProxy.INSTANCE.get(mLauncher).registerRemoteTransition(mLauncherOpenTransition);
}
if (mBackAnimationController != null) {
mBackAnimationController.registerBackCallbacks(mHandler);
@@ -1172,7 +1172,7 @@
unregisterRemoteAnimations();
unregisterRemoteTransitions();
mStartingWindowListener.setTransitionManager(null);
- SystemUiProxy.INSTANCE.getNoCreate().setStartingWindowListener(null);
+ SystemUiProxy.INSTANCE.get(mLauncher).setStartingWindowListener(null);
}
private void unregisterRemoteAnimations() {
@@ -1196,7 +1196,7 @@
}
if (hasControlRemoteAppTransitionPermission()) {
if (mLauncherOpenTransition == null) return;
- SystemUiProxy.INSTANCE.getNoCreate().unregisterRemoteTransition(
+ SystemUiProxy.INSTANCE.get(mLauncher).unregisterRemoteTransition(
mLauncherOpenTransition);
mLauncherOpenTransition = null;
mWallpaperOpenTransitionRunner = null;
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index 175a1d9..f65b907 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -210,7 +210,7 @@
0, 1));
// Center nav buttons in new height for IME.
float transForIme = (mContext.getDeviceProfile().taskbarSize
- - mContext.getTaskbarHeightForIme()) / 2f;
+ - mControllers.taskbarInsetsController.getTaskbarHeightForIme()) / 2f;
// For gesture nav, nav buttons only show for IME anyway so keep them translated down.
float defaultButtonTransY = alwaysShowButtons ? 0 : transForIme;
mPropertyHolders.add(new StatePropertyHolder(mTaskbarNavButtonTranslationYForIme,
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 89e54b8..9561b74 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -27,9 +27,6 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_OPEN;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
-import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
-import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_EXTRA_NAVIGATION_BAR;
-import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_SIZE;
import android.animation.AnimatorSet;
import android.app.ActivityOptions;
@@ -39,7 +36,6 @@
import android.content.pm.ActivityInfo.Config;
import android.content.pm.LauncherApps;
import android.content.res.Resources;
-import android.graphics.Insets;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.Process;
@@ -88,7 +84,6 @@
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.rotation.RotationButtonController;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider;
import java.io.PrintWriter;
@@ -113,7 +108,6 @@
private final WindowManager mWindowManager;
private final @Nullable RoundedCorner mLeftCorner, mRightCorner;
- private final int mTaskbarHeightForIme;
private WindowManager.LayoutParams mWindowLayoutParams;
private boolean mIsFullscreen;
// The size we should return to when we call setTaskbarWindowFullscreen(false)
@@ -154,7 +148,6 @@
Settings.Secure.getUriFor(Settings.Secure.NAV_BAR_KIDS_MODE), 0);
updateIconSize(resources);
- mTaskbarHeightForIme = resources.getDimensionPixelSize(R.dimen.taskbar_ime_size);
// Get display and corners first, as views might use them in constructor.
Display display = windowContext.getDisplay();
@@ -202,29 +195,14 @@
new TaskbarAutohideSuspendController(this),
new TaskbarPopupController(this),
new TaskbarForceVisibleImmersiveController(this),
- new TaskbarAllAppsController(this));
+ new TaskbarAllAppsController(this),
+ new TaskbarInsetsController(this));
}
public void init(TaskbarSharedState sharedState) {
mLastRequestedNonFullscreenHeight = getDefaultTaskbarWindowHeight();
mWindowLayoutParams = createDefaultWindowLayoutParams();
- WindowManagerWrapper wmWrapper = WindowManagerWrapper.getInstance();
- wmWrapper.setProvidesInsetsTypes(
- mWindowLayoutParams,
- new int[] { ITYPE_EXTRA_NAVIGATION_BAR, ITYPE_BOTTOM_TAPPABLE_ELEMENT }
- );
- // Adjust the frame by the rounded corners (ie. leaving just the bar as the inset) when
- // the IME is showing
- mWindowLayoutParams.providedInternalImeInsets = new Insets[ITYPE_SIZE];
- final Insets reducingSize = Insets.of(0,
- getDefaultTaskbarWindowHeight() - mTaskbarHeightForIme, 0, 0);
- mWindowLayoutParams.providedInternalImeInsets[ITYPE_EXTRA_NAVIGATION_BAR] = reducingSize;
- mWindowLayoutParams.providedInternalImeInsets[ITYPE_BOTTOM_TAPPABLE_ELEMENT] =
- reducingSize;
-
- mWindowLayoutParams.insetsRoundedCornerFrame = true;
-
// Initialize controllers after all are constructed.
mControllers.init(sharedState);
updateSysuiStateFlags(sharedState.sysuiStateFlags, true /* fromInit */);
@@ -304,6 +282,10 @@
return mRightCorner == null ? 0 : mRightCorner.getRadius();
}
+ public WindowManager.LayoutParams getWindowLayoutParams() {
+ return mWindowLayoutParams;
+ }
+
@Override
public TaskbarDragLayer getDragLayer() {
return mDragLayer;
@@ -380,6 +362,14 @@
folderBuilder.clearHotseat();
itemInfoBuilder.setContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
.setFolder(folderBuilder));
+ } else if (oldContainer.hasAllAppsContainer()) {
+ itemInfoBuilder.setContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
+ .setAllAppsContainer(oldContainer.getAllAppsContainer().toBuilder()
+ .setTaskbarContainer(LauncherAtom.TaskBarContainer.newBuilder())));
+ } else if (oldContainer.hasPredictionContainer()) {
+ itemInfoBuilder.setContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
+ .setPredictionContainer(oldContainer.getPredictionContainer().toBuilder()
+ .setTaskbarContainer(LauncherAtom.TaskBarContainer.newBuilder())));
}
}
@@ -569,14 +559,7 @@
}
}
mWindowLayoutParams.height = height;
- final Insets reducingSize =
- Insets.of(0, height - mTaskbarHeightForIme, 0, 0);
- if (mWindowLayoutParams.providedInternalImeInsets == null) {
- mWindowLayoutParams.providedInternalImeInsets = new Insets[ITYPE_SIZE];
- }
- mWindowLayoutParams.providedInternalImeInsets[ITYPE_EXTRA_NAVIGATION_BAR] = reducingSize;
- mWindowLayoutParams.providedInternalImeInsets[ITYPE_BOTTOM_TAPPABLE_ELEMENT] =
- reducingSize;
+ mControllers.taskbarInsetsController.onTaskbarWindowHeightChanged();
mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams);
}
@@ -588,13 +571,6 @@
}
/**
- * Returns the bottom insets taskbar provides to the IME when IME is visible.
- */
- public int getTaskbarHeightForIme() {
- return mTaskbarHeightForIme;
- }
-
- /**
* Either adds or removes {@link WindowManager.LayoutParams#FLAG_NOT_FOCUSABLE} on the taskbar
* window.
*/
@@ -703,6 +679,7 @@
}
} else if (tag instanceof AppInfo) {
startItemInfoActivity((AppInfo) tag);
+ mControllers.uiController.onTaskbarIconLaunched((AppInfo) tag);
} else {
Log.e(TAG, "Unknown type clicked: " + tag);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
index a3586396..5d3a152 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
@@ -51,6 +51,7 @@
public final TaskbarPopupController taskbarPopupController;
public final TaskbarForceVisibleImmersiveController taskbarForceVisibleImmersiveController;
public final TaskbarAllAppsController taskbarAllAppsController;
+ public final TaskbarInsetsController taskbarInsetsController;
@Nullable private LoggableTaskbarController[] mControllersToLog = null;
@@ -76,7 +77,8 @@
TaskbarAutohideSuspendController taskbarAutoHideSuspendController,
TaskbarPopupController taskbarPopupController,
TaskbarForceVisibleImmersiveController taskbarForceVisibleImmersiveController,
- TaskbarAllAppsController taskbarAllAppsController) {
+ TaskbarAllAppsController taskbarAllAppsController,
+ TaskbarInsetsController taskbarInsetsController) {
this.taskbarActivityContext = taskbarActivityContext;
this.taskbarDragController = taskbarDragController;
this.navButtonController = navButtonController;
@@ -94,6 +96,7 @@
this.taskbarPopupController = taskbarPopupController;
this.taskbarForceVisibleImmersiveController = taskbarForceVisibleImmersiveController;
this.taskbarAllAppsController = taskbarAllAppsController;
+ this.taskbarInsetsController = taskbarInsetsController;
}
/**
@@ -119,13 +122,14 @@
taskbarForceVisibleImmersiveController.init(this);
taskbarAllAppsController.init(this, sharedState);
navButtonController.init(this);
+ taskbarInsetsController.init(this);
mControllersToLog = new LoggableTaskbarController[] {
taskbarDragController, navButtonController, navbarButtonsViewController,
taskbarDragLayerController, taskbarScrimViewController, taskbarViewController,
taskbarUnfoldAnimationController, taskbarKeyguardController,
stashedHandleViewController, taskbarStashController, taskbarEduController,
- taskbarAutohideSuspendController, taskbarPopupController
+ taskbarAutohideSuspendController, taskbarPopupController, taskbarInsetsController
};
mAreAllControllersInitialized = true;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
index c7330d3..307f674 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
@@ -15,18 +15,10 @@
*/
package com.android.launcher3.taskbar;
-import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
-import static com.android.launcher3.AbstractFloatingView.TYPE_TASKBAR_ALL_APPS;
-import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_CONTENT;
-import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_FRAME;
-import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_REGION;
-
import android.content.res.Resources;
import android.graphics.Rect;
-import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.R;
-import com.android.launcher3.anim.AlphaUpdateListener;
import com.android.launcher3.util.TouchController;
import com.android.quickstep.AnimatedFloat;
import com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo;
@@ -168,37 +160,7 @@
* @see InsetsInfo#setTouchableInsets(int)
*/
public void updateInsetsTouchability(InsetsInfo insetsInfo) {
- insetsInfo.touchableRegion.setEmpty();
- // Always have nav buttons be touchable
- mControllers.navbarButtonsViewController.addVisibleButtonsRegion(
- mTaskbarDragLayer, insetsInfo.touchableRegion);
- boolean insetsIsTouchableRegion = true;
-
- if (mTaskbarDragLayer.getAlpha() < AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD) {
- // Let touches pass through us.
- insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
- } else if (mControllers.navbarButtonsViewController.isImeVisible()) {
- insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
- } else if (!mControllers.uiController.isTaskbarTouchable()) {
- // Let touches pass through us.
- insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
- } else if (mControllers.taskbarDragController.isSystemDragInProgress()) {
- // Let touches pass through us.
- insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
- } else if (AbstractFloatingView.getOpenView(mActivity, TYPE_TASKBAR_ALL_APPS) != null) {
- // Let touches pass through us.
- insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
- } else if (mControllers.taskbarViewController.areIconsVisible()
- || AbstractFloatingView.getOpenView(mActivity, TYPE_ALL) != null
- || mActivity.isNavBarKidsModeActive()) {
- // Taskbar has some touchable elements, take over the full taskbar area
- insetsInfo.setTouchableInsets(mActivity.isTaskbarWindowFullscreen()
- ? TOUCHABLE_INSETS_FRAME : TOUCHABLE_INSETS_CONTENT);
- insetsIsTouchableRegion = false;
- } else {
- insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
- }
- mActivity.excludeFromMagnificationRegion(insetsIsTouchableRegion);
+ mControllers.taskbarInsetsController.updateInsetsTouchability(insetsInfo);
}
/**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
new file mode 100644
index 0000000..a2ff780
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2022 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.launcher3.taskbar
+
+import android.graphics.Insets
+import android.view.WindowManager
+import com.android.launcher3.AbstractFloatingView
+import com.android.launcher3.R
+import com.android.launcher3.anim.AlphaUpdateListener
+import com.android.launcher3.taskbar.TaskbarControllers.LoggableTaskbarController
+import com.android.quickstep.KtR
+import com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo
+import com.android.systemui.shared.system.WindowManagerWrapper
+import com.android.systemui.shared.system.WindowManagerWrapper.*
+import java.io.PrintWriter
+
+/**
+ * Handles the insets that Taskbar provides to underlying apps and the IME.
+ */
+class TaskbarInsetsController(val context: TaskbarActivityContext): LoggableTaskbarController {
+
+ /** The bottom insets taskbar provides to the IME when IME is visible. */
+ val taskbarHeightForIme: Int = context.resources.getDimensionPixelSize(
+ KtR.dimen.taskbar_ime_size)
+
+ // Initialized in init.
+ private lateinit var controllers: TaskbarControllers
+ private lateinit var windowLayoutParams: WindowManager.LayoutParams
+
+ fun init(controllers: TaskbarControllers) {
+ this.controllers = controllers
+ windowLayoutParams = context.windowLayoutParams
+
+ val wmWrapper: WindowManagerWrapper = getInstance()
+ wmWrapper.setProvidesInsetsTypes(
+ windowLayoutParams,
+ intArrayOf(
+ ITYPE_EXTRA_NAVIGATION_BAR,
+ ITYPE_BOTTOM_TAPPABLE_ELEMENT
+ )
+ )
+
+ windowLayoutParams.providedInternalImeInsets = arrayOfNulls<Insets>(ITYPE_SIZE)
+
+ onTaskbarWindowHeightChanged()
+
+ windowLayoutParams.insetsRoundedCornerFrame = true
+ }
+
+ fun onTaskbarWindowHeightChanged() {
+ val reducingSize = Insets.of(0, windowLayoutParams.height - taskbarHeightForIme, 0, 0)
+ windowLayoutParams.providedInternalImeInsets[ITYPE_EXTRA_NAVIGATION_BAR] = reducingSize
+ windowLayoutParams.providedInternalImeInsets[ITYPE_BOTTOM_TAPPABLE_ELEMENT] = reducingSize
+ }
+
+ /**
+ * Called to update the touchable insets.
+ * @see InsetsInfo.setTouchableInsets
+ */
+ fun updateInsetsTouchability(insetsInfo: InsetsInfo) {
+ insetsInfo.touchableRegion.setEmpty()
+ // Always have nav buttons be touchable
+ controllers.navbarButtonsViewController.addVisibleButtonsRegion(
+ context.dragLayer, insetsInfo.touchableRegion
+ )
+ var insetsIsTouchableRegion = true
+ if (context.dragLayer.alpha < AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD) {
+ // Let touches pass through us.
+ insetsInfo.setTouchableInsets(InsetsInfo.TOUCHABLE_INSETS_REGION)
+ } else if (controllers.navbarButtonsViewController.isImeVisible) {
+ insetsInfo.setTouchableInsets(InsetsInfo.TOUCHABLE_INSETS_REGION)
+ } else if (!controllers.uiController.isTaskbarTouchable) {
+ // Let touches pass through us.
+ insetsInfo.setTouchableInsets(InsetsInfo.TOUCHABLE_INSETS_REGION)
+ } else if (controllers.taskbarDragController.isSystemDragInProgress) {
+ // Let touches pass through us.
+ insetsInfo.setTouchableInsets(InsetsInfo.TOUCHABLE_INSETS_REGION)
+ } else if (AbstractFloatingView.getOpenView<AbstractFloatingView?>(
+ context,
+ AbstractFloatingView.TYPE_TASKBAR_ALL_APPS
+ ) != null
+ ) {
+ // Let touches pass through us.
+ insetsInfo.setTouchableInsets(InsetsInfo.TOUCHABLE_INSETS_REGION)
+ } else if (controllers.taskbarViewController.areIconsVisible()
+ || AbstractFloatingView.getOpenView<AbstractFloatingView?>(
+ context,
+ AbstractFloatingView.TYPE_ALL
+ ) != null
+ || context.isNavBarKidsModeActive
+ ) {
+ // Taskbar has some touchable elements, take over the full taskbar area
+ insetsInfo.setTouchableInsets(
+ if (context.isTaskbarWindowFullscreen) {
+ InsetsInfo.TOUCHABLE_INSETS_FRAME
+ } else {
+ InsetsInfo.TOUCHABLE_INSETS_CONTENT
+ }
+ )
+ insetsIsTouchableRegion = false
+ } else {
+ insetsInfo.setTouchableInsets(InsetsInfo.TOUCHABLE_INSETS_REGION)
+ }
+ context.excludeFromMagnificationRegion(insetsIsTouchableRegion)
+ }
+
+ override fun dumpLogs(prefix: String, pw: PrintWriter) {
+ pw.println(prefix + "TaskbarInsetsController:")
+ pw.println("$prefix\twindowHeight=${windowLayoutParams.height}")
+ pw.println("$prefix\tprovidedInternalImeInsets[ITYPE_EXTRA_NAVIGATION_BAR]=" +
+ "${windowLayoutParams.providedInternalImeInsets[ITYPE_EXTRA_NAVIGATION_BAR]}")
+ pw.println("$prefix\tprovidedInternalImeInsets[ITYPE_BOTTOM_TAPPABLE_ELEMENT]=" +
+ "${windowLayoutParams.providedInternalImeInsets[ITYPE_BOTTOM_TAPPABLE_ELEMENT]}")
+ }
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index 8291475..7548398 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -312,8 +312,8 @@
if (!mTouchEnabled) {
return true;
}
- if (mIconLayoutBounds.contains((int) event.getX(), (int) event.getY())) {
- // Don't allow long pressing between icons.
+ if (mIconLayoutBounds.left <= event.getX() && event.getX() <= mIconLayoutBounds.right) {
+ // Don't allow long pressing between icons, or above/below them.
return true;
}
if (mControllerCallbacks.onTouchEvent(event)) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index e1ce898..6416720 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -18,6 +18,7 @@
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_ALLAPPS_BUTTON_TAP;
import static com.android.quickstep.AnimatedFloat.VALUE;
import android.annotation.NonNull;
@@ -341,7 +342,10 @@
}
public View.OnClickListener getAllAppsButtonClickListener() {
- return v -> mControllers.taskbarAllAppsController.show();
+ return v -> {
+ mActivity.getStatsLogManager().logger().log(LAUNCHER_TASKBAR_ALLAPPS_BUTTON_TAP);
+ mControllers.taskbarAllAppsController.show();
+ };
}
public View.OnLongClickListener getIconOnLongClickListener() {
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 2ae0646..b073b90 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -770,7 +770,7 @@
// We will handle the sysui flags based on the centermost task view.
mRecentsAnimationController.setUseLauncherSystemBarFlags(swipeUpThresholdPassed
|| (quickswitchThresholdPassed && centermostTaskFlags != 0));
- mRecentsAnimationController.setSplitScreenMinimized(swipeUpThresholdPassed);
+ mRecentsAnimationController.setSplitScreenMinimized(mContext, swipeUpThresholdPassed);
// Provide a hint to WM the direction that we will be settling in case the animation
// needs to be canceled
mRecentsAnimationController.setWillFinishToHome(swipeUpThresholdPassed);
@@ -1006,8 +1006,8 @@
return false;
}
- private GestureEndTarget calculateEndTarget(PointF velocity, float endVelocity, boolean isFling,
- boolean isCancel) {
+ private GestureEndTarget calculateEndTarget(PointF velocity, float endVelocity,
+ boolean isFlingY, boolean isCancel) {
if (mGestureState.isHandlingAtomicEvent()) {
// Button mode, this is only used to go to recents
return RECENTS;
@@ -1028,11 +1028,17 @@
goingToNewTask = false;
}
final boolean reachedOverviewThreshold = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW;
- if (!isFling) {
+ final boolean isFlingX = Math.abs(velocity.x) > mContext.getResources()
+ .getDimension(R.dimen.quickstep_fling_threshold_speed);
+ if (!isFlingY) {
if (isCancel) {
endTarget = LAST_TASK;
} else if (mDeviceState.isFullyGesturalNavMode()) {
- if (mIsMotionPaused) {
+ if (goingToNewTask && isFlingX) {
+ // Flinging towards new task takes precedence over mIsMotionPaused (which only
+ // checks y-velocity).
+ endTarget = NEW_TASK;
+ } else if (mIsMotionPaused) {
endTarget = RECENTS;
} else if (goingToNewTask) {
endTarget = NEW_TASK;
diff --git a/quickstep/src/com/android/quickstep/KtR.java b/quickstep/src/com/android/quickstep/KtR.java
index a768ef5..758c6e0 100644
--- a/quickstep/src/com/android/quickstep/KtR.java
+++ b/quickstep/src/com/android/quickstep/KtR.java
@@ -31,6 +31,7 @@
public static final class dimen {
public static int task_menu_spacing = R.dimen.task_menu_spacing;
public static int task_menu_horizontal_padding = R.dimen.task_menu_horizontal_padding;
+ public static int taskbar_ime_size = R.dimen.taskbar_ime_size;
}
public static final class layout {
diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
index cc79f4a..921674a 100644
--- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
+++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
@@ -29,7 +29,6 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Handler;
-import android.util.Log;
import android.util.MathUtils;
import android.util.Pair;
import android.view.RemoteAnimationTarget;
@@ -55,7 +54,7 @@
* the app window and plays the rest of app close transitions in one go.
*
* This animation is used only for apps that enable back dispatching via
- * {@link android.view.OnBackInvokedDispatcher}. The controller registers
+ * {@link android.window.OnBackInvokedDispatcher}. The controller registers
* an {@link IOnBackInvokedCallback} with WM Shell and receives back dispatches when a back
* navigation to launcher starts.
*
@@ -66,7 +65,6 @@
public class LauncherBackAnimationController {
private static final int CANCEL_TRANSITION_DURATION = 233;
private static final float MIN_WINDOW_SCALE = 0.7f;
- private static final String TAG = "LauncherBackAnimationController";
private final QuickstepTransitionManager mQuickstepTransitionManager;
private final Matrix mTransformMatrix = new Matrix();
/** The window position at the beginning of the back animation. */
@@ -115,12 +113,7 @@
* @param handler Handler to the thread to run the animations on.
*/
public void registerBackCallbacks(Handler handler) {
- SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.getNoCreate();
- if (systemUiProxy == null) {
- Log.e(TAG, "SystemUiProxy is null. Skip registering back invocation callbacks");
- return;
- }
- systemUiProxy.setBackToLauncherCallback(
+ SystemUiProxy.INSTANCE.get(mLauncher).setBackToLauncherCallback(
new IOnBackInvokedCallback.Stub() {
@Override
public void onBackCancelled() {
@@ -170,10 +163,7 @@
/** Unregisters the back to launcher callback in shell. */
public void unregisterBackCallbacks() {
- SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.getNoCreate();
- if (systemUiProxy != null) {
- systemUiProxy.clearBackToLauncherCallback();
- }
+ SystemUiProxy.INSTANCE.get(mLauncher).clearBackToLauncherCallback();
}
private void startBack(BackEvent backEvent) {
@@ -298,10 +288,7 @@
mInitialTouchPos.set(0, 0);
mAnimatorSetInProgress = false;
mSpringAnimationInProgress = false;
- SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.getNoCreate();
- if (systemUiProxy != null) {
- SystemUiProxy.INSTANCE.getNoCreate().onBackToLauncherAnimationFinished();
- }
+ SystemUiProxy.INSTANCE.get(mLauncher).onBackToLauncherAnimationFinished();
}
private void startTransitionAnimations(RectFSpringAnim springAnim, AnimatorSet anim) {
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
index c120b32..2007ee1 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
@@ -19,6 +19,7 @@
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
+import android.content.Context;
import android.os.RemoteException;
import android.util.Log;
import android.view.IRecentsAnimationController;
@@ -97,24 +98,20 @@
* Indicates that the gesture has crossed the window boundary threshold and we should minimize
* if we are in splitscreen.
*/
- public void setSplitScreenMinimized(boolean splitScreenMinimized) {
+ public void setSplitScreenMinimized(Context context, boolean splitScreenMinimized) {
if (!mAllowMinimizeSplitScreen) {
return;
}
if (mSplitScreenMinimized != splitScreenMinimized) {
mSplitScreenMinimized = splitScreenMinimized;
- UI_HELPER_EXECUTOR.execute(() -> {
- SystemUiProxy p = SystemUiProxy.INSTANCE.getNoCreate();
- if (p != null) {
- p.setSplitScreenMinimized(splitScreenMinimized);
- }
- });
+ UI_HELPER_EXECUTOR.execute(() -> SystemUiProxy.INSTANCE.get(context)
+ .setSplitScreenMinimized(splitScreenMinimized));
}
}
/**
* Remove task remote animation target from
- * {@link RecentsAnimationCallbacks#onTaskAppeared(RemoteAnimationTargetCompat)}}.
+ * {@link RecentsAnimationCallbacks#onTasksAppeared}}.
*/
@UiThread
public void removeTaskTarget(@NonNull RemoteAnimationTargetCompat target) {
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index b8334a9..f094d71 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -177,9 +177,8 @@
((RecentsActivity) activityInterface.getCreatedActivity()).startHome();
return;
}
- RemoteAnimationTarget[] nonAppTargets =
- SystemUiProxy.INSTANCE.getNoCreate()
- .onGoingToRecentsLegacy(false, nonHomeApps);
+ RemoteAnimationTarget[] nonAppTargets = SystemUiProxy.INSTANCE.get(mCtx)
+ .onGoingToRecentsLegacy(false, nonHomeApps);
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && activityInterface.isInLiveTileMode()
&& activityInterface.getCreatedActivity() != null) {
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index dd459f5..11f0ff3 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -84,6 +84,9 @@
public static final float QUICKSTEP_TOUCH_SLOP_RATIO_TWO_BUTTON = 9;
public static final float QUICKSTEP_TOUCH_SLOP_RATIO_GESTURAL = 2;
+ // Minimum angle of a gesture's coordinate where a release goes to overview.
+ public static final int OVERVIEW_MIN_DEGREES = 15;
+
private final RecentsAnimationDeviceState mDeviceState;
private final NavBarPosition mNavBarPosition;
private final TaskAnimationManager mTaskAnimationManager;
@@ -291,8 +294,9 @@
// the gesture (in which case mPassedPilferInputSlop starts as true).
boolean haveNotPassedSlopOnContinuedGesture =
!mPassedSlopOnThisGesture && mPassedPilferInputSlop;
+ double degrees = Math.toDegrees(Math.atan(upDist / horizontalDist));
boolean isLikelyToStartNewTask = haveNotPassedSlopOnContinuedGesture
- || horizontalDist > upDist;
+ || degrees <= OVERVIEW_MIN_DEGREES;
if (!mPassedPilferInputSlop) {
if (passedSlop) {
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index 13007ea..10c56c9 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -20,6 +20,7 @@
import static androidx.core.util.Preconditions.checkState;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_NON_ACTIONABLE;
+import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.ALL_APPS_CONTAINER;
import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.EXTENDED_CONTAINERS;
import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.FOLDER;
import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.SEARCH_RESULT_CONTAINER;
@@ -92,6 +93,7 @@
private static final int FOLDER_HIERARCHY_OFFSET = 100;
private static final int SEARCH_RESULT_HIERARCHY_OFFSET = 200;
private static final int EXTENDED_CONTAINERS_HIERARCHY_OFFSET = 300;
+ private static final int ALL_APPS_HIERARCHY_OFFSET = 400;
/**
* Flags for converting SearchAttribute to integer value.
@@ -632,6 +634,9 @@
} else if (info.getContainerInfo().getContainerCase() == EXTENDED_CONTAINERS) {
return info.getContainerInfo().getExtendedContainers().getContainerCase().getNumber()
+ EXTENDED_CONTAINERS_HIERARCHY_OFFSET;
+ } else if (info.getContainerInfo().getContainerCase() == ALL_APPS_CONTAINER) {
+ return info.getContainerInfo().getAllAppsContainer().getParentContainerCase()
+ .getNumber() + ALL_APPS_HIERARCHY_OFFSET;
} else {
return info.getContainerInfo().getContainerCase().getNumber();
}
diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
index 1631be0..6038a22 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
@@ -307,6 +307,7 @@
private void initMultipleOrientationListeners() {
mSharedPrefs.registerOnSharedPreferenceChangeListener(this);
mSettingsCache.register(ROTATION_SETTING_URI, mRotationChangeListener);
+ updateAutoRotateSetting();
}
private void destroyMultipleOrientationListeners() {
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
index 955fffc..244a794 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
@@ -26,6 +26,7 @@
import com.android.quickstep.util.RecentsOrientedState;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import java.util.HashMap;
import java.util.function.Consumer;
@@ -171,8 +172,14 @@
RunnableList endCallback = new RunnableList();
RecentsView recentsView = getRecentsView();
// Callbacks run from remote animation when recents animation not currently running
+ InteractionJankMonitorWrapper.begin(this,
+ InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER, "Enter form GroupedTaskView");
recentsView.getSplitPlaceholder().launchTasks(this /*groupedTaskView*/,
- success -> endCallback.executeAllAndDestroy(),
+ success -> {
+ endCallback.executeAllAndDestroy();
+ InteractionJankMonitorWrapper.end(
+ InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER);
+ },
false /* freezeTaskList */);
// Callbacks get run from recentsView for case when recents animation already running
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index aff9df6..5f715f8 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -175,6 +175,7 @@
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.systemui.shared.system.PackageManagerWrapper;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
@@ -2744,9 +2745,16 @@
mFirstFloatingTaskView.addAnimation(anim, startingTaskRect,
mTempRect, true /* fadeWithThumbnail */, true /* isStagedTask */);
}
+ InteractionJankMonitorWrapper.begin(this,
+ InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER, "First tile selected");
anim.addEndListener(success -> {
if (success) {
mSplitToast.show();
+ InteractionJankMonitorWrapper.end(
+ InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER);
+ } else {
+ InteractionJankMonitorWrapper.cancel(
+ InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER);
}
});
}
@@ -4040,9 +4048,11 @@
mSecondFloatingTaskView.setAlpha(1);
mSecondFloatingTaskView.addAnimation(pendingAnimation, secondTaskStartingBounds,
secondTaskEndingBounds, true /* fadeWithThumbnail */, false /* isStagedTask */);
- pendingAnimation.addEndListener(aBoolean ->
- mSplitSelectStateController.launchSplitTasks(
- aBoolean1 -> RecentsView.this.resetFromSplitSelectionState()));
+ pendingAnimation.addEndListener(aBoolean -> {
+ mSplitSelectStateController.launchSplitTasks(
+ aBoolean1 -> RecentsView.this.resetFromSplitSelectionState());
+ InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER);
+ });
if (containerTaskView.containsMultipleTasks()) {
// If we are launching from a child task, then only hide the thumbnail itself
mSecondSplitHiddenView = thumbnailView;
@@ -4050,6 +4060,8 @@
mSecondSplitHiddenView = containerTaskView;
}
mSecondSplitHiddenView.setVisibility(INVISIBLE);
+ InteractionJankMonitorWrapper.begin(this,
+ InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER, "Second tile selected");
pendingAnimation.buildAnim().start();
return true;
}
@@ -4486,10 +4498,7 @@
// Reset the minimized state since we force-toggled the minimized state when entering
// overview, but never actually finished the recents animation. This is a catch all for
// cases where we haven't already reset it.
- SystemUiProxy p = SystemUiProxy.INSTANCE.getNoCreate();
- if (p != null) {
- p.setSplitScreenMinimized(false);
- }
+ SystemUiProxy.INSTANCE.get(getContext()).setSplitScreenMinimized(false);
}
if (mRecentsAnimationController == null) {
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 9c5c643..8869ff1 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -20,7 +20,6 @@
import static android.widget.Toast.LENGTH_SHORT;
import static com.android.launcher3.AbstractFloatingView.TYPE_TASK_MENU;
-import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
import static com.android.launcher3.Utilities.comp;
import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
@@ -628,10 +627,7 @@
// Reset the minimized state since we force-toggled the minimized state when entering
// overview, but never actually finished the recents animation
- SystemUiProxy p = SystemUiProxy.INSTANCE.getNoCreate();
- if (p != null) {
- p.setSplitScreenMinimized(false);
- }
+ SystemUiProxy.INSTANCE.get(getContext()).setSplitScreenMinimized(false);
mIsClickableAsLiveTile = false;
RemoteAnimationTargets targets;
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index ca6712f..f7600ff 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -57,6 +57,7 @@
import com.android.launcher3.testcomponent.TestCommandReceiver;
import com.android.launcher3.util.Wait;
import com.android.launcher3.util.rule.FailureWatcher;
+import com.android.launcher3.util.rule.SamplerRule;
import com.android.launcher3.util.rule.ScreenRecordRule;
import com.android.quickstep.views.RecentsView;
@@ -111,7 +112,8 @@
}
mOrderSensitiveRules = RuleChain
- .outerRule(new NavigationModeSwitchRule(mLauncher))
+ .outerRule(new SamplerRule())
+ .around(new NavigationModeSwitchRule(mLauncher))
.around(new FailureWatcher(mDevice, mLauncher));
mOtherLauncherActivity = context.getPackageManager().queryIntentActivities(
diff --git a/res/drawable/gm_edit_24.xml b/res/drawable/gm_edit_24.xml
index 59a0dc2..f741333 100644
--- a/res/drawable/gm_edit_24.xml
+++ b/res/drawable/gm_edit_24.xml
@@ -5,6 +5,6 @@
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/textColorPrimaryInverse"
android:pathData="M20.41,4.94l-1.35,-1.35c-0.78,-0.78 -2.05,-0.78 -2.83,0L3,16.82L3,21h4.18L20.41,7.77c0.79,-0.78 0.79,-2.05 0,-2.83zM6.41,19.06L5,19v-1.36l9.82,-9.82 1.41,1.41 -9.82,9.83z"/>
</vector>
diff --git a/res/xml/device_profiles.xml b/res/xml/device_profiles.xml
index dd201e5..07ce598 100644
--- a/res/xml/device_profiles.xml
+++ b/res/xml/device_profiles.xml
@@ -145,6 +145,8 @@
launcher:numFolderColumns="3"
launcher:numHotseatIcons="6"
launcher:numAllAppsColumns="6"
+ launcher:isScalable="true"
+ launcher:devicePaddingId="@xml/paddings_6x5"
launcher:dbFile="launcher_6_by_5.db"
launcher:defaultLayoutId="@xml/default_workspace_6x5"
launcher:deviceCategory="tablet" >
@@ -153,14 +155,29 @@
launcher:name="Tablet"
launcher:minWidthDps="900"
launcher:minHeightDps="820"
- launcher:minCellHeight="104"
- launcher:minCellWidth="80"
+ launcher:minCellHeight="120"
+ launcher:minCellWidth="102"
+ launcher:minCellHeightLandscape="104"
+ launcher:minCellWidthLandscape="120"
launcher:iconImageSize="60"
launcher:iconTextSize="14"
- launcher:borderSpace="16"
+ launcher:borderSpaceHorizontal="16"
+ launcher:borderSpaceVertical="64"
+ launcher:borderSpaceLandscapeHorizontal="64"
+ launcher:borderSpaceLandscapeVertical="16"
+ launcher:horizontalMargin="54"
+ launcher:horizontalMarginLandscape="120"
+ launcher:allAppsCellWidth="96"
+ launcher:allAppsCellHeight="142"
+ launcher:allAppsCellWidthLandscape="126"
+ launcher:allAppsCellHeightLandscape="126"
launcher:allAppsIconSize="60"
launcher:allAppsIconTextSize="14"
- launcher:allAppsBorderSpace="16"
+ launcher:allAppsBorderSpaceHorizontal="8"
+ launcher:allAppsBorderSpaceVertical="16"
+ launcher:allAppsBorderSpaceLandscape="16"
+ launcher:hotseatBorderSpace="58"
+ launcher:hotseatBorderSpaceLandscape="50.4"
launcher:canBeDefault="true" />
</grid-option>
diff --git a/res/xml/paddings_6x5.xml b/res/xml/paddings_6x5.xml
new file mode 100644
index 0000000..a958ec7
--- /dev/null
+++ b/res/xml/paddings_6x5.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+
+<device-paddings xmlns:launcher="http://schemas.android.com/apk/res-auto" >
+
+ <!-- Some non default screen sizes -->
+ <device-padding
+ launcher:maxEmptySpace="30dp">
+ <workspaceTopPadding
+ launcher:a="0.34"
+ launcher:b="0"/>
+ <workspaceBottomPadding
+ launcher:a="0.26"
+ launcher:b="0"/>
+ <hotseatBottomPadding
+ launcher:a="0.4"
+ launcher:b="0"/>
+ </device-padding>
+
+ <device-padding
+ launcher:maxEmptySpace="80dp">
+ <workspaceTopPadding
+ launcher:a="0"
+ launcher:b="20dp"/>
+ <workspaceBottomPadding
+ launcher:a="0.4"
+ launcher:b="0"
+ launcher:c="20dp"/>
+ <hotseatBottomPadding
+ launcher:a="0.6"
+ launcher:b="0"
+ launcher:c="20dp"/>
+ </device-padding>
+
+ <device-padding
+ launcher:maxEmptySpace="280dp">
+ <workspaceTopPadding
+ launcher:a="0"
+ launcher:b="112dp"/>
+ <workspaceBottomPadding
+ launcher:a="0.4"
+ launcher:b="0"
+ launcher:c="112dp"/>
+ <hotseatBottomPadding
+ launcher:a="0.6"
+ launcher:b="0"
+ launcher:c="112dp"/>
+ </device-padding>
+
+ <device-padding
+ launcher:maxEmptySpace="9999dp">
+ <workspaceTopPadding
+ launcher:a="0.40"
+ launcher:c="36dp"/>
+ <workspaceBottomPadding
+ launcher:a="0.60"
+ launcher:c="36dp"/>
+ <hotseatBottomPadding
+ launcher:a="0"
+ launcher:b="36dp"/>
+ </device-padding>
+</device-paddings>
\ No newline at end of file
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 5a59de5..33bb0a5 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -928,6 +928,13 @@
return workspaceSpringLoadShrunkBottom;
}
+ /**
+ * Gets the minimum visible amount of the next workspace page when in the spring-loaded state.
+ */
+ public float getWorkspaceSpringLoadedMinimumNextPageVisible() {
+ return getCellSize().x / 2f;
+ }
+
public int getWorkspaceWidth() {
return getWorkspaceWidth(getTotalWorkspacePadding());
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index fd9d0e1..b6a05b0 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -3235,12 +3235,12 @@
/** Pauses view updates that should not be run during the app launch animation. */
public void pauseExpensiveViewUpdates() {
// Pause page indicator animations as they lead to layer trashing.
- mWorkspace.getPageIndicator().pauseAnimations();
+ getWorkspace().getPageIndicator().pauseAnimations();
}
/** Resumes view updates at the end of the app launch animation. */
public void resumeExpensiveViewUpdates() {
- mWorkspace.getPageIndicator().skipAnimationsToEnd();
+ getWorkspace().getPageIndicator().skipAnimationsToEnd();
}
}
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 462daf5..12eb837 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -313,7 +313,7 @@
* Find empty space on the workspace and returns the screenId.
*/
protected int findSpaceOnWorkspace(ItemInfo info, int[] outCoordinates) {
- Workspace workspace = mContext.getWorkspace();
+ Workspace<?> workspace = mContext.getWorkspace();
IntArray workspaceScreens = workspace.getScreenOrder();
int screenId;
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index 3dfece7..a11bd4f 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -23,7 +23,6 @@
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems;
import static com.android.launcher3.model.ModelUtils.getMissingHotseatRanks;
-import static com.android.launcher3.model.ModelUtils.sortWorkspaceItemsSpatially;
import android.annotation.TargetApi;
import android.app.Fragment;
@@ -420,8 +419,6 @@
currentWorkspaceItems, otherWorkspaceItems);
filterCurrentWorkspaceItems(currentScreenIds, dataModel.appWidgets, currentAppWidgets,
otherAppWidgets);
-
- sortWorkspaceItemsSpatially(mIdp, currentWorkspaceItems);
for (ItemInfo itemInfo : currentWorkspaceItems) {
switch (itemInfo.itemType) {
case Favorites.ITEM_TYPE_APPLICATION:
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index 7ba2317..fe9b633 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -42,8 +42,10 @@
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
+import android.util.SparseArray;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.core.util.Pair;
import com.android.launcher3.InvariantDeviceProfile;
@@ -94,6 +96,8 @@
private final InstantAppResolver mInstantAppResolver;
private final IconProvider mIconProvider;
+ private final SparseArray<BitmapInfo> mWidgetCategoryBitmapInfos;
+
private int mPendingIconRequestCount = 0;
public IconCache(Context context, InvariantDeviceProfile idp) {
@@ -111,6 +115,7 @@
mUserManager = UserCache.INSTANCE.get(mContext);
mInstantAppResolver = InstantAppResolver.newInstance(mContext);
mIconProvider = iconProvider;
+ mWidgetCategoryBitmapInfos = new SparseArray<>();
}
@Override
@@ -477,13 +482,39 @@
CacheEntry entry = getEntryForPackageLocked(
infoInOut.packageName, infoInOut.user, useLowResIcon);
applyCacheEntry(entry, infoInOut);
- if (infoInOut.widgetCategory != NO_CATEGORY) {
- WidgetSection widgetSection = WidgetSections.getWidgetSections(mContext)
- .get(infoInOut.widgetCategory);
- infoInOut.title = mContext.getString(widgetSection.mSectionTitle);
- infoInOut.contentDescription = mPackageManager.getUserBadgedLabel(
- infoInOut.title, infoInOut.user);
+ if (infoInOut.widgetCategory == NO_CATEGORY) {
+ return;
}
+
+ WidgetSection widgetSection = WidgetSections.getWidgetSections(mContext)
+ .get(infoInOut.widgetCategory);
+ infoInOut.title = mContext.getString(widgetSection.mSectionTitle);
+ infoInOut.contentDescription = mPackageManager.getUserBadgedLabel(
+ infoInOut.title, infoInOut.user);
+ final BitmapInfo cachedBitmap = mWidgetCategoryBitmapInfos.get(infoInOut.widgetCategory);
+ if (cachedBitmap != null) {
+ infoInOut.bitmap = getBadgedIcon(cachedBitmap, infoInOut.user);
+ return;
+ }
+
+ try (LauncherIcons li = LauncherIcons.obtain(mContext)) {
+ final BitmapInfo tempBitmap = li.createBadgedIconBitmap(
+ mContext.getDrawable(widgetSection.mSectionDrawable),
+ new BaseIconFactory.IconOptions().setShrinkNonAdaptiveIcons(false));
+ mWidgetCategoryBitmapInfos.put(infoInOut.widgetCategory, tempBitmap);
+ infoInOut.bitmap = getBadgedIcon(tempBitmap, infoInOut.user);
+ } catch (Exception e) {
+ Log.e(TAG, "Error initializing bitmap for icons with widget category", e);
+ }
+
+ }
+
+ private synchronized BitmapInfo getBadgedIcon(@Nullable final BitmapInfo bitmap,
+ @NonNull final UserHandle user) {
+ if (bitmap == null) {
+ return getDefaultIcon(user);
+ }
+ return bitmap.withFlags(getUserFlagOpLocked(user));
}
protected void applyCacheEntry(CacheEntry entry, ItemInfoWithIcon info) {
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 9af72c3..2b6e426 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -586,6 +586,9 @@
@UiEvent(doc = "User clicked on IME quicksearch button.")
LAUNCHER_ALLAPPS_QUICK_SEARCH_WITH_IME(1047),
+
+ @UiEvent(doc = "User tapped taskbar All Apps button.")
+ LAUNCHER_TASKBAR_ALLAPPS_BUTTON_TAP(1057),
;
// ADD MORE
diff --git a/src/com/android/launcher3/model/BaseLoaderResults.java b/src/com/android/launcher3/model/BaseLoaderResults.java
index 5b278ab..6c4cfb9 100644
--- a/src/com/android/launcher3/model/BaseLoaderResults.java
+++ b/src/com/android/launcher3/model/BaseLoaderResults.java
@@ -18,7 +18,6 @@
import static com.android.launcher3.model.ItemInstallQueue.FLAG_LOADER_RUNNING;
import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems;
-import static com.android.launcher3.model.ModelUtils.sortWorkspaceItemsSpatially;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import android.os.Process;
@@ -27,6 +26,8 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel.CallbackTask;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
import com.android.launcher3.model.data.AppInfo;
@@ -110,6 +111,42 @@
public abstract void bindWidgets();
+ /**
+ * Sorts the set of items by hotseat, workspace (spatially from top to bottom, left to right)
+ */
+ protected void sortWorkspaceItemsSpatially(InvariantDeviceProfile profile,
+ ArrayList<ItemInfo> workspaceItems) {
+ final int screenCols = profile.numColumns;
+ final int screenCellCount = profile.numColumns * profile.numRows;
+ Collections.sort(workspaceItems, (lhs, rhs) -> {
+ if (lhs.container == rhs.container) {
+ // Within containers, order by their spatial position in that container
+ switch (lhs.container) {
+ case LauncherSettings.Favorites.CONTAINER_DESKTOP: {
+ int lr = (lhs.screenId * screenCellCount + lhs.cellY * screenCols
+ + lhs.cellX);
+ int rr = (rhs.screenId * screenCellCount + +rhs.cellY * screenCols
+ + rhs.cellX);
+ return Integer.compare(lr, rr);
+ }
+ case LauncherSettings.Favorites.CONTAINER_HOTSEAT: {
+ // We currently use the screen id as the rank
+ return Integer.compare(lhs.screenId, rhs.screenId);
+ }
+ default:
+ if (FeatureFlags.IS_STUDIO_BUILD) {
+ throw new RuntimeException(
+ "Unexpected container type when sorting workspace items.");
+ }
+ return 0;
+ }
+ } else {
+ // Between containers, order by hotseat, desktop
+ return Integer.compare(lhs.container, rhs.container);
+ }
+ });
+ }
+
protected void executeCallbacksTask(CallbackTask task, Executor executor) {
executor.execute(() -> {
if (mMyBindingId != mBgDataModel.lastBindId) {
@@ -131,7 +168,7 @@
return idleLock;
}
- private static class WorkspaceBinder {
+ private class WorkspaceBinder {
private final Executor mUiExecutor;
private final Callbacks mCallbacks;
diff --git a/src/com/android/launcher3/model/ModelUtils.java b/src/com/android/launcher3/model/ModelUtils.java
index ef5eef1..df6768d 100644
--- a/src/com/android/launcher3/model/ModelUtils.java
+++ b/src/com/android/launcher3/model/ModelUtils.java
@@ -23,10 +23,8 @@
import android.os.Process;
import android.util.Log;
-import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.model.data.ItemInfo;
@@ -92,42 +90,6 @@
}
/**
- * Sorts the set of items by hotseat, workspace (spatially from top to bottom, left to right)
- */
- public static void sortWorkspaceItemsSpatially(InvariantDeviceProfile profile,
- ArrayList<ItemInfo> workspaceItems) {
- final int screenCols = profile.numColumns;
- final int screenCellCount = profile.numColumns * profile.numRows;
- Collections.sort(workspaceItems, (lhs, rhs) -> {
- if (lhs.container == rhs.container) {
- // Within containers, order by their spatial position in that container
- switch (lhs.container) {
- case LauncherSettings.Favorites.CONTAINER_DESKTOP: {
- int lr = (lhs.screenId * screenCellCount + lhs.cellY * screenCols
- + lhs.cellX);
- int rr = (rhs.screenId * screenCellCount + +rhs.cellY * screenCols
- + rhs.cellX);
- return Integer.compare(lr, rr);
- }
- case LauncherSettings.Favorites.CONTAINER_HOTSEAT: {
- // We currently use the screen id as the rank
- return Integer.compare(lhs.screenId, rhs.screenId);
- }
- default:
- if (FeatureFlags.IS_STUDIO_BUILD) {
- throw new RuntimeException(
- "Unexpected container type when sorting workspace items.");
- }
- return 0;
- }
- } else {
- // Between containers, order by hotseat, desktop
- return Integer.compare(lhs.container, rhs.container);
- }
- });
- }
-
- /**
* Iterates though current workspace items and returns available hotseat ranks for prediction.
*/
public static IntArray getMissingHotseatRanks(List<ItemInfo> items, int len) {
diff --git a/src/com/android/launcher3/popup/ArrowPopup.java b/src/com/android/launcher3/popup/ArrowPopup.java
index df8a3e2..196cc56 100644
--- a/src/com/android/launcher3/popup/ArrowPopup.java
+++ b/src/com/android/launcher3/popup/ArrowPopup.java
@@ -602,6 +602,7 @@
mIsAboveIcon = y > dragLayer.getTop() + insets.top;
if (!mIsAboveIcon) {
y = mTempRect.top + iconHeight + extraVerticalSpace;
+ height -= extraVerticalSpace;
}
// Insets are added later, so subtract them now.
@@ -609,7 +610,7 @@
y -= insets.top;
mGravity = 0;
- if (y + height > dragLayer.getBottom() - insets.bottom) {
+ if ((insets.top + y + height) > (dragLayer.getBottom() - insets.bottom)) {
// The container is opening off the screen, so just center it in the drag layer instead.
mGravity = Gravity.CENTER_VERTICAL;
// Put the container next to the icon, preferring the right side in ltr (left in rtl).
diff --git a/src/com/android/launcher3/states/SpringLoadedState.java b/src/com/android/launcher3/states/SpringLoadedState.java
index 52356ce..7e9d56d 100644
--- a/src/com/android/launcher3/states/SpringLoadedState.java
+++ b/src/com/android/launcher3/states/SpringLoadedState.java
@@ -53,7 +53,15 @@
float shrunkTop = grid.getWorkspaceSpringLoadShrunkTop();
float shrunkBottom = grid.getWorkspaceSpringLoadShrunkBottom();
- float scale = (shrunkBottom - shrunkTop) / ws.getNormalChildHeight();
+ float scale = Math.min((shrunkBottom - shrunkTop) / ws.getNormalChildHeight(), 1f);
+
+ // Reduce scale if next pages would not be visible after scaling the workspace
+ float scaledWorkspaceWidth = ws.getWidth() * scale;
+ float maxAvailableWidth =
+ ws.getWidth() - (2 * grid.getWorkspaceSpringLoadedMinimumNextPageVisible());
+ if (scaledWorkspaceWidth > maxAvailableWidth) {
+ scale *= maxAvailableWidth / scaledWorkspaceWidth;
+ }
float halfHeight = ws.getHeight() / 2;
float myCenter = ws.getTop() + halfHeight;
diff --git a/src/com/android/launcher3/util/GridOccupancy.java b/src/com/android/launcher3/util/GridOccupancy.java
index 9c752a7..1301460 100644
--- a/src/com/android/launcher3/util/GridOccupancy.java
+++ b/src/com/android/launcher3/util/GridOccupancy.java
@@ -7,7 +7,7 @@
/**
* Utility object to manage the occupancy in a grid.
*/
-public class GridOccupancy {
+public class GridOccupancy extends AbsGridOccupancy {
private final int mCountX;
private final int mCountY;
@@ -30,24 +30,7 @@
* @return true if a vacant cell was found
*/
public boolean findVacantCell(int[] vacantOut, int spanX, int spanY) {
- for (int y = 0; (y + spanY) <= mCountY; y++) {
- for (int x = 0; (x + spanX) <= mCountX; x++) {
- boolean available = !cells[x][y];
- out:
- for (int i = x; i < x + spanX; i++) {
- for (int j = y; j < y + spanY; j++) {
- available = available && !cells[i][j];
- if (!available) break out;
- }
- }
- if (available) {
- vacantOut[0] = x;
- vacantOut[1] = y;
- return true;
- }
- }
- }
- return false;
+ return super.findVacantCell(vacantOut, cells, mCountX, mCountY, spanX, spanY);
}
public void copyTo(GridOccupancy dest) {
diff --git a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
index 1f6551e..130ee3a 100644
--- a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
@@ -18,7 +18,6 @@
import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon;
import static com.android.launcher3.icons.FastBitmapDrawable.getDisabledColorFilter;
-import static com.android.launcher3.widget.WidgetSections.getWidgetSections;
import android.content.Context;
import android.graphics.Canvas;
@@ -341,8 +340,6 @@
if (mInfo.pendingItemInfo.widgetCategory == WidgetSections.NO_CATEGORY) {
return null;
}
- Context context = getContext();
- return context.getDrawable(getWidgetSections(context).get(
- mInfo.pendingItemInfo.widgetCategory).mSectionDrawable);
+ return mInfo.pendingItemInfo.newIcon(getContext());
}
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListHeader.java b/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
index 932e06d..b0e2ec1 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
@@ -15,7 +15,6 @@
*/
package com.android.launcher3.widget.picker;
-import static com.android.launcher3.widget.WidgetSections.NO_CATEGORY;
import android.content.Context;
import android.content.res.Resources;
@@ -43,8 +42,6 @@
import com.android.launcher3.model.data.PackageItemInfo;
import com.android.launcher3.util.PluralMessageFormat;
import com.android.launcher3.views.ActivityContext;
-import com.android.launcher3.widget.WidgetSections;
-import com.android.launcher3.widget.WidgetSections.WidgetSection;
import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
import com.android.launcher3.widget.model.WidgetsListSearchHeaderEntry;
@@ -177,13 +174,7 @@
private void setIcon(PackageItemInfo info) {
Drawable icon;
- if (info.widgetCategory == NO_CATEGORY) {
- icon = info.newIcon(getContext());
- } else {
- WidgetSection widgetSection = WidgetSections.getWidgetSections(getContext())
- .get(info.widgetCategory);
- icon = getContext().getDrawable(widgetSection.mSectionDrawable);
- }
+ icon = info.newIcon(getContext());
applyDrawables(icon);
mIconDrawable = icon;
if (mIconDrawable != null) {
diff --git a/src_shortcuts_overrides/com/android/launcher3/util/AbsGridOccupancy.java b/src_shortcuts_overrides/com/android/launcher3/util/AbsGridOccupancy.java
new file mode 100644
index 0000000..968b281
--- /dev/null
+++ b/src_shortcuts_overrides/com/android/launcher3/util/AbsGridOccupancy.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2022 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.launcher3.util;
+
+/**
+ * Defines method to find the next vacant cell on a grid.
+ * This uses the default top-down, left-right approach and can be over-written through
+ * code swaps in different launchers.
+ */
+public abstract class AbsGridOccupancy {
+ /**
+ * Find the first vacant cell, if there is one.
+ *
+ * @param vacantOut Holds the x and y coordinate of the vacant cell
+ * @param spanX Horizontal cell span.
+ * @param spanY Vertical cell span.
+ *
+ * @return true if a vacant cell was found
+ */
+ protected boolean findVacantCell(int[] vacantOut, boolean[][] cells, int countX, int countY,
+ int spanX, int spanY) {
+ for (int y = 0; (y + spanY) <= countY; y++) {
+ for (int x = 0; (x + spanX) <= countX; x++) {
+ boolean available = !cells[x][y];
+ out:
+ for (int i = x; i < x + spanX; i++) {
+ for (int j = y; j < y + spanY; j++) {
+ available = available && !cells[i][j];
+ if (!available) break out;
+ }
+ }
+ if (available) {
+ vacantOut[0] = x;
+ vacantOut[1] = y;
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+}
diff --git a/tests/Android.bp b/tests/Android.bp
index 7542d04..54cded0 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -37,6 +37,7 @@
"src/com/android/launcher3/util/WidgetUtils.java",
"src/com/android/launcher3/util/rule/FailureWatcher.java",
"src/com/android/launcher3/util/rule/LauncherActivityRule.java",
+ "src/com/android/launcher3/util/rule/SamplerRule.java",
"src/com/android/launcher3/util/rule/ScreenRecordRule.java",
"src/com/android/launcher3/util/rule/ShellCommandRule.java",
"src/com/android/launcher3/util/rule/SimpleActivityRule.java",
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 136f115..7080c85 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -68,6 +68,7 @@
import com.android.launcher3.util.WidgetUtils;
import com.android.launcher3.util.rule.FailureWatcher;
import com.android.launcher3.util.rule.LauncherActivityRule;
+import com.android.launcher3.util.rule.SamplerRule;
import com.android.launcher3.util.rule.ScreenRecordRule;
import com.android.launcher3.util.rule.ShellCommandRule;
import com.android.launcher3.util.rule.TestStabilityRule;
@@ -227,7 +228,8 @@
@Rule
public TestRule mOrderSensitiveRules = RuleChain
- .outerRule(new TestStabilityRule())
+ .outerRule(new SamplerRule())
+ .around(new TestStabilityRule())
.around(mActivityMonitor)
.around(getRulesInsideActivityMonitor());
diff --git a/tests/src/com/android/launcher3/util/rule/SamplerRule.java b/tests/src/com/android/launcher3/util/rule/SamplerRule.java
new file mode 100644
index 0000000..6125f2a
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/rule/SamplerRule.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2022 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.launcher3.util.rule;
+
+import android.os.SystemClock;
+
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * A rule that generates a file that helps diagnosing cases when the test process was terminated
+ * because the test execution took too long, and tests that ran for too long even without being
+ * terminated. If the process was terminated or the test was long, the test leaves an artifact with
+ * stack traces of all threads, every SAMPLE_INTERVAL_MS. This will help understanding where we
+ * stuck.
+ */
+public class SamplerRule implements TestRule {
+ private static final int TOO_LONG_TEST_MS = 180000;
+ private static final int SAMPLE_INTERVAL_MS = 3000;
+
+ public static Thread startThread(Description description) {
+ Thread thread =
+ new Thread() {
+ @Override
+ public void run() {
+ // Write all-threads stack stace every SAMPLE_INTERVAL_MS while the test
+ // is running.
+ // After the test finishes, delete that file. If the test process is
+ // terminated due to timeout, the trace file won't be deleted.
+ final File file = getFile();
+
+ final long startTime = SystemClock.elapsedRealtime();
+ try (OutputStreamWriter outputStreamWriter =
+ new OutputStreamWriter(
+ new BufferedOutputStream(
+ new FileOutputStream(file)))) {
+ writeSamples(outputStreamWriter);
+ } catch (IOException | InterruptedException e) {
+ // Simply suppressing the exceptions, nothing to do here.
+ } finally {
+ // If the process is not killed, then there was no test timeout, and
+ // we are not interested in the trace file, unless the test ran too
+ // long.
+ if (SystemClock.elapsedRealtime() - startTime < TOO_LONG_TEST_MS) {
+ file.delete();
+ }
+ }
+ }
+
+ private File getFile() {
+ final String strDate = new SimpleDateFormat("HH:mm:ss").format(new Date());
+
+ final String descStr = description.getTestClass().getSimpleName() + "."
+ + description.getMethodName();
+ return artifactFile(
+ "ThreadStackSamples-" + strDate + "-" + descStr + ".txt");
+ }
+
+ private void writeSamples(OutputStreamWriter writer)
+ throws IOException, InterruptedException {
+ int count = 0;
+ while (true) {
+ writer.write(
+ "#"
+ + (count++)
+ + " =============================================\r\n");
+ for (StackTraceElement[] stack : getAllStackTraces().values()) {
+ writer.write("---------------------\r\n");
+ for (StackTraceElement frame : stack) {
+ writer.write(frame.toString() + "\r\n");
+ }
+ }
+ writer.flush();
+
+ sleep(SAMPLE_INTERVAL_MS);
+ }
+ }
+ };
+
+ thread.start();
+ return thread;
+ }
+
+ @Override
+ public Statement apply(Statement base, Description description) {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ final Thread traceThread = startThread(description);
+ try {
+ base.evaluate();
+ } finally {
+ traceThread.interrupt();
+ traceThread.join();
+ }
+ }
+ };
+ }
+
+ private static File artifactFile(String fileName) {
+ return new File(
+ InstrumentationRegistry.getInstrumentation().getTargetContext().getFilesDir(),
+ fileName);
+ }
+}