Merge "Moving startBinding and clearPendingBinds to ModelCallbacks" into main
diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig
index 760d8ac..9080284 100644
--- a/aconfig/launcher.aconfig
+++ b/aconfig/launcher.aconfig
@@ -76,3 +76,10 @@
description: "Enables logging of Launcher restore metrics to the Backup & Restore team"
bug: "307527314"
}
+
+flag {
+ name: "enable_unfolded_two_pane_picker"
+ namespace: "launcher"
+ description: "Enables two pane widget picker for unfolded foldables"
+ bug: "313922374"
+}
diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto
index 5a9e147..8240f11 100644
--- a/protos/launcher_atom.proto
+++ b/protos/launcher_atom.proto
@@ -138,7 +138,7 @@
}
}
-// Next value 54
+// Next value 55
enum Attribute {
option allow_alias = true;
@@ -200,6 +200,7 @@
DATA_SOURCE_APPSEARCH_CATEGORY_SRP_PREVIEW = 48;
DATA_SOURCE_APPSEARCH_ENTITY_SRP_PREVIEW = 49;
DATA_SOURCE_AIAI_SEARCH_ROOT = 47;
+ DATA_SOURCE_LAUNCHER = 54;
// Web suggestions provided by AGA
ALL_APPS_SEARCH_RESULT_WEB_SUGGEST = 39;
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index d6ab54e..8db63e3 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -55,6 +55,7 @@
import static com.android.launcher3.config.FeatureFlags.KEYGUARD_ANIMATION;
import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
+import static com.android.launcher3.testing.shared.TestProtocol.WALLPAPER_OPEN_ANIMATION_FINISHED_MESSAGE;
import static com.android.launcher3.util.DisplayController.isTransientTaskbar;
import static com.android.launcher3.util.Executors.ORDERED_BG_EXECUTOR;
import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
@@ -120,6 +121,7 @@
import com.android.launcher3.LauncherAnimationRunner.RemoteAnimationFactory;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorListeners;
+import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.launcher3.model.data.ItemInfo;
@@ -1651,6 +1653,15 @@
if (launcherIsForceInvisibleOrOpening) {
addCujInstrumentation(anim, playFallBackAnimation
? CUJ_APP_CLOSE_TO_HOME_FALLBACK : CUJ_APP_CLOSE_TO_HOME);
+
+ anim.addListener(new AnimationSuccessListener() {
+ @Override
+ public void onAnimationSuccess(Animator animator) {
+ AccessibilityManagerCompat.sendTestProtocolEventToTest(
+ mLauncher, WALLPAPER_OPEN_ANIMATION_FINISHED_MESSAGE);
+ }
+ });
+
// Only register the content animation for cancellation when state changes
mLauncher.getStateManager().setCurrentAnimation(anim);
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
index cbb991d..b29ce6b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
@@ -133,11 +133,20 @@
GroupTask task = mControllerCallbacks.getTaskAt(index);
if (task == null) {
return Math.max(0, index);
- } else if (mOnDesktop) {
+ }
+ Task task2 = task.task2;
+ int runningTaskId = ActivityManagerWrapper.getInstance().getRunningTask().taskId;
+ if (runningTaskId == task.task1.key.id
+ || (task2 != null && runningTaskId == task2.key.id)) {
+ // Ignore attempts to run the selected task if it is already running.
+ return -1;
+ }
+
+ if (mOnDesktop) {
UI_HELPER_EXECUTOR.execute(() ->
SystemUiProxy.INSTANCE.get(mKeyboardQuickSwitchView.getContext())
.showDesktopApp(task.task1.key.id));
- } else if (task.task2 == null) {
+ } else if (task2 == null) {
UI_HELPER_EXECUTOR.execute(() ->
ActivityManagerWrapper.getInstance().startActivityFromRecents(
task.task1.key,
@@ -145,8 +154,7 @@
taskView == null ? mKeyboardQuickSwitchView : taskView, null)
.options));
} else {
- mControllers.uiController.launchSplitTasks(
- taskView == null ? mKeyboardQuickSwitchView : taskView, task);
+ mControllers.uiController.launchSplitTasks(task);
}
return -1;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index bbe73ff..b4754c6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -26,7 +26,6 @@
import android.os.RemoteException;
import android.util.Log;
import android.view.TaskTransitionSpec;
-import android.view.View;
import android.view.WindowManagerGlobal;
import androidx.annotation.NonNull;
@@ -386,8 +385,8 @@
}
@Override
- public void launchSplitTasks(@NonNull View taskView, @NonNull GroupTask groupTask) {
- mLauncher.launchSplitTasks(taskView, groupTask);
+ public void launchSplitTasks(@NonNull GroupTask groupTask) {
+ mLauncher.launchSplitTasks(groupTask);
}
@Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 988ef80..60ee38f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -203,11 +203,16 @@
Display display = windowContext.getDisplay();
Context c = getApplicationContext();
mWindowManager = c.getSystemService(WindowManager.class);
- mLeftCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT);
- mRightCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT);
+
+ boolean phoneMode = TaskbarManager.isPhoneMode(mDeviceProfile);
+ mLeftCorner = phoneMode
+ ? null
+ : display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT);
+ mRightCorner = phoneMode
+ ? null
+ : display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT);
// Inflate views.
- boolean phoneMode = TaskbarManager.isPhoneMode(mDeviceProfile);
int taskbarLayout = DisplayController.isTransientTaskbar(this) && !phoneMode
? R.layout.transient_taskbar
: R.layout.taskbar;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index 445b312..aee3c6f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -297,11 +297,9 @@
}
/**
- * Launches the focused task in splitscreen.
- *
- * No-op if the view is not yet open.
+ * Launches the given task in split-screen.
*/
- public void launchSplitTasks(@NonNull View taskview, @NonNull GroupTask groupTask) { }
+ public void launchSplitTasks(@NonNull GroupTask groupTask) { }
/**
* Returns the matching view (if any) in the taskbar.
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 5b0c8c3..89b7fa4 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -19,6 +19,7 @@
import static android.os.Trace.TRACE_TAG_APP;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_OPTIMIZE_MEASURE;
import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED;
+
import static com.android.app.animation.Interpolators.EMPHASIZED;
import static com.android.launcher3.LauncherConstants.SavedInstanceKeys.PENDING_SPLIT_SELECT_INFO;
import static com.android.launcher3.LauncherConstants.SavedInstanceKeys.RUNTIME_STATE;
@@ -34,8 +35,6 @@
import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_HOME_TRANSITION_LISTENER;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
import static com.android.launcher3.popup.QuickstepSystemShortcut.getSplitSelectShortcutByPosition;
@@ -1263,24 +1262,19 @@
/**
* Launches the given {@link GroupTask} in splitscreen.
- *
- * If the second split task is missing, launches the first task normally.
*/
- public void launchSplitTasks(@NonNull View taskView, @NonNull GroupTask groupTask) {
- if (groupTask.task2 == null) {
- UI_HELPER_EXECUTOR.execute(() ->
- ActivityManagerWrapper.getInstance().startActivityFromRecents(
- groupTask.task1.key,
- getActivityLaunchOptions(taskView, null).options));
- return;
- }
+ public void launchSplitTasks(@NonNull GroupTask groupTask) {
+ // Top/left and bottom/right tasks respectively.
+ Task task1 = groupTask.task1;
+ // task2 should never be null when calling this method. Allow a crash to catch invalid calls
+ Task task2 = groupTask.task2;
mSplitSelectStateController.launchExistingSplitPair(
null /* launchingTaskView */,
- groupTask.task1.key.id,
- groupTask.task2.key.id,
+ task1.key.id,
+ task2.key.id,
SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT,
/* callback= */ success -> mSplitSelectStateController.resetState(),
- /* freezeTaskList= */ true,
+ /* freezeTaskList= */ false,
groupTask.mSplitBounds == null
? SNAP_TO_50_50
: groupTask.mSplitBounds.snapPosition);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsUI.java b/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsUI.java
index 6671fc3..301fbe4 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsUI.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsUI.java
@@ -359,7 +359,7 @@
category.addPreference(createSeekBarPreference("Haptic hint scale exponent",
1, 5, 1, LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_SCALE_EXPONENT));
category.addPreference(createSeekBarPreference("Haptic hint iterations (12 ms each)",
- 0, 100, 1, LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_ITERATIONS));
+ 0, 200, 1, LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_ITERATIONS));
category.addPreference(createSeekBarPreference("Haptic hint delay (ms)",
0, 400, 1, LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_DELAY));
}
diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
index 5568459..9e58160 100644
--- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
+++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
@@ -55,6 +55,7 @@
import com.android.launcher3.QuickstepTransitionManager;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.taskbar.LauncherTaskbarUIController;
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.systemui.shared.system.QuickStepContract;
@@ -416,6 +417,10 @@
if (mLauncher.isDestroyed()) {
return;
}
+ LauncherTaskbarUIController taskbarUIController = mLauncher.getTaskbarUIController();
+ if (taskbarUIController != null) {
+ taskbarUIController.onLauncherVisibilityChanged(true);
+ }
// TODO: Catch the moment when launcher becomes visible after the top app un-occludes
// launcher and start animating afterwards. Currently we occasionally get a flicker from
// animating when launcher is still invisible.
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 39edd70..22163b9 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -18,9 +18,11 @@
import static android.os.Trace.TRACE_TAG_APP;
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
+
import static com.android.launcher3.QuickstepTransitionManager.RECENTS_LAUNCH_DURATION;
import static com.android.launcher3.QuickstepTransitionManager.STATUS_BAR_TRANSITION_DURATION;
import static com.android.launcher3.QuickstepTransitionManager.STATUS_BAR_TRANSITION_PRE_DELAY;
+import static com.android.launcher3.testing.shared.TestProtocol.LAUNCHER_ACTIVITY_STOPPED_MESSAGE;
import static com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_STATE_ORDINAL;
import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely;
import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
@@ -344,6 +346,8 @@
// Workaround for b/78520668, explicitly trim memory once UI is hidden
onTrimMemory(TRIM_MEMORY_UI_HIDDEN);
mFallbackRecentsView.updateLocusId();
+ AccessibilityManagerCompat.sendTestProtocolEventToTest(
+ this, LAUNCHER_ACTIVITY_STOPPED_MESSAGE);
}
@Override
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 0d7ca07..27de20c 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -403,10 +403,10 @@
}
@Override
- public void animateNavBarLongPress(boolean isTouchDown, long durationMs) {
+ public void animateNavBarLongPress(boolean isTouchDown, boolean shrink, long durationMs) {
if (mSystemUiProxy != null) {
try {
- mSystemUiProxy.animateNavBarLongPress(isTouchDown, durationMs);
+ mSystemUiProxy.animateNavBarLongPress(isTouchDown, shrink, durationMs);
} catch (RemoteException e) {
Log.w(TAG, "Failed call animateNavBarLongPress", e);
}
diff --git a/quickstep/src/com/android/quickstep/TaskIconCache.java b/quickstep/src/com/android/quickstep/TaskIconCache.java
index 20a751b..1b3f598 100644
--- a/quickstep/src/com/android/quickstep/TaskIconCache.java
+++ b/quickstep/src/com/android/quickstep/TaskIconCache.java
@@ -31,7 +31,6 @@
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.SparseArray;
-import android.view.accessibility.AccessibilityManager;
import androidx.annotation.WorkerThread;
@@ -45,6 +44,7 @@
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
import com.android.launcher3.util.DisplayController.Info;
+import com.android.launcher3.util.FlagOp;
import com.android.launcher3.util.Preconditions;
import com.android.quickstep.util.CancellableTask;
import com.android.quickstep.util.TaskKeyLruCache;
@@ -62,7 +62,6 @@
public class TaskIconCache implements DisplayInfoChangeListener {
private final Executor mBgExecutor;
- private final AccessibilityManager mAccessibilityManager;
private final Context mContext;
private final TaskKeyLruCache<TaskCacheEntry> mIconCache;
@@ -79,7 +78,6 @@
public TaskIconCache(Context context, Executor bgExecutor, IconProvider iconProvider) {
mContext = context;
mBgExecutor = bgExecutor;
- mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
mIconProvider = iconProvider;
Resources res = context.getResources();
@@ -238,14 +236,11 @@
if ((index = mDefaultIcons.indexOfKey(userId)) >= 0) {
return mDefaultIcons.valueAt(index).newIcon(mContext);
} else {
- try (BaseIconFactory li = getIconFactory()) {
- BitmapInfo info = mDefaultIconBase.withFlags(
- li.getBitmapFlagOp(new IconOptions()
- .setUser(UserCache.INSTANCE.get(mContext)
- .getUserInfo(UserHandle.of(userId)))));
- mDefaultIcons.put(userId, info);
- return info.newIcon(mContext);
- }
+ BitmapInfo info = mDefaultIconBase.withFlags(
+ UserCache.INSTANCE.get(mContext).getUserInfo(UserHandle.of(userId))
+ .applyBitmapInfoFlags(FlagOp.NO_OP));
+ mDefaultIcons.put(userId, info);
+ return info.newIcon(mContext);
}
}
}
diff --git a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
index e5fca4b..9e21595 100644
--- a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
+++ b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
@@ -195,11 +195,15 @@
return null;
}
- CancellableTask<ThumbnailData> request = new CancellableTask<ThumbnailData>() {
+ CancellableTask<ThumbnailData> request = new CancellableTask<>() {
@Override
public ThumbnailData getResultOnBg() {
- return ActivityManagerWrapper.getInstance().getTaskThumbnail(
+ ThumbnailData thumbnailData = ActivityManagerWrapper.getInstance().getTaskThumbnail(
key.id, lowResolution);
+ if (thumbnailData.thumbnail != null) {
+ return thumbnailData;
+ }
+ return ActivityManagerWrapper.getInstance().takeTaskThumbnail(key.id);
}
@Override
diff --git a/quickstep/src/com/android/quickstep/util/AssistStateManager.java b/quickstep/src/com/android/quickstep/util/AssistStateManager.java
index a0bb76d..f7437eb 100644
--- a/quickstep/src/com/android/quickstep/util/AssistStateManager.java
+++ b/quickstep/src/com/android/quickstep/util/AssistStateManager.java
@@ -31,6 +31,11 @@
public AssistStateManager() {}
+ /** Whether search supports haptic on invocation. */
+ public boolean supportsCommitHaptic() {
+ return false;
+ }
+
/** Whether search is available. */
public boolean isSearchAvailable() {
return false;
diff --git a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
index 1effaff..dfbd32c 100644
--- a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
+++ b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
@@ -257,10 +257,6 @@
Interpolators.clampToProgress(Interpolators.LINEAR,
timings.instructionsContainerFadeInStartOffset,
timings.instructionsContainerFadeInEndOffset))
- anim.setViewAlpha(splitInstructionsView!!.textView, 1f,
- Interpolators.clampToProgress(Interpolators.LINEAR,
- timings.instructionsTextFadeInStartOffset,
- timings.instructionsTextFadeInEndOffset))
anim.addFloat(splitInstructionsView, SplitInstructionsView.UNFOLD, 0.1f, 1f,
Interpolators.clampToProgress(Interpolators.EMPHASIZED_DECELERATE,
timings.instructionsUnfoldStartOffset,
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt b/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt
index 423ba43..c013483 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt
@@ -93,6 +93,7 @@
private var secondTaskId: Int = INVALID_TASK_ID
private var initialIntent: Intent? = null
private var secondIntent: Intent? = null
+ private var widgetSecondIntent: Intent? = null
private var initialUser: UserHandle? = null
private var secondUser: UserHandle? = null
private var initialPendingIntent: PendingIntent? = null
@@ -167,6 +168,16 @@
secondUser = pendingIntent.creatorUserHandle
}
+ /**
+ * Similar to [setSecondTask] except this is to be called for widgets which can pass through
+ * an extra intent from their RemoteResponse.
+ * See [android.widget.RemoteViews.RemoteResponse.getLaunchOptions].first
+ */
+ fun setSecondWidget(pendingIntent: PendingIntent, widgetIntent: Intent?) {
+ setSecondTask(pendingIntent)
+ widgetSecondIntent = widgetIntent
+ }
+
private fun getShortcutInfo(intent: Intent?, user: UserHandle?): ShortcutInfo? {
val intentPackage = intent?.getPackage() ?: return null
val shortcutId = intent.getStringExtra(ShortcutKey.EXTRA_SHORTCUT_ID)
@@ -241,6 +252,7 @@
secondTaskId,
initialPendingIntent,
secondPendingIntent,
+ widgetSecondIntent,
initialUser?.identifier ?: -1,
secondUser?.identifier ?: -1,
initialShortcut,
@@ -257,7 +269,8 @@
* Note that both [initialIntent] and [secondIntent] will be nullified on method return
*
* One caveat is that if [secondPendingIntent] is set, we will use that and *not* attempt to
- * convert [secondIntent]
+ * convert [secondIntent].
+ * This also leaves [widgetSecondIntent] untouched.
*/
private fun convertIntentsToFinalTypes() {
initialShortcut = getShortcutInfo(initialIntent, initialUser)
@@ -343,6 +356,7 @@
var secondTaskId: Int = INVALID_TASK_ID,
var initialPendingIntent: PendingIntent? = null,
var secondPendingIntent: PendingIntent? = null,
+ var widgetSecondIntent: Intent? = null,
var initialUserId: Int = -1,
var secondUserId: Int = -1,
var initialShortcut: ShortcutInfo? = null,
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index 145707b..8b27a85 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -32,6 +32,7 @@
import static com.android.quickstep.util.SplitSelectDataHolder.SPLIT_TASK_SHORTCUT;
import static com.android.quickstep.util.SplitSelectDataHolder.SPLIT_TASK_TASK;
import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
+import static com.android.wm.shell.common.split.SplitScreenConstants.KEY_EXTRA_WIDGET_INTENT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50;
import android.animation.Animator;
@@ -355,6 +356,10 @@
mSplitSelectDataHolder.setSecondTask(pendingIntent);
}
+ public void setSecondWidget(PendingIntent pendingIntent, Intent widgetIntent) {
+ mSplitSelectDataHolder.setSecondWidget(pendingIntent, widgetIntent);
+ }
+
/**
* To be called when we want to launch split pairs from Overview. Split can be initiated from
* either Overview or home, or all apps. Either both taskIds are set, or a pending intent + a
@@ -380,11 +385,13 @@
ShortcutInfo secondShortcut = launchData.getSecondShortcut();
PendingIntent firstPI = launchData.getInitialPendingIntent();
PendingIntent secondPI = launchData.getSecondPendingIntent();
+ Intent widgetIntent = launchData.getWidgetSecondIntent();
int firstUserId = launchData.getInitialUserId();
int secondUserId = launchData.getSecondUserId();
int initialStagePosition = launchData.getInitialStagePosition();
Bundle optionsBundle = options1.toBundle();
-
+ Bundle extrasBundle = new Bundle(1);
+ extrasBundle.putParcelable(KEY_EXTRA_WIDGET_INTENT, widgetIntent);
if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
final RemoteTransition remoteTransition = getShellRemoteTransition(firstTaskId,
secondTaskId, callback, "LaunchSplitPair");
@@ -396,7 +403,7 @@
case SPLIT_TASK_PENDINGINTENT ->
mSystemUiProxy.startIntentAndTask(secondPI, secondUserId, optionsBundle,
- firstTaskId, null /*options2*/, initialStagePosition, snapPosition,
+ firstTaskId, extrasBundle, initialStagePosition, snapPosition,
remoteTransition, shellInstanceId);
case SPLIT_TASK_SHORTCUT ->
@@ -411,9 +418,9 @@
case SPLIT_PENDINGINTENT_PENDINGINTENT ->
mSystemUiProxy.startIntents(firstPI, firstUserId, firstShortcut,
- optionsBundle, secondPI, secondUserId, secondShortcut,
- null /*options2*/, initialStagePosition, snapPosition,
- remoteTransition, shellInstanceId);
+ optionsBundle, secondPI, secondUserId, secondShortcut, extrasBundle,
+ initialStagePosition, snapPosition, remoteTransition,
+ shellInstanceId);
case SPLIT_SHORTCUT_TASK ->
mSystemUiProxy.startShortcutAndTask(firstShortcut, optionsBundle,
diff --git a/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java b/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java
index 9313342..e705285 100644
--- a/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java
@@ -72,7 +72,8 @@
* @return {@code true} if we can attempt launch the widget into split, {@code false} otherwise
* to allow launcher to handle the click
*/
- public boolean handleSecondWidgetSelectionForSplit(View view, PendingIntent pendingIntent) {
+ public boolean handleSecondWidgetSelectionForSplit(View view, PendingIntent pendingIntent,
+ Intent remoteResponseIntent) {
if (shouldIgnoreSecondSplitLaunch()) {
return false;
}
@@ -86,7 +87,7 @@
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
view.post(() -> {
- mController.setSecondTask(pendingIntent);
+ mController.setSecondWidget(pendingIntent, remoteResponseIntent);
// Convert original widgetView into bitmap to use for animation
Canvas canvas = new Canvas(bitmap);
view.draw(canvas);
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 6235490..7e1034b 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -3298,9 +3298,6 @@
anim.setViewAlpha(splitInstructionsView, 1, clampToProgress(LINEAR,
timings.getInstructionsContainerFadeInStartOffset(),
timings.getInstructionsContainerFadeInEndOffset()));
- anim.setViewAlpha(splitInstructionsView.getTextView(), 1, clampToProgress(LINEAR,
- timings.getInstructionsTextFadeInStartOffset(),
- timings.getInstructionsTextFadeInEndOffset()));
anim.addFloat(splitInstructionsView, splitInstructionsView.UNFOLD, 0.1f, 1,
clampToProgress(EMPHASIZED_DECELERATE,
timings.getInstructionsUnfoldStartOffset(),
diff --git a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
index 8bc85cf..f6501f1 100644
--- a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
+++ b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
@@ -23,9 +23,9 @@
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.LinearLayout;
+import android.widget.TextView;
import androidx.annotation.Nullable;
-import androidx.appcompat.widget.AppCompatTextView;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
@@ -41,12 +41,9 @@
*/
public class SplitInstructionsView extends LinearLayout {
private final StatefulActivity mLauncher;
- private AppCompatTextView mInstructionTextView;
- /** Only used if {@link com.android.wm.shell.FeatureFlags#enableSplitContextual()} is true. */
- private AppCompatTextView mCancelTextView;
public static final FloatProperty<SplitInstructionsView> UNFOLD =
- new FloatProperty<SplitInstructionsView>("SplitInstructionsUnfold") {
+ new FloatProperty<>("SplitInstructionsUnfold") {
@Override
public void setValue(SplitInstructionsView splitInstructionsView, float v) {
splitInstructionsView.setScaleY(v);
@@ -97,12 +94,11 @@
}
private void init() {
- mInstructionTextView = findViewById(R.id.split_instructions_text);
- mCancelTextView = findViewById(R.id.split_instructions_text_cancel);
+ TextView cancelTextView = findViewById(R.id.split_instructions_text_cancel);
if (FeatureFlags.enableSplitContextually()) {
- mCancelTextView.setVisibility(VISIBLE);
- mCancelTextView.setOnClickListener((v) -> exitSplitSelection());
+ cancelTextView.setVisibility(VISIBLE);
+ cancelTextView.setOnClickListener((v) -> exitSplitSelection());
}
}
@@ -147,8 +143,4 @@
getMeasuredWidth()
);
}
-
- public AppCompatTextView getTextView() {
- return mInstructionTextView;
- }
}
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 137ffe8..025003b 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -432,7 +432,7 @@
<dimen name="split_instructions_start_margin_cancel">8dp</dimen>
<!-- Workspace grid visualization parameters -->
- <dimen name="grid_visualization_rounding_radius">28dp</dimen>
+ <dimen name="grid_visualization_rounding_radius">16dp</dimen>
<dimen name="grid_visualization_horizontal_cell_spacing">6dp</dimen>
<dimen name="grid_visualization_vertical_cell_spacing">6dp</dimen>
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 11b263d..5443ff9 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -70,6 +70,7 @@
import com.android.launcher3.celllayout.DelegatedCellDrawing;
import com.android.launcher3.celllayout.ItemConfiguration;
import com.android.launcher3.celllayout.ReorderAlgorithm;
+import com.android.launcher3.celllayout.ReorderParameters;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.folder.PreviewBackground;
@@ -1741,140 +1742,6 @@
return swapSolution.isSolution;
}
- /**
- * Find a vacant area that will fit the given bounds nearest the requested
- * cell location, and will also weigh in a suggested direction vector of the
- * desired location. This method computers distance based on unit grid distances,
- * not pixel distances.
- *
- * @param cellX The X cell nearest to which you want to search for a vacant area.
- * @param cellY The Y cell nearest which you want to search for a vacant area.
- * @param spanX Horizontal span of the object.
- * @param spanY Vertical span of the object.
- * @param direction The favored direction in which the views should move from x, y
- * @param occupied The array which represents which cells in the CellLayout are occupied
- * @param blockOccupied The array which represents which cells in the specified block (cellX,
- * cellY, spanX, spanY) are occupied. This is used when try to move a group of views.
- * @param result Array in which to place the result, or null (in which case a new array will
- * be allocated)
- * @return The X, Y cell of a vacant area that can contain this object,
- * nearest the requested location.
- */
- public int[] findNearestArea(int cellX, int cellY, int spanX, int spanY, int[] direction,
- boolean[][] occupied, boolean blockOccupied[][], int[] result) {
- // Keep track of best-scoring drop area
- final int[] bestXY = result != null ? result : new int[2];
- float bestDistance = Float.MAX_VALUE;
- int bestDirectionScore = Integer.MIN_VALUE;
-
- final int countX = mCountX;
- final int countY = mCountY;
-
- for (int y = 0; y < countY - (spanY - 1); y++) {
- inner:
- for (int x = 0; x < countX - (spanX - 1); x++) {
- // First, let's see if this thing fits anywhere
- for (int i = 0; i < spanX; i++) {
- for (int j = 0; j < spanY; j++) {
- if (occupied[x + i][y + j] && (blockOccupied == null || blockOccupied[i][j])) {
- continue inner;
- }
- }
- }
-
- float distance = (float) Math.hypot(x - cellX, y - cellY);
- int[] curDirection = mTmpPoint;
- computeDirectionVector(x - cellX, y - cellY, curDirection);
- // The direction score is just the dot product of the two candidate direction
- // and that passed in.
- int curDirectionScore = direction[0] * curDirection[0] +
- direction[1] * curDirection[1];
- if (Float.compare(distance, bestDistance) < 0 ||
- (Float.compare(distance, bestDistance) == 0
- && curDirectionScore > bestDirectionScore)) {
- bestDistance = distance;
- bestDirectionScore = curDirectionScore;
- bestXY[0] = x;
- bestXY[1] = y;
- }
- }
- }
-
- // Return -1, -1 if no suitable location found
- if (bestDistance == Float.MAX_VALUE) {
- bestXY[0] = -1;
- bestXY[1] = -1;
- }
- return bestXY;
- }
-
- /*
- * Returns a pair (x, y), where x,y are in {-1, 0, 1} corresponding to vector between
- * the provided point and the provided cell
- */
- private void computeDirectionVector(float deltaX, float deltaY, int[] result) {
- double angle = Math.atan(deltaY / deltaX);
-
- result[0] = 0;
- result[1] = 0;
- if (Math.abs(Math.cos(angle)) > 0.5f) {
- result[0] = (int) Math.signum(deltaX);
- }
- if (Math.abs(Math.sin(angle)) > 0.5f) {
- result[1] = (int) Math.signum(deltaY);
- }
- }
-
- /* This seems like it should be obvious and straight-forward, but when the direction vector
- needs to match with the notion of the dragView pushing other views, we have to employ
- a slightly more subtle notion of the direction vector. The question is what two points is
- the vector between? The center of the dragView and its desired destination? Not quite, as
- this doesn't necessarily coincide with the interaction of the dragView and items occupying
- those cells. Instead we use some heuristics to often lock the vector to up, down, left
- or right, which helps make pushing feel right.
- */
- public void getDirectionVectorForDrop(int dragViewCenterX, int dragViewCenterY, int spanX,
- int spanY, View dragView, int[] resultDirection) {
-
- //TODO(adamcohen) b/151776141 use the items visual center for the direction vector
- int[] targetDestination = new int[2];
-
- findNearestAreaIgnoreOccupied(dragViewCenterX, dragViewCenterY, spanX, spanY,
- targetDestination);
- Rect dragRect = new Rect();
- cellToRect(targetDestination[0], targetDestination[1], spanX, spanY, dragRect);
- dragRect.offset(dragViewCenterX - dragRect.centerX(), dragViewCenterY - dragRect.centerY());
-
- Rect region = new Rect(targetDestination[0], targetDestination[1],
- targetDestination[0] + spanX, targetDestination[1] + spanY);
- Rect dropRegionRect = getIntersectingRectanglesInRegion(region, dragView);
- if (dropRegionRect == null) dropRegionRect = new Rect(region);
-
- int dropRegionSpanX = dropRegionRect.width();
- int dropRegionSpanY = dropRegionRect.height();
-
- cellToRect(dropRegionRect.left, dropRegionRect.top, dropRegionRect.width(),
- dropRegionRect.height(), dropRegionRect);
-
- int deltaX = (dropRegionRect.centerX() - dragViewCenterX) / spanX;
- int deltaY = (dropRegionRect.centerY() - dragViewCenterY) / spanY;
-
- if (dropRegionSpanX == mCountX || spanX == mCountX) {
- deltaX = 0;
- }
- if (dropRegionSpanY == mCountY || spanY == mCountY) {
- deltaY = 0;
- }
-
- if (deltaX == 0 && deltaY == 0) {
- // No idea what to do, give a random direction.
- resultDirection[0] = 1;
- resultDirection[1] = 0;
- } else {
- computeDirectionVector(deltaX, deltaY, resultDirection);
- }
- }
-
public ReorderAlgorithm createReorderAlgorithm() {
return new ReorderAlgorithm(this);
}
@@ -1882,8 +1749,11 @@
protected ItemConfiguration findReorderSolution(int pixelX, int pixelY, int minSpanX,
int minSpanY, int spanX, int spanY, int[] direction, View dragView, boolean decX,
ItemConfiguration solution) {
- return createReorderAlgorithm().findReorderSolution(pixelX, pixelY, minSpanX, minSpanY,
- spanX, spanY, direction, dragView, decX, solution);
+ ItemConfiguration configuration = new ItemConfiguration();
+ copyCurrentStateToSolution(configuration);
+ ReorderParameters parameters = new ReorderParameters(pixelX, pixelY, spanX, spanY, minSpanX,
+ minSpanY, dragView, configuration);
+ return createReorderAlgorithm().findReorderSolution(parameters, decX);
}
public void copyCurrentStateToSolution(ItemConfiguration solution) {
@@ -1913,8 +1783,12 @@
*/
public ItemConfiguration calculateReorder(int pixelX, int pixelY, int minSpanX, int minSpanY,
int spanX, int spanY, View dragView) {
- return createReorderAlgorithm().calculateReorder(pixelX, pixelY, minSpanX, minSpanY,
- spanX, spanY, dragView);
+ ItemConfiguration configuration = new ItemConfiguration();
+ copyCurrentStateToSolution(configuration);
+ return createReorderAlgorithm().calculateReorder(
+ new ReorderParameters(pixelX, pixelY, spanX, spanY, minSpanX, minSpanY, dragView,
+ configuration)
+ );
}
int[] performReorder(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX, int spanY,
diff --git a/src/com/android/launcher3/MultipageCellLayout.java b/src/com/android/launcher3/MultipageCellLayout.java
index 5c5d71c..0ec9034 100644
--- a/src/com/android/launcher3/MultipageCellLayout.java
+++ b/src/com/android/launcher3/MultipageCellLayout.java
@@ -56,17 +56,6 @@
}
@Override
- public void getDirectionVectorForDrop(int dragViewCenterX, int dragViewCenterY, int spanX,
- int spanY, View dragView, int[] resultDirection) {
- createReorderAlgorithm().simulateSeam(
- () -> {
- super.getDirectionVectorForDrop(dragViewCenterX, dragViewCenterY, spanX, spanY,
- dragView, resultDirection);
- return 0;
- });
- }
-
- @Override
public boolean isNearestDropLocationOccupied(int pixelX, int pixelY, int spanX, int spanY,
View dragView, int[] result) {
return createReorderAlgorithm().simulateSeam(
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index b74699a..e0f6101 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -79,7 +79,6 @@
import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
import com.android.launcher3.graphics.TintedDrawableSpan;
-import com.android.launcher3.icons.BaseIconFactory;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.icons.ShortcutCachingLogic;
@@ -91,6 +90,7 @@
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.shortcuts.ShortcutRequest;
import com.android.launcher3.testing.shared.ResourceUtils;
+import com.android.launcher3.util.FlagOp;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
import com.android.launcher3.util.Themes;
@@ -676,12 +676,11 @@
}
if (badge == null) {
- try (LauncherIcons li = LauncherIcons.obtain(context)) {
- badge = BitmapInfo.LOW_RES_INFO.withFlags(
- li.getBitmapFlagOp(new BaseIconFactory.IconOptions().setUser(
- UserCache.INSTANCE.get(context).getUserInfo(info.user))))
- .getBadgeDrawable(context, useTheme);
- }
+ badge = BitmapInfo.LOW_RES_INFO.withFlags(
+ UserCache.INSTANCE.get(context)
+ .getUserInfo(info.user)
+ .applyBitmapInfoFlags(FlagOp.NO_OP))
+ .getBadgeDrawable(context, useTheme);
if (badge == null) {
badge = new ColorDrawable(Color.TRANSPARENT);
}
diff --git a/src/com/android/launcher3/allapps/PrivateProfileManager.java b/src/com/android/launcher3/allapps/PrivateProfileManager.java
index d8f6689..77eb07e 100644
--- a/src/com/android/launcher3/allapps/PrivateProfileManager.java
+++ b/src/com/android/launcher3/allapps/PrivateProfileManager.java
@@ -92,9 +92,6 @@
boolean isEnabled = !mAllApps.getAppsStore()
.hasModelFlag(FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED);
int updatedState = isEnabled ? STATE_ENABLED : STATE_DISABLED;
- if (getCurrentState() == updatedState) {
- return;
- }
setCurrentState(updatedState);
resetPrivateSpaceDecorator(updatedState);
}
@@ -126,19 +123,27 @@
@VisibleForTesting
void resetPrivateSpaceDecorator(int updatedState) {
+ ActivityAllAppsContainerView<?>.AdapterHolder mainAdapterHolder = mAllApps.mAH.get(MAIN);
if (updatedState == STATE_ENABLED) {
- // Add Private Space Decorator to the Recycler view.
+ // Create a new decorator instance if not already available.
if (mPrivateAppsSectionDecorator == null) {
mPrivateAppsSectionDecorator = new PrivateAppsSectionDecorator(
mAllApps.mActivityContext,
- mAllApps.mAH.get(MAIN).mAppsList);
+ mainAdapterHolder.mAppsList);
}
- mAllApps.mAH.get(MAIN).mRecyclerView.addItemDecoration(mPrivateAppsSectionDecorator);
+ for (int i = 0; i < mainAdapterHolder.mRecyclerView.getItemDecorationCount(); i++) {
+ if (mainAdapterHolder.mRecyclerView.getItemDecorationAt(i)
+ .equals(mPrivateAppsSectionDecorator)) {
+ // No need to add another decorator if one is already present in recycler view.
+ return;
+ }
+ }
+ // Add Private Space Decorator to the Recycler view.
+ mainAdapterHolder.mRecyclerView.addItemDecoration(mPrivateAppsSectionDecorator);
} else {
// Remove Private Space Decorator from the Recycler view.
if (mPrivateAppsSectionDecorator != null) {
- mAllApps.mAH.get(MAIN).mRecyclerView
- .removeItemDecoration(mPrivateAppsSectionDecorator);
+ mainAdapterHolder.mRecyclerView.removeItemDecoration(mPrivateAppsSectionDecorator);
}
}
}
diff --git a/src/com/android/launcher3/celllayout/MulticellReorderAlgorithm.java b/src/com/android/launcher3/celllayout/MulticellReorderAlgorithm.java
index 7deb653..8d0cf13 100644
--- a/src/com/android/launcher3/celllayout/MulticellReorderAlgorithm.java
+++ b/src/com/android/launcher3/celllayout/MulticellReorderAlgorithm.java
@@ -48,28 +48,24 @@
}
@Override
- public ItemConfiguration closestEmptySpaceReorder(int pixelX, int pixelY, int minSpanX,
- int minSpanY, int spanX, int spanY) {
+ public ItemConfiguration closestEmptySpaceReorder(ReorderParameters reorderParameters) {
return removeSeamFromSolution(simulateSeam(
- () -> super.closestEmptySpaceReorder(pixelX, pixelY, minSpanX, minSpanY, spanX,
- spanY)));
+ () -> super.closestEmptySpaceReorder(reorderParameters))
+ );
}
@Override
- public ItemConfiguration findReorderSolution(int pixelX, int pixelY, int minSpanX,
- int minSpanY, int spanX, int spanY, int[] direction, View dragView, boolean decX,
- ItemConfiguration solution) {
+ public ItemConfiguration findReorderSolution(ReorderParameters reorderParameters,
+ boolean decX) {
return removeSeamFromSolution(simulateSeam(
- () -> super.findReorderSolution(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY,
- direction, dragView, decX, solution)));
+ () -> super.findReorderSolution(reorderParameters, decX)));
}
@Override
- public ItemConfiguration dropInPlaceSolution(int pixelX, int pixelY, int spanX,
- int spanY,
- View dragView) {
- return removeSeamFromSolution(simulateSeam(
- () -> super.dropInPlaceSolution(pixelX, pixelY, spanX, spanY, dragView)));
+ public ItemConfiguration dropInPlaceSolution(ReorderParameters reorderParameters) {
+ return removeSeamFromSolution(
+ simulateSeam(() -> super.dropInPlaceSolution(reorderParameters))
+ );
}
void addSeam() {
diff --git a/src/com/android/launcher3/celllayout/ReorderAlgorithm.java b/src/com/android/launcher3/celllayout/ReorderAlgorithm.java
index 6682f32..42b6991 100644
--- a/src/com/android/launcher3/celllayout/ReorderAlgorithm.java
+++ b/src/com/android/launcher3/celllayout/ReorderAlgorithm.java
@@ -45,36 +45,28 @@
* This method differs from closestEmptySpaceReorder and dropInPlaceSolution because this method
* will move items around and will change the shape of the item if possible to try to find a
* solution.
- *
+ * <p>
* When changing the size of the widget this method will try first subtracting -1 in the x
* dimension and then subtracting -1 in the y dimension until finding a possible solution or
* until it no longer can reduce the span.
*
- * @param pixelX X coordinate in pixels in the screen
- * @param pixelY Y coordinate in pixels in the screen
- * @param minSpanX minimum possible horizontal span it will try to find a solution for.
- * @param minSpanY minimum possible vertical span it will try to find a solution for.
- * @param spanX horizontal cell span
- * @param spanY vertical cell span
- * @param direction direction in which it will try to push the items intersecting the desired
- * view
- * @param dragView view being dragged in reorder
- * @param decX whether it will decrease the horizontal or vertical span if it can't find a
- * solution for the current span.
- * @param solution variable to store the solution
+ * @param decX whether it will decrease the horizontal or vertical span if it can't find a
+ * solution for the current span.
* @return the same solution variable
*/
- public ItemConfiguration findReorderSolution(int pixelX, int pixelY, int minSpanX,
- int minSpanY, int spanX, int spanY, int[] direction, View dragView, boolean decX,
- ItemConfiguration solution) {
- return findReorderSolutionRecursive(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY,
- direction, dragView, decX, solution);
+ public ItemConfiguration findReorderSolution(ReorderParameters reorderParameters,
+ boolean decX) {
+ return findReorderSolutionRecursive(reorderParameters.getPixelX(),
+ reorderParameters.getPixelY(), reorderParameters.getMinSpanX(),
+ reorderParameters.getMinSpanY(), reorderParameters.getSpanX(),
+ reorderParameters.getSpanY(), mCellLayout.mDirectionVector,
+ reorderParameters.getDragView(), decX, reorderParameters.getSolution());
}
- private ItemConfiguration findReorderSolutionRecursive(int pixelX, int pixelY,
- int minSpanX, int minSpanY, int spanX, int spanY, int[] direction, View dragView,
- boolean decX, ItemConfiguration solution) {
+ private ItemConfiguration findReorderSolutionRecursive(int pixelX, int pixelY, int minSpanX,
+ int minSpanY, int spanX, int spanY, int[] direction, View dragView, boolean decX,
+ ItemConfiguration solution) {
// Copy the current state into the solution. This solution will be manipulated as necessary.
mCellLayout.copyCurrentStateToSolution(solution);
// Copy the current occupied array into the temporary occupied array. This array will be
@@ -89,8 +81,8 @@
boolean success;
// First we try the exact nearest position of the item being dragged,
// we will then want to try to move this around to other neighbouring positions
- success = rearrangementExists(result[0], result[1], spanX, spanY, direction,
- dragView, solution);
+ success = rearrangementExists(result[0], result[1], spanX, spanY, direction, dragView,
+ solution);
if (!success) {
// We try shrinking the widget down to size in an alternating pattern, shrink 1 in
@@ -135,10 +127,11 @@
// and not by the views hash which is "random".
// The views are sorted twice, once for the X position and a second time for the Y position
// to ensure same order everytime.
- Comparator comparator = Comparator.comparing(view ->
- ((CellLayoutLayoutParams) ((View) view).getLayoutParams()).getCellX())
- .thenComparing(view ->
- ((CellLayoutLayoutParams) ((View) view).getLayoutParams()).getCellY());
+ Comparator comparator = Comparator.comparing(
+ view -> ((CellLayoutLayoutParams) ((View) view).getLayoutParams()).getCellX()
+ ).thenComparing(
+ view -> ((CellLayoutLayoutParams) ((View) view).getLayoutParams()).getCellY()
+ );
List<View> views = solution.map.keySet().stream().sorted(comparator).toList();
for (View child : views) {
if (child == ignoreView) continue;
@@ -158,15 +151,13 @@
// First we try to find a solution which respects the push mechanic. That is,
// we try to find a solution such that no displaced item travels through another item
// without also displacing that item.
- if (attemptPushInDirection(intersectingViews, occupiedRect, direction,
- ignoreView,
+ if (attemptPushInDirection(intersectingViews, occupiedRect, direction, ignoreView,
solution)) {
return true;
}
// Next we try moving the views as a block, but without requiring the push mechanic.
- if (addViewsToTempLocation(intersectingViews, occupiedRect, direction,
- ignoreView,
+ if (addViewsToTempLocation(intersectingViews, occupiedRect, direction, ignoreView,
solution)) {
return true;
}
@@ -180,15 +171,15 @@
return true;
}
- private boolean addViewToTempLocation(View v, Rect rectOccupiedByPotentialDrop,
- int[] direction, ItemConfiguration currentState) {
+ private boolean addViewToTempLocation(View v, Rect rectOccupiedByPotentialDrop, int[] direction,
+ ItemConfiguration currentState) {
CellAndSpan c = currentState.map.get(v);
boolean success = false;
mCellLayout.mTmpOccupied.markCells(c, false);
mCellLayout.mTmpOccupied.markCells(rectOccupiedByPotentialDrop, true);
- int[] tmpLocation = mCellLayout.findNearestArea(c.cellX, c.cellY, c.spanX, c.spanY,
- direction, mCellLayout.mTmpOccupied.cells, null, new int[2]);
+ int[] tmpLocation = findNearestArea(c.cellX, c.cellY, c.spanX, c.spanY, direction,
+ mCellLayout.mTmpOccupied.cells, null, new int[2]);
if (tmpLocation[0] >= 0 && tmpLocation[1] >= 0) {
c.cellX = tmpLocation[0];
@@ -305,16 +296,16 @@
int temp = direction[1];
direction[1] = 0;
- if (pushViewsToTempLocation(intersectingViews, occupied, direction,
- ignoreView, solution)) {
+ if (pushViewsToTempLocation(intersectingViews, occupied, direction, ignoreView,
+ solution)) {
return true;
}
direction[1] = temp;
temp = direction[0];
direction[0] = 0;
- if (pushViewsToTempLocation(intersectingViews, occupied, direction,
- ignoreView, solution)) {
+ if (pushViewsToTempLocation(intersectingViews, occupied, direction, ignoreView,
+ solution)) {
return true;
}
// Revert the direction
@@ -325,16 +316,16 @@
direction[1] *= -1;
temp = direction[1];
direction[1] = 0;
- if (pushViewsToTempLocation(intersectingViews, occupied, direction,
- ignoreView, solution)) {
+ if (pushViewsToTempLocation(intersectingViews, occupied, direction, ignoreView,
+ solution)) {
return true;
}
direction[1] = temp;
temp = direction[0];
direction[0] = 0;
- if (pushViewsToTempLocation(intersectingViews, occupied, direction,
- ignoreView, solution)) {
+ if (pushViewsToTempLocation(intersectingViews, occupied, direction, ignoreView,
+ solution)) {
return true;
}
// revert the direction
@@ -345,15 +336,15 @@
} else {
// If the direction vector has a single non-zero component, we push first in the
// direction of the vector
- if (pushViewsToTempLocation(intersectingViews, occupied, direction,
- ignoreView, solution)) {
+ if (pushViewsToTempLocation(intersectingViews, occupied, direction, ignoreView,
+ solution)) {
return true;
}
// Then we try the opposite direction
direction[0] *= -1;
direction[1] *= -1;
- if (pushViewsToTempLocation(intersectingViews, occupied, direction,
- ignoreView, solution)) {
+ if (pushViewsToTempLocation(intersectingViews, occupied, direction, ignoreView,
+ solution)) {
return true;
}
// Switch the direction back
@@ -367,16 +358,16 @@
int temp = direction[1];
direction[1] = direction[0];
direction[0] = temp;
- if (pushViewsToTempLocation(intersectingViews, occupied, direction,
- ignoreView, solution)) {
+ if (pushViewsToTempLocation(intersectingViews, occupied, direction, ignoreView,
+ solution)) {
return true;
}
// Then we try the opposite direction
direction[0] *= -1;
direction[1] *= -1;
- if (pushViewsToTempLocation(intersectingViews, occupied, direction,
- ignoreView, solution)) {
+ if (pushViewsToTempLocation(intersectingViews, occupied, direction, ignoreView,
+ solution)) {
return true;
}
// Switch the direction back
@@ -419,7 +410,7 @@
mCellLayout.mTmpOccupied.markCells(rectOccupiedByPotentialDrop, true);
- int[] tmpLocation = mCellLayout.findNearestArea(boundingRect.left, boundingRect.top,
+ int[] tmpLocation = findNearestArea(boundingRect.left, boundingRect.top,
boundingRect.width(), boundingRect.height(), direction,
mCellLayout.mTmpOccupied.cells, blockOccupied.cells, new int[2]);
@@ -446,63 +437,59 @@
/**
* Returns a "reorder" if there is empty space without rearranging anything.
*
- * @param pixelX X coordinate in pixels in the screen
- * @param pixelY Y coordinate in pixels in the screen
- * @param spanX horizontal cell span
- * @param spanY vertical cell span
- * @param dragView view being dragged in reorder
* @return the configuration that represents the found reorder
*/
- public ItemConfiguration dropInPlaceSolution(int pixelX, int pixelY, int spanX,
- int spanY, View dragView) {
- int[] result = mCellLayout.findNearestAreaIgnoreOccupied(pixelX, pixelY, spanX, spanY,
- new int[2]);
+ public ItemConfiguration dropInPlaceSolution(ReorderParameters reorderParameters) {
+ int[] result = mCellLayout.findNearestAreaIgnoreOccupied(reorderParameters.getPixelX(),
+ reorderParameters.getPixelY(), reorderParameters.getSpanX(),
+ reorderParameters.getSpanY(), new int[2]);
ItemConfiguration solution = new ItemConfiguration();
mCellLayout.copyCurrentStateToSolution(solution);
solution.isSolution = !isConfigurationRegionOccupied(
- new Rect(result[0], result[1], result[0] + spanX, result[1] + spanY),
- solution,
- dragView
- );
+ new Rect(result[0], result[1], result[0] + reorderParameters.getSpanX(),
+ result[1] + reorderParameters.getSpanY()), solution,
+ reorderParameters.getDragView());
if (!solution.isSolution) {
return solution;
}
solution.cellX = result[0];
solution.cellY = result[1];
- solution.spanX = spanX;
- solution.spanY = spanY;
+ solution.spanX = reorderParameters.getSpanX();
+ solution.spanY = reorderParameters.getSpanY();
return solution;
}
- private boolean isConfigurationRegionOccupied(Rect region,
- ItemConfiguration configuration, View ignoreView) {
- return configuration.map.entrySet()
+ private boolean isConfigurationRegionOccupied(Rect region, ItemConfiguration configuration,
+ View ignoreView) {
+ return configuration.map
+ .entrySet()
.stream()
.filter(entry -> entry.getKey() != ignoreView)
.map(Entry::getValue)
- .anyMatch(cellAndSpan -> region.intersect(cellAndSpan.cellX, cellAndSpan.cellY,
+ .anyMatch(cellAndSpan -> region.intersect(
+ cellAndSpan.cellX,
+ cellAndSpan.cellY,
cellAndSpan.cellX + cellAndSpan.spanX,
- cellAndSpan.cellY + cellAndSpan.spanY));
+ cellAndSpan.cellY + cellAndSpan.spanY
+ )
+ );
}
/**
* Returns a "reorder" where we simply drop the item in the closest empty space, without moving
* any other item in the way.
*
- * @param pixelX X coordinate in pixels in the screen
- * @param pixelY Y coordinate in pixels in the screen
- * @param spanX horizontal cell span
- * @param spanY vertical cell span
* @return the configuration that represents the found reorder
*/
- public ItemConfiguration closestEmptySpaceReorder(int pixelX, int pixelY,
- int minSpanX, int minSpanY, int spanX, int spanY) {
+ public ItemConfiguration closestEmptySpaceReorder(ReorderParameters reorderParameters) {
ItemConfiguration solution = new ItemConfiguration();
int[] result = new int[2];
int[] resultSpan = new int[2];
- mCellLayout.findNearestVacantArea(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY, result,
- resultSpan);
+ mCellLayout.findNearestVacantArea(reorderParameters.getPixelX(),
+ reorderParameters.getPixelY(), reorderParameters.getMinSpanX(),
+ reorderParameters.getMinSpanY(), reorderParameters.getSpanX(),
+ reorderParameters.getSpanY(), result, resultSpan);
if (result[0] >= 0 && result[1] >= 0) {
mCellLayout.copyCurrentStateToSolution(solution);
solution.cellX = result[0];
@@ -521,32 +508,19 @@
* the workspace to make space for the new item, this function return a solution for that
* reorder.
*
- * @param pixelX X coordinate in the screen of the dragView in pixels
- * @param pixelY Y coordinate in the screen of the dragView in pixels
- * @param minSpanX minimum horizontal span the item can be shrunk to
- * @param minSpanY minimum vertical span the item can be shrunk to
- * @param spanX occupied horizontal span
- * @param spanY occupied vertical span
- * @param dragView the view of the item being draged
* @return returns a solution for the given parameters, the solution contains all the icons and
* the locations they should be in the given solution.
*/
- public ItemConfiguration calculateReorder(int pixelX, int pixelY, int minSpanX,
- int minSpanY, int spanX, int spanY, View dragView) {
- mCellLayout.getDirectionVectorForDrop(pixelX, pixelY, spanX, spanY, dragView,
- mCellLayout.mDirectionVector);
+ public ItemConfiguration calculateReorder(ReorderParameters reorderParameters) {
+ getDirectionVectorForDrop(reorderParameters, mCellLayout.mDirectionVector);
- ItemConfiguration dropInPlaceSolution = dropInPlaceSolution(pixelX, pixelY, spanX, spanY,
- dragView);
+ ItemConfiguration dropInPlaceSolution = dropInPlaceSolution(reorderParameters);
// Find a solution involving pushing / displacing any items in the way
- ItemConfiguration swapSolution = findReorderSolution(pixelX, pixelY, minSpanX,
- minSpanY, spanX, spanY, mCellLayout.mDirectionVector, dragView, true,
- new ItemConfiguration());
+ ItemConfiguration swapSolution = findReorderSolution(reorderParameters, true);
// We attempt the approach which doesn't shuffle views at all
- ItemConfiguration closestSpaceSolution = closestEmptySpaceReorder(pixelX, pixelY, minSpanX,
- minSpanY, spanX, spanY);
+ ItemConfiguration closestSpaceSolution = closestEmptySpaceReorder(reorderParameters);
// If the reorder solution requires resizing (shrinking) the item being dropped, we instead
// favor a solution in which the item is not resized, but
@@ -559,4 +533,150 @@
}
return null;
}
+
+ /*
+ * Returns a pair (x, y), where x,y are in {-1, 0, 1} corresponding to vector between
+ * the provided point and the provided cell
+ */
+ private void computeDirectionVector(float deltaX, float deltaY, int[] result) {
+ double angle = Math.atan(deltaY / deltaX);
+
+ result[0] = 0;
+ result[1] = 0;
+ if (Math.abs(Math.cos(angle)) > 0.5f) {
+ result[0] = (int) Math.signum(deltaX);
+ }
+ if (Math.abs(Math.sin(angle)) > 0.5f) {
+ result[1] = (int) Math.signum(deltaY);
+ }
+ }
+
+ /**
+ * This seems like it should be obvious and straight-forward, but when the direction vector
+ * needs to match with the notion of the dragView pushing other views, we have to employ
+ * a slightly more subtle notion of the direction vector. The question is what two points is
+ * the vector between? The center of the dragView and its desired destination? Not quite, as
+ * this doesn't necessarily coincide with the interaction of the dragView and items occupying
+ * those cells. Instead we use some heuristics to often lock the vector to up, down, left
+ * or right, which helps make pushing feel right.
+ */
+ public void getDirectionVectorForDrop(ReorderParameters reorderParameters,
+ int[] resultDirection) {
+
+ //TODO(adamcohen) b/151776141 use the items visual center for the direction vector
+ int[] targetDestination = new int[2];
+
+ mCellLayout.findNearestAreaIgnoreOccupied(reorderParameters.getPixelX(),
+ reorderParameters.getPixelY(), reorderParameters.getSpanX(),
+ reorderParameters.getSpanY(), targetDestination);
+ Rect dragRect = new Rect();
+ mCellLayout.cellToRect(targetDestination[0], targetDestination[1],
+ reorderParameters.getSpanX(), reorderParameters.getSpanY(), dragRect);
+ dragRect.offset(reorderParameters.getPixelX() - dragRect.centerX(),
+ reorderParameters.getPixelY() - dragRect.centerY());
+
+ Rect region = new Rect(targetDestination[0], targetDestination[1],
+ targetDestination[0] + reorderParameters.getSpanX(),
+ targetDestination[1] + reorderParameters.getSpanY());
+ Rect dropRegionRect = mCellLayout.getIntersectingRectanglesInRegion(region,
+ reorderParameters.getDragView());
+ if (dropRegionRect == null) dropRegionRect = new Rect(region);
+
+ int dropRegionSpanX = dropRegionRect.width();
+ int dropRegionSpanY = dropRegionRect.height();
+
+ mCellLayout.cellToRect(dropRegionRect.left, dropRegionRect.top, dropRegionRect.width(),
+ dropRegionRect.height(), dropRegionRect);
+
+ int deltaX = (dropRegionRect.centerX() - reorderParameters.getPixelX())
+ / reorderParameters.getSpanX();
+ int deltaY = (dropRegionRect.centerY() - reorderParameters.getPixelY())
+ / reorderParameters.getSpanY();
+
+ if (dropRegionSpanX == mCellLayout.getCountX()
+ || reorderParameters.getSpanX() == mCellLayout.getCountX()) {
+ deltaX = 0;
+ }
+ if (dropRegionSpanY == mCellLayout.getCountY()
+ || reorderParameters.getSpanY() == mCellLayout.getCountY()) {
+ deltaY = 0;
+ }
+
+ if (deltaX == 0 && deltaY == 0) {
+ // No idea what to do, give a random direction.
+ resultDirection[0] = 1;
+ resultDirection[1] = 0;
+ } else {
+ computeDirectionVector(deltaX, deltaY, resultDirection);
+ }
+ }
+
+ /**
+ * Find a vacant area that will fit the given bounds nearest the requested
+ * cell location, and will also weigh in a suggested direction vector of the
+ * desired location. This method computers distance based on unit grid distances,
+ * not pixel distances.
+ *
+ * @param cellX The X cell nearest to which you want to search for a vacant area.
+ * @param cellY The Y cell nearest which you want to search for a vacant area.
+ * @param spanX Horizontal span of the object.
+ * @param spanY Vertical span of the object.
+ * @param direction The favored direction in which the views should move from x, y
+ * @param occupied The array which represents which cells in the CellLayout are occupied
+ * @param blockOccupied The array which represents which cells in the specified block (cellX,
+ * cellY, spanX, spanY) are occupied. This is used when try to move a group
+ * of views.
+ * @param result Array in which to place the result, or null (in which case a new array
+ * will
+ * be allocated)
+ * @return The X, Y cell of a vacant area that can contain this object,
+ * nearest the requested location.
+ */
+ public int[] findNearestArea(int cellX, int cellY, int spanX, int spanY, int[] direction,
+ boolean[][] occupied, boolean[][] blockOccupied, int[] result) {
+ // Keep track of best-scoring drop area
+ final int[] bestXY = result != null ? result : new int[2];
+ float bestDistance = Float.MAX_VALUE;
+ int bestDirectionScore = Integer.MIN_VALUE;
+
+ final int countX = mCellLayout.getCountX();
+ final int countY = mCellLayout.getCountY();
+
+ for (int y = 0; y < countY - (spanY - 1); y++) {
+ inner:
+ for (int x = 0; x < countX - (spanX - 1); x++) {
+ // First, let's see if this thing fits anywhere
+ for (int i = 0; i < spanX; i++) {
+ for (int j = 0; j < spanY; j++) {
+ if (occupied[x + i][y + j] && (blockOccupied == null
+ || blockOccupied[i][j])) {
+ continue inner;
+ }
+ }
+ }
+
+ float distance = (float) Math.hypot(x - cellX, y - cellY);
+ int[] curDirection = new int[2];
+ computeDirectionVector(x - cellX, y - cellY, curDirection);
+ // The direction score is just the dot product of the two candidate direction
+ // and that passed in.
+ int curDirectionScore =
+ direction[0] * curDirection[0] + direction[1] * curDirection[1];
+ if (Float.compare(distance, bestDistance) < 0 || (Float.compare(distance,
+ bestDistance) == 0 && curDirectionScore > bestDirectionScore)) {
+ bestDistance = distance;
+ bestDirectionScore = curDirectionScore;
+ bestXY[0] = x;
+ bestXY[1] = y;
+ }
+ }
+ }
+
+ // Return -1, -1 if no suitable location found
+ if (bestDistance == Float.MAX_VALUE) {
+ bestXY[0] = -1;
+ bestXY[1] = -1;
+ }
+ return bestXY;
+ }
}
diff --git a/src/com/android/launcher3/celllayout/ReorderParameters.kt b/src/com/android/launcher3/celllayout/ReorderParameters.kt
new file mode 100644
index 0000000..3fdf35c
--- /dev/null
+++ b/src/com/android/launcher3/celllayout/ReorderParameters.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2023 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.celllayout
+
+import android.view.View
+
+class ReorderParameters(
+ val pixelX: Int,
+ val pixelY: Int,
+ val spanX: Int,
+ val spanY: Int,
+ val minSpanX: Int,
+ val minSpanY: Int,
+ val dragView: View?,
+ val solution: ItemConfiguration
+) {}
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 15cc6e3..bed6efb 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -124,6 +124,10 @@
getReleaseFlag(308693847, "ANIMATE_LPNH", TEAMFOOD,
"Animates navbar when long pressing");
+ public static final BooleanFlag SHRINK_NAV_HANDLE_ON_PRESS =
+ getReleaseFlag(314158312, "SHRINK_NAV_HANDLE_ON_PRESS", DISABLED,
+ "Shrinks navbar when long pressing if ANIMATE_LPNH is enabled");
+
public static final IntFlag LPNH_SLOP_PERCENTAGE =
getIntFlag(301680992, "LPNH_SLOP_PERCENTAGE", 100,
"Controls touch slop percentage for lpnh");
@@ -287,7 +291,7 @@
"Enables haptic hint while long pressing on the bottom bar nav handle.");
public static final BooleanFlag ENABLE_SEARCH_HAPTIC_COMMIT =
- getReleaseFlag(314005577, "ENABLE_SEARCH_HAPTIC_COMMIT", DISABLED,
+ getReleaseFlag(314005577, "ENABLE_SEARCH_HAPTIC_COMMIT", ENABLED,
"Enables haptic hint at end of long pressing on the bottom bar nav handle.");
public static final IntFlag LPNH_HAPTIC_HINT_START_SCALE_PERCENT =
diff --git a/src/com/android/launcher3/keyboard/ItemFocusIndicatorHelper.java b/src/com/android/launcher3/keyboard/ItemFocusIndicatorHelper.java
index 57fab2d..2dc8d81 100644
--- a/src/com/android/launcher3/keyboard/ItemFocusIndicatorHelper.java
+++ b/src/com/android/launcher3/keyboard/ItemFocusIndicatorHelper.java
@@ -29,8 +29,7 @@
import android.util.FloatProperty;
import android.view.View;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.util.Themes;
+import com.android.launcher3.R;
/**
* A helper class to draw background of a focused item.
@@ -103,9 +102,8 @@
setAlpha(0);
mShift = 0;
- if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
- mRadius = Themes.getDialogCornerRadius(container.getContext());
- }
+ mRadius = container.getResources().getDimensionPixelSize(
+ R.dimen.grid_visualization_rounding_radius);
}
protected void setAlpha(float alpha) {
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index 0438e57..1b1d347 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -104,6 +104,15 @@
return getUIProperty(Bundle::putBoolean, t -> isLauncherInitialized(), () -> true);
}
+ case TestProtocol.REQUEST_IS_LAUNCHER_LAUNCHER_ACTIVITY_STARTED: {
+ final Bundle bundle = getLauncherUIProperty(Bundle::putBoolean, l -> l.isStarted());
+ if (bundle != null) return bundle;
+
+ // If Launcher activity wasn't created, it's not started.
+ response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD, false);
+ return response;
+ }
+
case TestProtocol.REQUEST_FREEZE_APP_LIST:
return getLauncherUIProperty(Bundle::putBoolean, l -> {
l.getAppsView().getAppsStore().enableDeferUpdates(DEFER_UPDATES_TEST);
diff --git a/src/com/android/launcher3/util/VibratorWrapper.java b/src/com/android/launcher3/util/VibratorWrapper.java
index e32fec2..f283fb6 100644
--- a/src/com/android/launcher3/util/VibratorWrapper.java
+++ b/src/com/android/launcher3/util/VibratorWrapper.java
@@ -240,7 +240,7 @@
/** Indicates that search has been invoked. */
public void vibrateForSearch() {
- if (mSearchEffect != null && FeatureFlags.ENABLE_SEARCH_HAPTIC_COMMIT.get()) {
+ if (mSearchEffect != null) {
vibrate(mSearchEffect);
}
}
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index 5ec1022..9855b20 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -105,6 +105,7 @@
mNavBarScrimPaint.setColor(navBarScrimColor);
invalidate();
}
+ setupNavBarColor();
}
@Override
@@ -218,10 +219,18 @@
}
protected void setupNavBarColor() {
- boolean isSheetDark = Themes.getAttrBoolean(getContext(), R.attr.isMainColorDark);
- getSystemUiController().updateUiState(
- SystemUiController.UI_STATE_WIDGET_BOTTOM_SHEET,
- isSheetDark ? SystemUiController.FLAG_DARK_NAV : SystemUiController.FLAG_LIGHT_NAV);
+ boolean isNavBarDark = Themes.getAttrBoolean(getContext(), R.attr.isMainColorDark);
+
+ // In light mode, landscape reverses navbar background color.
+ boolean isPhoneLandscape =
+ !mActivityContext.getDeviceProfile().isTablet && mInsets.bottom == 0;
+ if (!isNavBarDark && isPhoneLandscape) {
+ isNavBarDark = true;
+ }
+
+ getSystemUiController().updateUiState(SystemUiController.UI_STATE_WIDGET_BOTTOM_SHEET,
+ isNavBarDark ? SystemUiController.FLAG_DARK_NAV
+ : SystemUiController.FLAG_LIGHT_NAV);
}
protected SystemUiController getSystemUiController() {
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 953ecda..da90f17 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -24,7 +24,6 @@
import android.animation.Animator;
import android.content.Context;
-import android.content.pm.LauncherApps;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Rect;
@@ -95,7 +94,7 @@
private static final long FADE_IN_DURATION = 150;
private static final long EDUCATION_TIP_DELAY_MS = 200;
private static final long EDUCATION_DIALOG_DELAY_MS = 500;
- private static final float VERTICAL_START_POSITION = 0.3f;
+
// The widget recommendation table can easily take over the entire screen on devices with small
// resolution or landscape on phone. This ratio defines the max percentage of content area that
// the table can display.
@@ -106,9 +105,7 @@
private final UserHandle mCurrentUser = Process.myUserHandle();
private final Predicate<WidgetsListBaseEntry> mPrimaryWidgetsFilter =
entry -> mCurrentUser.equals(entry.mPkgItem.user);
- private final Predicate<WidgetsListBaseEntry> mWorkWidgetsFilter =
- entry -> !mCurrentUser.equals(entry.mPkgItem.user)
- && !mUserManagerState.isUserQuiet(entry.mPkgItem.user);
+ private final Predicate<WidgetsListBaseEntry> mWorkWidgetsFilter;
protected final boolean mHasWorkProfile;
protected boolean mHasRecommendedWidgets;
protected final SparseArray<AdapterHolder> mAdapters = new SparseArray();
@@ -182,20 +179,23 @@
public WidgetsFullSheet(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mDeviceProfile = mActivityContext.getDeviceProfile();
- mHasWorkProfile = context.getSystemService(LauncherApps.class).getProfiles().size() > 1;
mOrientation = context.getResources().getConfiguration().orientation;
+ mUserCache = UserCache.INSTANCE.get(context);
+ mHasWorkProfile = mUserCache.getUserProfiles()
+ .stream()
+ .anyMatch(user -> mUserCache.getUserInfo(user).isWork());
+ mWorkWidgetsFilter = entry -> mHasWorkProfile
+ && mUserCache.getUserInfo(entry.mPkgItem.user).isWork();
mAdapters.put(AdapterHolder.PRIMARY, new AdapterHolder(AdapterHolder.PRIMARY));
mAdapters.put(AdapterHolder.WORK, new AdapterHolder(AdapterHolder.WORK));
mAdapters.put(AdapterHolder.SEARCH, new AdapterHolder(AdapterHolder.SEARCH));
Resources resources = getResources();
+ mUserManagerState.init(UserCache.INSTANCE.get(context),
+ context.getSystemService(UserManager.class));
mTabsHeight = mHasWorkProfile
? resources.getDimensionPixelSize(R.dimen.all_apps_header_pill_height)
: 0;
-
- mUserCache = UserCache.INSTANCE.get(context);
- mUserManagerState.init(UserCache.INSTANCE.get(context),
- context.getSystemService(UserManager.class));
}
public WidgetsFullSheet(Context context, AttributeSet attrs) {
@@ -622,7 +622,6 @@
if (animate) {
if (getPopupContainer().getInsets().bottom > 0) {
mContent.setAlpha(0);
- setTranslationShift(VERTICAL_START_POSITION);
}
setUpOpenAnimation(mActivityContext.getDeviceProfile().bottomSheetOpenDuration);
Animator animator = mOpenCloseAnimation.getAnimationPlayer();
diff --git a/tests/assets/ReorderWidgets/full_reorder_case b/tests/assets/ReorderWidgets/full_reorder_case
index 33ebaae..850e4fd 100644
--- a/tests/assets/ReorderWidgets/full_reorder_case
+++ b/tests/assets/ReorderWidgets/full_reorder_case
@@ -20,7 +20,7 @@
bbmm
iimm
iiaa
-arguments: 0 3
+arguments: 0 2
board: 4x4
xxxx
bbii
diff --git a/tests/assets/ReorderWidgets/simple_reorder_case b/tests/assets/ReorderWidgets/simple_reorder_case
index f5eb7b6..2c50ce4 100644
--- a/tests/assets/ReorderWidgets/simple_reorder_case
+++ b/tests/assets/ReorderWidgets/simple_reorder_case
@@ -34,7 +34,7 @@
--mm
--mm
----
-arguments: 3 3
+arguments: 2 2
board: 4x4
xxxx
----
diff --git a/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java b/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java
index b940ba1..7621acd 100644
--- a/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java
+++ b/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java
@@ -30,6 +30,8 @@
public static final String FOLDER_OPENED_MESSAGE = "TAPL_FOLDER_OPENED";
public static final String SEARCH_RESULT_COMPLETE = "SEARCH_RESULT_COMPLETE";
public static final String LAUNCHER_ACTIVITY_STOPPED_MESSAGE = "TAPL_LAUNCHER_ACTIVITY_STOPPED";
+ public static final String WALLPAPER_OPEN_ANIMATION_FINISHED_MESSAGE =
+ "TAPL_WALLPAPER_OPEN_ANIMATION_FINISHED";
public static final int NORMAL_STATE_ORDINAL = 0;
public static final int SPRING_LOADED_STATE_ORDINAL = 1;
public static final int OVERVIEW_STATE_ORDINAL = 2;
@@ -86,6 +88,8 @@
public static final String REQUEST_ICON_HEIGHT =
"icon-height";
public static final String REQUEST_IS_LAUNCHER_INITIALIZED = "is-launcher-initialized";
+ public static final String REQUEST_IS_LAUNCHER_LAUNCHER_ACTIVITY_STARTED =
+ "is-launcher-activity-started";
public static final String REQUEST_FREEZE_APP_LIST = "freeze-app-list";
public static final String REQUEST_UNFREEZE_APP_LIST = "unfreeze-app-list";
public static final String REQUEST_ENABLE_MANUAL_TASKBAR_STASHING = "enable-taskbar-stashing";
diff --git a/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java b/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java
index 28471f6..e1af774 100644
--- a/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java
+++ b/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java
@@ -191,9 +191,21 @@
int[] testCaseXYinPixels = new int[2];
cl.regionToCenterPoint(x, y, spanX, spanY, testCaseXYinPixels);
- ItemConfiguration solution = cl.createReorderAlgorithm().calculateReorder(
- testCaseXYinPixels[0], testCaseXYinPixels[1], minSpanX, minSpanY, spanX, spanY,
- null);
+ ItemConfiguration configuration = new ItemConfiguration();
+ cl.copyCurrentStateToSolution(configuration);
+ ItemConfiguration solution = cl.createReorderAlgorithm()
+ .calculateReorder(
+ new ReorderParameters(
+ testCaseXYinPixels[0],
+ testCaseXYinPixels[1],
+ spanX,
+ spanY,
+ minSpanX,
+ minSpanY,
+ null,
+ configuration
+ )
+ );
if (solution == null) {
solution = new ItemConfiguration();
solution.isSolution = false;
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 5f536c7..8ad2249 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -590,6 +590,12 @@
getInstrumentation().getTargetContext().startActivity(intent);
assertTrue("App didn't start: " + selector,
TestHelpers.wait(Until.hasObject(selector), DEFAULT_UI_TIMEOUT));
+
+ // Wait for the Launcher to stop.
+ final LauncherInstrumentation launcherInstrumentation = new LauncherInstrumentation();
+ Wait.atMost("Launcher activity didn't stop",
+ () -> !launcherInstrumentation.isLauncherActivityStarted(),
+ DEFAULT_ACTIVITY_TIMEOUT, launcherInstrumentation);
}
public static ActivityInfo resolveSystemAppInfo(String category) {
diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
index b2ce400..cce4c5b 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
@@ -113,9 +113,11 @@
}
private void setResult(boolean success) {
- getInstrumentation().getTargetContext().sendBroadcast(
- WidgetConfigActivity.getCommandIntent(WidgetConfigActivity.class,
- success ? "clickOK" : "clickCancel"));
+ mLauncher.executeAndWaitForWallpaperAnimation(() ->
+ getInstrumentation().getTargetContext().sendBroadcast(
+ WidgetConfigActivity.getCommandIntent(WidgetConfigActivity.class,
+ success ? "clickOK" : "clickCancel")),
+ "setting widget coinfig result");
}
/**
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 5b5077e..f8e0f3d 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -585,6 +585,7 @@
if (hasSystemLauncherObject(OVERVIEW_RES_ID)) return "Overview";
if (hasLauncherObject(WORKSPACE_RES_ID)) return "Workspace";
if (hasLauncherObject(APPS_RES_ID)) return "AllApps";
+ if (hasLauncherObject(TASKBAR_RES_ID)) return "Taskbar";
if (mDevice.hasObject(By.pkg(getLauncherPackageName()).depth(0))) {
return "<Launcher in invalid state>";
}
@@ -944,6 +945,12 @@
fail("Launcher didn't initialize");
}
+ public boolean isLauncherActivityStarted() {
+ return getTestInfo(
+ TestProtocol.REQUEST_IS_LAUNCHER_LAUNCHER_ACTIVITY_STARTED).
+ getBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ }
+
Parcelable executeAndWaitForLauncherEvent(Runnable command,
UiAutomation.AccessibilityEventFilter eventFilter, Supplier<String> message,
String actionName) {
@@ -2306,4 +2313,14 @@
}
return result;
}
+
+ /** Executes a runnable and waits for the wallpaper-open animation completion. */
+ public void executeAndWaitForWallpaperAnimation(Runnable r, String actionName) {
+ executeAndWaitForLauncherEvent(
+ () -> r.run(),
+ event -> TestProtocol.WALLPAPER_OPEN_ANIMATION_FINISHED_MESSAGE
+ .equals(event.getClassName().toString()),
+ () -> "Didn't detect finishing wallpaper-open animation",
+ actionName);
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
index 8a34f0d..068482e 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
@@ -16,8 +16,6 @@
package com.android.launcher3.tapl;
-import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
-
import android.graphics.Rect;
import androidx.annotation.NonNull;
@@ -192,8 +190,8 @@
private List<Integer> getCurrentTasksCenterXList() {
return mLauncher.isTablet()
? mOverview.getCurrentTasksForTablet().stream()
- .map(OverviewTask::getTaskCenterX)
- .collect(Collectors.toList())
+ .map(OverviewTask::getTaskCenterX)
+ .collect(Collectors.toList())
: List.of(mOverview.getCurrentTask().getTaskCenterX());
}
@@ -203,11 +201,11 @@
public LaunchedAppState open() {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
verifyActiveContainer();
- mLauncher.executeAndWaitForEvent(
+ mLauncher.executeAndWaitForLauncherEvent(
() -> mLauncher.clickLauncherObject(mTask),
- event -> event.getEventType() == TYPE_WINDOW_STATE_CHANGED,
- () -> "Launching task didn't open a new window: "
- + mTask.getParent().getContentDescription(),
+ event -> TestProtocol.LAUNCHER_ACTIVITY_STOPPED_MESSAGE
+ .equals(event.getClassName().toString()),
+ () -> "Launcher activity didn't stop",
"clicking an overview task");
if (mOverview.getContainerType()
== LauncherInstrumentation.ContainerType.SPLIT_SCREEN_SELECT) {
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java b/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java
index 38cc321..4a0131b 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java
@@ -20,6 +20,8 @@
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.UiObject2;
+import com.android.launcher3.testing.shared.TestProtocol;
+
/** Represents the menu of an overview task. */
public class OverviewTaskMenu {
@@ -59,8 +61,13 @@
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"before tapping the app info menu item")) {
- mLauncher.clickLauncherObject(
- mLauncher.findObjectInContainer(mMenu, By.text("App info")));
+ mLauncher.executeAndWaitForLauncherEvent(
+ () -> mLauncher.clickLauncherObject(
+ mLauncher.findObjectInContainer(mMenu, By.text("App info"))),
+ event -> TestProtocol.LAUNCHER_ACTIVITY_STOPPED_MESSAGE
+ .equals(event.getClassName().toString()),
+ () -> "Launcher activity didn't stop",
+ "tapped app info menu item");
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
"tapped app info menu item")) {