Merge "Do not tint task/nav bar icons in folded state when task bar on phones is enabled" into main
diff --git a/OWNERS b/OWNERS
index a66bf54..22efa33 100644
--- a/OWNERS
+++ b/OWNERS
@@ -30,6 +30,7 @@
jeremysim@google.com
atsjenk@google.com
brianji@google.com
+hwwang@google.com
# Overview eng team
alexchau@google.com
@@ -52,4 +53,4 @@
per-file DeviceConfigWrapper.java = sunnygoyal@google.com, winsonc@google.com, adamcohen@google.com, hyunyoungs@google.com
# Predictive Back
-per-file LauncherBackAnimationController.java = shanh@google.com, gallmann@google.com
\ No newline at end of file
+per-file LauncherBackAnimationController.java = shanh@google.com, gallmann@google.com
diff --git a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
index 8b5ed7c..6af5a30 100644
--- a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
+++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
@@ -205,6 +205,7 @@
mActive = true;
}
+ @WorkerThread
@Override
public void workspaceLoadComplete() {
super.workspaceLoadComplete();
@@ -323,6 +324,7 @@
}
}
+ @WorkerThread
@Override
public void destroy() {
super.destroy();
diff --git a/quickstep/src/com/android/launcher3/model/WellbeingModel.java b/quickstep/src/com/android/launcher3/model/WellbeingModel.java
index a7c9652..28bc01c 100644
--- a/quickstep/src/com/android/launcher3/model/WellbeingModel.java
+++ b/quickstep/src/com/android/launcher3/model/WellbeingModel.java
@@ -111,6 +111,7 @@
mWorkerHandler.post(this::initializeInBackground);
}
+ @WorkerThread
private void initializeInBackground() {
if (!TextUtils.isEmpty(mWellbeingProviderPkg)) {
mContext.registerReceiver(
@@ -134,8 +135,8 @@
public void close() {
if (!TextUtils.isEmpty(mWellbeingProviderPkg)) {
mWorkerHandler.post(() -> {
- mWellbeingAppChangeReceiver.unregisterReceiverSafely(mContext);
- mAppAddRemoveReceiver.unregisterReceiverSafely(mContext);
+ mWellbeingAppChangeReceiver.unregisterReceiverSafelySync(mContext);
+ mAppAddRemoveReceiver.unregisterReceiverSafelySync(mContext);
mContext.getContentResolver().unregisterContentObserver(mContentObserver);
});
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
index f703463..a9b34d2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
@@ -18,7 +18,6 @@
import static android.view.KeyEvent.ACTION_UP;
import static android.view.KeyEvent.KEYCODE_BACK;
-import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION;
import android.content.Context;
@@ -42,7 +41,6 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
-import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.MultiPropertyFactory;
import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
import com.android.launcher3.views.BaseDragLayer;
@@ -106,10 +104,6 @@
mTaskbarBackgroundAlpha = new MultiPropertyFactory<>(this, BG_ALPHA, INDEX_COUNT,
(a, b) -> a * b, 1f);
mTaskbarBackgroundAlpha.get(INDEX_ALL_OTHER_STATES).setValue(0);
- mTaskbarBackgroundAlpha.get(INDEX_STASH_ANIM).setValue(
- enableScalingRevealHomeAnimation() && DisplayController.isTransientTaskbar(context)
- ? 0
- : 1);
}
public void init(TaskbarDragLayerController.TaskbarDragLayerCallbacks callbacks) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index 2a58db2..051bdc8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -304,7 +304,7 @@
.register(NAV_BAR_KIDS_MODE, mOnSettingsChangeListener);
Log.d(TASKBAR_NOT_DESTROYED_TAG, "registering component callbacks from constructor.");
mContext.registerComponentCallbacks(mComponentCallbacks);
- mShutdownReceiver.register(mContext, Intent.ACTION_SHUTDOWN);
+ mShutdownReceiver.registerAsync(mContext, Intent.ACTION_SHUTDOWN);
UI_HELPER_EXECUTOR.execute(() -> {
mSharedState.taskbarSystemActionPendingIntent = PendingIntent.getBroadcast(
mContext,
@@ -582,8 +582,7 @@
public void destroy() {
debugWhyTaskbarNotDestroyed("TaskbarManager#destroy()");
removeActivityCallbacksAndListeners();
- UI_HELPER_EXECUTOR.execute(
- () -> mTaskbarBroadcastReceiver.unregisterReceiverSafely(mContext));
+ mTaskbarBroadcastReceiver.unregisterReceiverSafelyAsync(mContext);
destroyExistingTaskbar();
removeTaskbarRootViewFromWindow();
if (mUserUnlocked) {
@@ -595,7 +594,7 @@
.unregister(NAV_BAR_KIDS_MODE, mOnSettingsChangeListener);
Log.d(TASKBAR_NOT_DESTROYED_TAG, "unregistering component callbacks from destroy().");
mContext.unregisterComponentCallbacks(mComponentCallbacks);
- mContext.unregisterReceiver(mShutdownReceiver);
+ mShutdownReceiver.unregisterReceiverSafelyAsync(mContext);
}
public @Nullable TaskbarActivityContext getCurrentActivityContext() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 7ff887c..fa2d907 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -245,7 +245,7 @@
private Animator mTaskbarBackgroundAlphaAnimator;
private long mTaskbarBackgroundDuration;
- private boolean mIsGoingHome;
+ private boolean mUserIsNotGoingHome = false;
// Evaluate whether the handle should be stashed
private final LongPredicate mIsStashedPredicate = flags -> {
@@ -338,7 +338,16 @@
// For now, assume we're in an app, since LauncherTaskbarUIController won't be able to tell
// us that we're paused until a bit later. This avoids flickering upon recreating taskbar.
updateStateForFlag(FLAG_IN_APP, true);
+
applyState(/* duration = */ 0);
+
+ // Hide the background while stashed so it doesn't show on fast swipes home
+ boolean shouldHideTaskbarBackground = enableScalingRevealHomeAnimation()
+ && DisplayController.isTransientTaskbar(mActivity)
+ && isStashed();
+
+ mTaskbarBackgroundAlphaForStash.setValue(shouldHideTaskbarBackground ? 0 : 1);
+
if (mTaskbarSharedState.getTaskbarWasPinned()
|| !mTaskbarSharedState.taskbarWasStashedAuto) {
tryStartTaskbarTimeout();
@@ -828,17 +837,13 @@
private boolean mTaskbarBgAlphaAnimationStarted = false;
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
- if (mIsGoingHome) {
- mTaskbarBgAlphaAnimationStarted = true;
- }
if (mTaskbarBgAlphaAnimationStarted) {
return;
}
if (valueAnimator.getAnimatedFraction() >= ANIMATED_FRACTION_THRESHOLD) {
- if (!mIsGoingHome) {
+ if (mUserIsNotGoingHome) {
playTaskbarBackgroundAlphaAnimation();
- setUserIsGoingHome(false);
mTaskbarBgAlphaAnimationStarted = true;
}
}
@@ -850,8 +855,8 @@
/**
* Sets whether the user is going home based on the current gesture.
*/
- public void setUserIsGoingHome(boolean isGoingHome) {
- mIsGoingHome = isGoingHome;
+ public void setUserIsNotGoingHome(boolean userIsNotGoingHome) {
+ mUserIsNotGoingHome = userIsNotGoingHome;
}
/**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index 593285f..ce281c3 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -415,7 +415,7 @@
/**
* Sets whether the user is going home based on the current gesture.
*/
- public void setUserIsGoingHome(boolean isGoingHome) {
- mControllers.taskbarStashController.setUserIsGoingHome(isGoingHome);
+ public void setUserIsNotGoingHome(boolean isNotGoingHome) {
+ mControllers.taskbarStashController.setUserIsNotGoingHome(isNotGoingHome);
}
}
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 1acafab..04207bb 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -1196,7 +1196,7 @@
}
if (mContainerInterface.getTaskbarController() != null) {
// Resets this value as the gesture is now complete.
- mContainerInterface.getTaskbarController().setUserIsGoingHome(false);
+ mContainerInterface.getTaskbarController().setUserIsNotGoingHome(false);
}
ActiveGestureLog.INSTANCE.addLog(
new ActiveGestureLog.CompoundString("onSettledOnEndTarget ")
@@ -1350,7 +1350,7 @@
&& mIsTransientTaskbar
&& mContainerInterface.getTaskbarController() != null) {
mContainerInterface.getTaskbarController()
- .setUserIsGoingHome(endTarget == GestureState.GestureEndTarget.HOME);
+ .setUserIsNotGoingHome(endTarget != GestureState.GestureEndTarget.HOME);
}
float endShift = endTarget == ALL_APPS ? mDragLengthFactor
@@ -1796,6 +1796,8 @@
&& (windowRotation == ROTATION_90 || windowRotation == ROTATION_270)) {
builder.setFromRotation(mRemoteTargetHandles[0].getTaskViewSimulator(), windowRotation,
taskInfo.displayCutoutInsets);
+ } else if (taskInfo.displayCutoutInsets != null) {
+ builder.setDisplayCutoutInsets(taskInfo.displayCutoutInsets);
}
final SwipePipToHomeAnimator swipePipToHomeAnimator = builder.build();
AnimatorPlaybackController activityAnimationToHome =
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 7da92bc..8f533a3 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -265,6 +265,10 @@
case TYPE_HOME:
ActiveGestureLog.INSTANCE.addLog(
"OverviewCommandHelper.executeCommand(TYPE_HOME)");
+ // Although IActivityTaskManager$Stub$Proxy.startActivity is a slow binder call,
+ // we should still call it on main thread because launcher is waiting for
+ // ActivityTaskManager to resume it. Also calling startActivity() on bg thread
+ // could potentially delay resuming launcher. See b/348668521 for more details.
mService.startActivity(mOverviewComponentObserver.getHomeIntent());
return true;
case TYPE_SHOW:
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
index a71e314..9c64576 100644
--- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -36,6 +36,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
import com.android.launcher3.R;
import com.android.launcher3.util.SimpleBroadcastReceiver;
@@ -101,7 +102,7 @@
mConfigChangesMap.append(fallbackComponent.hashCode(), fallbackInfo.configChanges);
} catch (PackageManager.NameNotFoundException ignored) { /* Impossible */ }
- mUserPreferenceChangeReceiver.register(mContext, ACTION_PREFERRED_ACTIVITY_CHANGED);
+ mUserPreferenceChangeReceiver.registerAsync(mContext, ACTION_PREFERRED_ACTIVITY_CHANGED);
updateOverviewTargets();
}
@@ -114,6 +115,8 @@
mOverviewChangeListener = overviewChangeListener;
}
+ /** Called on {@link TouchInteractionService#onSystemUiFlagsChanged} */
+ @UiThread
public void onSystemUiStateChanged() {
if (mDeviceState.isHomeDisabled() != mIsHomeDisabled) {
updateOverviewTargets();
@@ -128,6 +131,7 @@
* Update overview intent and {@link BaseActivityInterface} based off the current launcher home
* component.
*/
+ @UiThread
private void updateOverviewTargets() {
ComponentName defaultHome = PackageManagerWrapper.getInstance()
.getHomeActivities(new ArrayList<>());
@@ -187,8 +191,9 @@
unregisterOtherHomeAppUpdateReceiver();
mUpdateRegisteredPackage = defaultHome.getPackageName();
- mOtherHomeAppUpdateReceiver.registerPkgActions(mContext, mUpdateRegisteredPackage,
- ACTION_PACKAGE_ADDED, ACTION_PACKAGE_CHANGED, ACTION_PACKAGE_REMOVED);
+ mOtherHomeAppUpdateReceiver.registerPkgActionsAsync(
+ mContext, mUpdateRegisteredPackage, ACTION_PACKAGE_ADDED,
+ ACTION_PACKAGE_CHANGED, ACTION_PACKAGE_REMOVED);
}
}
mOverviewChangeListener.accept(mIsHomeAndOverviewSame);
@@ -198,13 +203,13 @@
* Clean up any registered receivers.
*/
public void onDestroy() {
- mContext.unregisterReceiver(mUserPreferenceChangeReceiver);
+ mUserPreferenceChangeReceiver.unregisterReceiverSafelyAsync(mContext);
unregisterOtherHomeAppUpdateReceiver();
}
private void unregisterOtherHomeAppUpdateReceiver() {
if (mUpdateRegisteredPackage != null) {
- mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver);
+ mOtherHomeAppUpdateReceiver.unregisterReceiverSafelyAsync(mContext);
mUpdateRegisteredPackage = null;
}
}
diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
index fd141c3..4691ea9 100644
--- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
@@ -228,6 +228,7 @@
// Take the thumbnail of the task without a scrim and apply it back after
float alpha = mThumbnailView.getDimAlpha();
+ // TODO(b/348643341) add ability to get override the scrim for this Bitmap retrieval
mThumbnailView.setDimAlpha(0);
Bitmap thumbnail = RecentsTransition.drawViewIntoHardwareBitmap(
taskBounds.width(), taskBounds.height(), mThumbnailView, 1f,
@@ -492,18 +493,8 @@
TaskContainer taskContainer) {
boolean isTablet = container.getDeviceProfile().isTablet;
boolean isGridOnlyOverview = isTablet && Flags.enableGridOnlyOverview();
- // Extra conditions if it's not grid-only overview
if (!isGridOnlyOverview) {
- RecentsOrientedState orientedState = taskContainer.getTaskView().getOrientedState();
- boolean isFakeLandscape = !orientedState.isRecentsActivityRotationAllowed()
- && orientedState.getTouchRotation() != ROTATION_0;
- if (!isFakeLandscape) {
- return null;
- }
- // Disallow "Select" when swiping up from landscape due to rotated thumbnail.
- if (orientedState.getDisplayRotation() != ROTATION_0) {
- return null;
- }
+ return null;
}
SystemShortcut modalStateSystemShortcut =
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
index 2836c89..dbe2b19 100644
--- a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
@@ -53,25 +53,31 @@
TaskThumbnailViewModel(
recentsView.mRecentsViewData,
(parent as TaskView).taskViewData,
+ (parent as TaskView).getTaskContainerForTaskThumbnailView(this)!!.taskContainerData,
recentsView.mTasksRepository,
)
}
private var uiState: TaskThumbnailUiState = Uninitialized
private var inheritedScale: Float = 1f
+ private var dimProgress: Float = 0f
private val backgroundPaint = Paint(Paint.ANTI_ALIAS_FLAG)
+ private val scrimPaint = Paint().apply { color = Color.BLACK }
private val _measuredBounds = Rect()
private val measuredBounds: Rect
get() {
_measuredBounds.set(0, 0, measuredWidth, measuredHeight)
return _measuredBounds
}
+
private var cornerRadius: Float = TaskCornerRadius.get(context)
private var fullscreenCornerRadius: Float = QuickStepContract.getWindowCornerRadius(context)
constructor(context: Context?) : super(context)
+
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
+
constructor(
context: Context?,
attrs: AttributeSet?,
@@ -87,6 +93,13 @@
invalidate()
}
}
+ MainScope().launch {
+ viewModel.dimProgress.collect { dimProgress ->
+ // TODO(b/348195366) Add fade in/out for scrim
+ this@TaskThumbnailView.dimProgress = dimProgress
+ invalidate()
+ }
+ }
MainScope().launch { viewModel.recentsFullscreenProgress.collect { invalidateOutline() } }
MainScope().launch {
viewModel.inheritedScale.collect { viewModelInheritedScale ->
@@ -111,6 +124,10 @@
is Snapshot -> drawSnapshotState(canvas, uiStateVal)
is BackgroundOnly -> drawBackgroundOnly(canvas, uiStateVal.backgroundColor)
}
+
+ if (dimProgress > 0) {
+ drawScrim(canvas)
+ }
}
private fun drawBackgroundOnly(canvas: Canvas, @ColorInt backgroundColor: Int) {
@@ -135,6 +152,11 @@
canvas.drawBitmap(snapshot.bitmap, snapshot.drawnRect, measuredBounds, null)
}
+ private fun drawScrim(canvas: Canvas) {
+ scrimPaint.alpha = (dimProgress * MAX_SCRIM_ALPHA).toInt()
+ canvas.drawRect(measuredBounds, scrimPaint)
+ }
+
private fun getCurrentCornerRadius() =
Utilities.mapRange(
viewModel.recentsFullscreenProgress.value,
@@ -145,5 +167,6 @@
companion object {
private val CLEAR_PAINT =
Paint().apply { xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR) }
+ private const val MAX_SCRIM_ALPHA = (0.4f * 255).toInt()
}
}
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModel.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModel.kt
index 4511ea7..fe21174 100644
--- a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModel.kt
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModel.kt
@@ -25,6 +25,7 @@
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.LiveTile
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Snapshot
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Uninitialized
+import com.android.quickstep.task.viewmodel.TaskContainerData
import com.android.quickstep.task.viewmodel.TaskViewData
import com.android.systemui.shared.recents.model.Task
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -40,6 +41,7 @@
class TaskThumbnailViewModel(
recentsViewData: RecentsViewData,
taskViewData: TaskViewData,
+ taskContainerData: TaskContainerData,
private val tasksRepository: RecentTasksRepository,
) {
private val task = MutableStateFlow<Flow<Task?>>(flowOf(null))
@@ -50,6 +52,7 @@
combine(recentsViewData.scale, taskViewData.scale) { recentsScale, taskScale ->
recentsScale * taskScale
}
+ val dimProgress: Flow<Float> = taskContainerData.taskMenuOpenProgress
val uiState: Flow<TaskThumbnailUiState> =
task
.flatMapLatest { taskFlow ->
diff --git a/quickstep/src/com/android/quickstep/task/viewmodel/TaskContainerData.kt b/quickstep/src/com/android/quickstep/task/viewmodel/TaskContainerData.kt
new file mode 100644
index 0000000..769424c
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/task/viewmodel/TaskContainerData.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 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.quickstep.task.viewmodel
+
+import kotlinx.coroutines.flow.MutableStateFlow
+
+class TaskContainerData {
+ val taskMenuOpenProgress = MutableStateFlow(0f)
+}
diff --git a/quickstep/src/com/android/quickstep/util/AsyncClockEventDelegate.java b/quickstep/src/com/android/quickstep/util/AsyncClockEventDelegate.java
index cda87c0..c26fc0c5 100644
--- a/quickstep/src/com/android/quickstep/util/AsyncClockEventDelegate.java
+++ b/quickstep/src/com/android/quickstep/util/AsyncClockEventDelegate.java
@@ -18,8 +18,6 @@
import static android.content.Intent.ACTION_TIMEZONE_CHANGED;
import static android.content.Intent.ACTION_TIME_CHANGED;
-import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -64,9 +62,7 @@
private AsyncClockEventDelegate(Context context) {
super(context);
mContext = context;
-
- UI_HELPER_EXECUTOR.execute(() ->
- mReceiver.register(mContext, ACTION_TIME_CHANGED, ACTION_TIMEZONE_CHANGED));
+ mReceiver.registerAsync(mContext, ACTION_TIME_CHANGED, ACTION_TIMEZONE_CHANGED);
}
@Override
@@ -127,6 +123,6 @@
public void close() {
mDestroyed = true;
SettingsCache.INSTANCE.get(mContext).unregister(mFormatUri, this);
- UI_HELPER_EXECUTOR.execute(() -> mReceiver.unregisterReceiverSafely(mContext));
+ mReceiver.unregisterReceiverSafelyAsync(mContext);
}
}
diff --git a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
index 2b944bc..88c3a08 100644
--- a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
@@ -442,13 +442,21 @@
return this;
}
+ public Builder setDisplayCutoutInsets(@NonNull Rect displayCutoutInsets) {
+ mDisplayCutoutInsets = new Rect(displayCutoutInsets);
+ return this;
+ }
+
public SwipePipToHomeAnimator build() {
if (mDestinationBoundsTransformed.isEmpty()) {
mDestinationBoundsTransformed.set(mDestinationBounds);
}
// adjust the mSourceRectHint / mAppBounds by display cutout if applicable.
if (mSourceRectHint != null && mDisplayCutoutInsets != null) {
- if (mFromRotation == Surface.ROTATION_90) {
+ if (mFromRotation == Surface.ROTATION_0 && mDisplayCutoutInsets.top >= 0) {
+ // TODO: this is to special case the issues on Pixel Foldable device(s).
+ mSourceRectHint.offset(mDisplayCutoutInsets.left, mDisplayCutoutInsets.top);
+ } else if (mFromRotation == Surface.ROTATION_90) {
mSourceRectHint.offset(mDisplayCutoutInsets.left, mDisplayCutoutInsets.top);
} else if (mFromRotation == Surface.ROTATION_270) {
mAppBounds.inset(mDisplayCutoutInsets);
@@ -462,15 +470,6 @@
}
}
- private static class RotatedPosition {
- private final float degree;
- private final float positionX;
- private final float positionY;
-
- private RotatedPosition(float degree, float positionX, float positionY) {
- this.degree = degree;
- this.positionX = positionX;
- this.positionY = positionY;
- }
+ private record RotatedPosition(float degree, float positionX, float positionY) {
}
}
diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt b/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
index 936f6a1..4c78e21 100644
--- a/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
@@ -193,23 +193,23 @@
}
val taskContainer =
TaskContainer(
- task,
- // TODO(b/338360089): Support new TTV for DesktopTaskView
- thumbnailView = null,
- thumbnailViewDeprecated,
- iconView,
- TransformingTouchDelegate(iconView.asView()),
- SplitConfigurationOptions.STAGE_POSITION_UNDEFINED,
- digitalWellBeingToast = null,
- showWindowsView = null,
- taskOverlayFactory
- )
- .apply { thumbnailViewDeprecated.bind(task, overlay) }
+ task,
+ // TODO(b/338360089): Support new TTV for DesktopTaskView
+ thumbnailView = null,
+ thumbnailViewDeprecated,
+ iconView,
+ TransformingTouchDelegate(iconView.asView()),
+ SplitConfigurationOptions.STAGE_POSITION_UNDEFINED,
+ digitalWellBeingToast = null,
+ showWindowsView = null,
+ taskOverlayFactory
+ )
if (index >= taskContainers.size) {
taskContainers.add(taskContainer)
} else {
taskContainers[index] = taskContainer
}
+ taskContainer.bind()
}
repeat(taskContainers.size - tasks.size) {
with(taskContainers.removeLast()) {
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt b/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
index d6a3376..6296b0e 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
@@ -145,6 +145,8 @@
taskOverlayFactory
)
)
+ taskContainers.forEach { it.bind() }
+
this.splitBoundsConfig =
splitBoundsConfig?.also {
taskContainers[0]
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 43a3eda..d806e3d 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -316,7 +316,6 @@
/**
* Can be used to tint the color of the RecentsView to simulate a scrim that can views
* excluded from. Really should be a proper scrim.
- * TODO(b/187528071): Remove this and replace with a real scrim.
*/
private static final FloatProperty<RecentsView> COLOR_TINT =
new FloatProperty<RecentsView>("colorTint") {
@@ -557,7 +556,6 @@
@Nullable
protected GestureState.GestureEndTarget mCurrentGestureEndTarget;
- // TODO(b/187528071): Remove these and replace with a real scrim.
private float mColorTint;
private final int mTintingColor;
@Nullable
@@ -6032,6 +6030,7 @@
* tasks to be dimmed while other elements in the recents view are left alone.
*/
public void showForegroundScrim(boolean show) {
+ // TODO(b/335606129) Add scrim response into new TTV - this is called from overlay
if (!show && mColorTint == 0) {
if (mTintingAnimator != null) {
mTintingAnimator.cancel();
@@ -6047,7 +6046,6 @@
}
/** Tint the RecentsView and TaskViews in to simulate a scrim. */
- // TODO(b/187528071): Replace this tinting with a scrim on top of RecentsView
private void setColorTint(float tintAmount) {
mColorTint = tintAmount;
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
index eda58c5..4f446b2 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
@@ -18,6 +18,7 @@
import static com.android.app.animation.Interpolators.EMPHASIZED;
import static com.android.launcher3.Flags.enableOverviewIconMenu;
+import static com.android.launcher3.Flags.enableRefactorTaskThumbnail;
import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
import static com.android.quickstep.views.TaskThumbnailViewDeprecated.DIM_ALPHA;
@@ -367,6 +368,14 @@
mTaskContainer.getThumbnailViewDeprecated(), DIM_ALPHA,
closing ? 0 : TaskView.MAX_PAGE_SCRIM_ALPHA),
ObjectAnimator.ofFloat(this, ALPHA, closing ? 0 : 1));
+ if (enableRefactorTaskThumbnail()) {
+ mRevealAnimator.addUpdateListener(animation -> {
+ float animatedFraction = animation.getAnimatedFraction();
+ float openProgress = closing ? (1 - animatedFraction) : animatedFraction;
+ mTaskContainer.getTaskContainerData()
+ .getTaskMenuOpenProgress().setValue(openProgress);
+ });
+ }
mOpenCloseAnimator.addListener(new AnimationSuccessListener() {
@Override
public void onAnimationStart(Animator animation) {
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.kt b/quickstep/src/com/android/quickstep/views/TaskView.kt
index 9c1aaa6..7a3b00f 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskView.kt
@@ -88,6 +88,7 @@
import com.android.quickstep.orientation.RecentsPagedOrientationHandler
import com.android.quickstep.task.thumbnail.TaskThumbnail
import com.android.quickstep.task.thumbnail.TaskThumbnailView
+import com.android.quickstep.task.viewmodel.TaskContainerData
import com.android.quickstep.task.viewmodel.TaskViewData
import com.android.quickstep.util.ActiveGestureErrorDetector
import com.android.quickstep.util.ActiveGestureLog
@@ -667,6 +668,7 @@
taskOverlayFactory
)
)
+ taskContainers.forEach { it.bind() }
setOrientationState(orientedState)
}
@@ -693,24 +695,16 @@
}
val iconView = getOrInflateIconView(iconViewId)
return TaskContainer(
- task,
- thumbnailView,
- thumbnailViewDeprecated,
- iconView,
- TransformingTouchDelegate(iconView.asView()),
- stagePosition,
- DigitalWellBeingToast(container, this),
- findViewById(showWindowViewId)!!,
- taskOverlayFactory
- )
- .apply {
- if (enableRefactorTaskThumbnail()) {
- thumbnailViewDeprecated.setTaskOverlay(overlay)
- bindThumbnailView()
- } else {
- thumbnailViewDeprecated.bind(task, overlay)
- }
- }
+ task,
+ thumbnailView,
+ thumbnailViewDeprecated,
+ iconView,
+ TransformingTouchDelegate(iconView.asView()),
+ stagePosition,
+ DigitalWellBeingToast(container, this),
+ findViewById(showWindowViewId)!!,
+ taskOverlayFactory
+ )
}
protected fun getOrInflateIconView(@IdRes iconViewId: Int): TaskViewIcon {
@@ -1379,7 +1373,6 @@
open fun setColorTint(amount: Float, tintColor: Int) {
taskContainers.forEach {
if (!enableRefactorTaskThumbnail()) {
- // TODO(b/334832108) Add scrim to new TTV
it.thumbnailViewDeprecated.dimAlpha = amount
}
it.iconView.setIconColorTint(tintColor, amount)
@@ -1522,6 +1515,9 @@
resetViewTransforms()
}
+ fun getTaskContainerForTaskThumbnailView(taskThumbnailView: TaskThumbnailView): TaskContainer? =
+ taskContainers.firstOrNull { it.thumbnailView == taskThumbnailView }
+
open fun resetViewTransforms() {
// fullscreenTranslation and accumulatedTranslation should not be reset, as
// resetViewTransforms is called during QuickSwitch scrolling.
@@ -1623,6 +1619,7 @@
taskOverlayFactory: TaskOverlayFactory
) {
val overlay: TaskOverlay<*> = taskOverlayFactory.createOverlay(this)
+ val taskContainerData = TaskContainerData()
val snapshotView: View
get() = thumbnailView ?: thumbnailViewDeprecated
@@ -1656,6 +1653,15 @@
thumbnailView?.let { taskView.removeView(it) }
}
+ fun bind() {
+ if (enableRefactorTaskThumbnail() && thumbnailView != null) {
+ thumbnailViewDeprecated.setTaskOverlay(overlay)
+ bindThumbnailView()
+ } else {
+ thumbnailViewDeprecated.bind(task, overlay)
+ }
+ }
+
// TODO(b/335649589): TaskView's VM will already have access to TaskThumbnailView's VM
// so there will be no need to access TaskThumbnailView's VM through the TaskThumbnailView
fun bindThumbnailView() {
diff --git a/quickstep/tests/src/com/android/launcher3/model/WidgetsPredictionsRequesterTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/model/WidgetsPredictionsRequesterTest.kt
similarity index 100%
rename from quickstep/tests/src/com/android/launcher3/model/WidgetsPredictionsRequesterTest.kt
rename to quickstep/tests/multivalentTests/src/com/android/launcher3/model/WidgetsPredictionsRequesterTest.kt
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarModeRule.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarModeRule.kt
new file mode 100644
index 0000000..3b53cdc
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarModeRule.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2024 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 com.android.launcher3.taskbar.TaskbarModeRule.Mode
+import com.android.launcher3.taskbar.TaskbarModeRule.TaskbarMode
+import com.android.launcher3.util.DisplayController
+import com.android.launcher3.util.MainThreadInitializedObject
+import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext
+import com.android.launcher3.util.NavigationMode
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.spy
+
+/**
+ * Allows tests to specify which Taskbar [Mode] to run under.
+ *
+ * [context] should match the test's target context, so that [MainThreadInitializedObject] instances
+ * are properly sandboxed.
+ *
+ * Annotate tests with [TaskbarMode] to set a mode. If the annotation is omitted for any tests, this
+ * rule is a no-op.
+ *
+ * Make sure this rule precedes any rules that depend on [DisplayController], or else the instance
+ * might be inconsistent across the test lifecycle.
+ */
+class TaskbarModeRule(private val context: SandboxContext) : TestRule {
+ /** The selected Taskbar mode. */
+ enum class Mode {
+ TRANSIENT,
+ PINNED,
+ THREE_BUTTONS,
+ }
+
+ /** Overrides Taskbar [mode] for a test. */
+ @Retention(AnnotationRetention.RUNTIME)
+ @Target(AnnotationTarget.FUNCTION)
+ annotation class TaskbarMode(val mode: Mode)
+
+ override fun apply(base: Statement, description: Description): Statement {
+ val taskbarMode = description.getAnnotation(TaskbarMode::class.java) ?: return base
+
+ return object : Statement() {
+ override fun evaluate() {
+ val mode = taskbarMode.mode
+
+ context.putObject(
+ DisplayController.INSTANCE,
+ object : DisplayController(context) {
+ override fun getInfo(): Info {
+ return spy(super.getInfo()) {
+ on { isTransientTaskbar } doReturn (mode == Mode.TRANSIENT)
+ on { isPinnedTaskbar } doReturn (mode == Mode.PINNED)
+ on { navigationMode } doReturn
+ when (mode) {
+ Mode.TRANSIENT,
+ Mode.PINNED -> NavigationMode.NO_BUTTON
+ Mode.THREE_BUTTONS -> NavigationMode.THREE_BUTTONS
+ }
+ }
+ }
+ },
+ )
+
+ base.evaluate()
+ }
+ }
+ }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarModeRuleTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarModeRuleTest.kt
new file mode 100644
index 0000000..7dfbb9a
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarModeRuleTest.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2024 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 androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import com.android.launcher3.InvariantDeviceProfile
+import com.android.launcher3.taskbar.TaskbarModeRule.Mode.PINNED
+import com.android.launcher3.taskbar.TaskbarModeRule.Mode.THREE_BUTTONS
+import com.android.launcher3.taskbar.TaskbarModeRule.Mode.TRANSIENT
+import com.android.launcher3.taskbar.TaskbarModeRule.TaskbarMode
+import com.android.launcher3.util.DisplayController
+import com.android.launcher3.util.LauncherMultivalentJUnit
+import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext
+import com.android.launcher3.util.NavigationMode
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(LauncherMultivalentJUnit::class)
+class TaskbarModeRuleTest {
+
+ private val context = SandboxContext(getInstrumentation().targetContext)
+
+ @get:Rule val taskbarModeRule = TaskbarModeRule(context)
+
+ @Test
+ @TaskbarMode(TRANSIENT)
+ fun testTaskbarMode_transient_overridesDisplayController() {
+ assertThat(DisplayController.isTransientTaskbar(context)).isTrue()
+ assertThat(DisplayController.isPinnedTaskbar(context)).isFalse()
+ assertThat(DisplayController.getNavigationMode(context)).isEqualTo(NavigationMode.NO_BUTTON)
+ }
+
+ @Test
+ @TaskbarMode(TRANSIENT)
+ fun testTaskbarMode_transient_overridesDeviceProfile() {
+ val dp = InvariantDeviceProfile.INSTANCE.get(context).getDeviceProfile(context)
+ assertThat(dp.isTransientTaskbar).isTrue()
+ assertThat(dp.isGestureMode).isTrue()
+ }
+
+ @Test
+ @TaskbarMode(PINNED)
+ fun testTaskbarMode_pinned_overridesDisplayController() {
+ assertThat(DisplayController.isTransientTaskbar(context)).isFalse()
+ assertThat(DisplayController.isPinnedTaskbar(context)).isTrue()
+ assertThat(DisplayController.getNavigationMode(context)).isEqualTo(NavigationMode.NO_BUTTON)
+ }
+
+ @Test
+ @TaskbarMode(PINNED)
+ fun testTaskbarMode_pinned_overridesDeviceProfile() {
+ val dp = InvariantDeviceProfile.INSTANCE.get(context).getDeviceProfile(context)
+ assertThat(dp.isTransientTaskbar).isFalse()
+ assertThat(dp.isGestureMode).isTrue()
+ }
+
+ @Test
+ @TaskbarMode(THREE_BUTTONS)
+ fun testTaskbarMode_threeButtons_overridesDisplayController() {
+ assertThat(DisplayController.isTransientTaskbar(context)).isFalse()
+ assertThat(DisplayController.isPinnedTaskbar(context)).isFalse()
+ assertThat(DisplayController.getNavigationMode(context))
+ .isEqualTo(NavigationMode.THREE_BUTTONS)
+ }
+
+ @Test
+ @TaskbarMode(THREE_BUTTONS)
+ fun testTaskbarMode_threeButtons_overridesDeviceProfile() {
+ val dp = InvariantDeviceProfile.INSTANCE.get(context).getDeviceProfile(context)
+ assertThat(dp.isTransientTaskbar).isFalse()
+ assertThat(dp.isGestureMode).isFalse()
+ }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarUnitTestRule.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarUnitTestRule.kt
index a999e7f..1c900b8 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarUnitTestRule.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarUnitTestRule.kt
@@ -18,6 +18,7 @@
import android.app.Instrumentation
import android.app.PendingIntent
+import android.content.Context
import android.content.IIntentSender
import android.content.Intent
import androidx.test.platform.app.InstrumentationRegistry
@@ -37,6 +38,8 @@
/**
* Manages the Taskbar lifecycle for unit tests.
*
+ * Tests need to provide their target [context] through the constructor.
+ *
* See [InjectController] for grabbing controller(s) under test with minimal boilerplate.
*
* The rule interacts with [TaskbarManager] on the main thread. A good rule of thumb for tests is
@@ -58,7 +61,7 @@
* }
* ```
*/
-class TaskbarUnitTestRule : MethodRule {
+class TaskbarUnitTestRule(private val context: Context) : MethodRule {
private val instrumentation = InstrumentationRegistry.getInstrumentation()
private val serviceTestRule = ServiceTestRule()
@@ -76,7 +79,6 @@
override fun evaluate() {
this@TaskbarUnitTestRule.target = target
- val context = instrumentation.targetContext
instrumentation.runOnMainSync {
assumeTrue(
LauncherAppState.getIDP(context).getDeviceProfile(context).isTaskbarPresent
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsControllerTest.kt
index fe4e2d2..9a514bf 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsControllerTest.kt
@@ -42,7 +42,7 @@
@EmulatedDevices(["pixelFoldable2023", "pixelTablet2023"])
class TaskbarAllAppsControllerTest {
- @get:Rule val taskbarUnitTestRule = TaskbarUnitTestRule()
+ @get:Rule val taskbarUnitTestRule = TaskbarUnitTestRule(getInstrumentation().targetContext)
@get:Rule val animatorTestRule = AnimatorTestRule(this)
@InjectController lateinit var allAppsController: TaskbarAllAppsController
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayControllerTest.kt
index eebd8f9..918ec7d 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayControllerTest.kt
@@ -41,7 +41,7 @@
@EmulatedDevices(["pixelFoldable2023"])
class TaskbarOverlayControllerTest {
- @get:Rule val taskbarUnitTestRule = TaskbarUnitTestRule()
+ @get:Rule val taskbarUnitTestRule = TaskbarUnitTestRule(getInstrumentation().targetContext)
@InjectController lateinit var overlayController: TaskbarOverlayController
private val taskbarContext: TaskbarActivityContext
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModelTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModelTest.kt
index 3b8754c..a394b65 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModelTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModelTest.kt
@@ -28,6 +28,7 @@
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.LiveTile
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Snapshot
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Uninitialized
+import com.android.quickstep.task.viewmodel.TaskContainerData
import com.android.quickstep.task.viewmodel.TaskViewData
import com.android.systemui.shared.recents.model.Task
import com.android.systemui.shared.recents.model.ThumbnailData
@@ -43,9 +44,10 @@
class TaskThumbnailViewModelTest {
private val recentsViewData = RecentsViewData()
private val taskViewData = TaskViewData()
+ private val taskContainerData = TaskContainerData()
private val tasksRepository = FakeTasksRepository()
private val systemUnderTest =
- TaskThumbnailViewModel(recentsViewData, taskViewData, tasksRepository)
+ TaskThumbnailViewModel(recentsViewData, taskViewData, taskContainerData, tasksRepository)
private val tasks = (0..5).map(::createTaskWithId)
diff --git a/quickstep/tests/src/com/android/quickstep/taskbar/customization/TaskbarSpecsEvaluatorTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/taskbar/customization/TaskbarSpecsEvaluatorTest.kt
similarity index 100%
rename from quickstep/tests/src/com/android/quickstep/taskbar/customization/TaskbarSpecsEvaluatorTest.kt
rename to quickstep/tests/multivalentTests/src/com/android/quickstep/taskbar/customization/TaskbarSpecsEvaluatorTest.kt
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 8546454..f775673 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -97,10 +97,9 @@
if (bubbleBarEnabled) {
float adjustedBorderSpace = dp.getHotseatAdjustedBorderSpaceForBubbleBar(getContext());
if (hasBubbles && Float.compare(adjustedBorderSpace, 0f) != 0) {
- getShortcutsAndWidgets().setTranslationProvider(child -> {
- int index = getShortcutsAndWidgets().indexOfChild(child);
+ getShortcutsAndWidgets().setTranslationProvider(cellX -> {
float borderSpaceDelta = adjustedBorderSpace - dp.hotseatBorderSpace;
- return dp.iconSizePx + index * borderSpaceDelta;
+ return dp.iconSizePx + cellX * borderSpaceDelta;
});
if (mQsb instanceof HorizontalInsettableView) {
HorizontalInsettableView insettableQsb = (HorizontalInsettableView) mQsb;
@@ -147,10 +146,7 @@
// update the translation provider for future layout passes of hotseat icons.
if (isBubbleBarVisible) {
- icons.setTranslationProvider(child -> {
- int index = icons.indexOfChild(child);
- return dp.iconSizePx + index * borderSpaceDelta;
- });
+ icons.setTranslationProvider(cellX -> dp.iconSizePx + cellX * borderSpaceDelta);
} else {
icons.setTranslationProvider(null);
}
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 3b8ff62..239967d 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -116,13 +116,13 @@
SimpleBroadcastReceiver modelChangeReceiver =
new SimpleBroadcastReceiver(mModel::onBroadcastIntent);
- modelChangeReceiver.register(mContext, Intent.ACTION_LOCALE_CHANGED,
+ modelChangeReceiver.registerAsync(mContext, Intent.ACTION_LOCALE_CHANGED,
ACTION_DEVICE_POLICY_RESOURCE_UPDATED);
if (BuildConfig.IS_STUDIO_BUILD) {
mContext.registerReceiver(modelChangeReceiver, new IntentFilter(ACTION_FORCE_ROLOAD),
RECEIVER_EXPORTED);
}
- mOnTerminateCallback.add(() -> mContext.unregisterReceiver(modelChangeReceiver));
+ mOnTerminateCallback.add(() -> modelChangeReceiver.unregisterReceiverSafelyAsync(mContext));
SafeCloseable userChangeListener = UserCache.INSTANCE.get(mContext)
.addUserEventListener(mModel::onUserEvent);
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index 7484b64..d2c3c78 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -245,7 +245,7 @@
}
child.layout(childLeft, childTop, childLeft + lp.width, childTop + lp.height);
if (mTranslationProvider != null) {
- final float tx = mTranslationProvider.getTranslationX(child);
+ final float tx = mTranslationProvider.getTranslationX(lp.getCellX());
if (child instanceof Reorderable) {
((Reorderable) child).getTranslateDelegate()
.getTranslationX(INDEX_BUBBLE_ADJUSTMENT_ANIM)
@@ -330,6 +330,6 @@
/** Provides translation values to apply when laying out child views. */
interface TranslationProvider {
- float getTranslationX(View child);
+ float getTranslationX(int cellX);
}
}
diff --git a/src/com/android/launcher3/pm/UserCache.java b/src/com/android/launcher3/pm/UserCache.java
index ed25186..cf03462 100644
--- a/src/com/android/launcher3/pm/UserCache.java
+++ b/src/com/android/launcher3/pm/UserCache.java
@@ -93,12 +93,12 @@
@Override
public void close() {
- MODEL_EXECUTOR.execute(() -> mUserChangeReceiver.unregisterReceiverSafely(mContext));
+ MODEL_EXECUTOR.execute(() -> mUserChangeReceiver.unregisterReceiverSafelySync(mContext));
}
@WorkerThread
private void initAsync() {
- mUserChangeReceiver.register(mContext,
+ mUserChangeReceiver.registerSync(mContext,
Intent.ACTION_MANAGED_PROFILE_AVAILABLE,
Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE,
Intent.ACTION_MANAGED_PROFILE_REMOVED,
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index 7f36d6f..3dcc663 100644
--- a/src/com/android/launcher3/util/DisplayController.java
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -132,11 +132,11 @@
mWindowContext.registerComponentCallbacks(this);
} else {
mWindowContext = null;
- mReceiver.register(mContext, ACTION_CONFIGURATION_CHANGED);
+ mReceiver.registerAsync(mContext, ACTION_CONFIGURATION_CHANGED);
}
// Initialize navigation mode change listener
- mReceiver.registerPkgActions(mContext, TARGET_OVERLAY_PACKAGE, ACTION_OVERLAY_CHANGED);
+ mReceiver.registerPkgActionsAsync(mContext, TARGET_OVERLAY_PACKAGE, ACTION_OVERLAY_CHANGED);
WindowManagerProxy wmProxy = WindowManagerProxy.INSTANCE.get(context);
Context displayInfoContext = getDisplayInfoContext(display);
@@ -223,6 +223,7 @@
} else {
// TODO: unregister broadcast receiver
}
+ mReceiver.unregisterReceiverSafelyAsync(mContext);
}
/**
diff --git a/src/com/android/launcher3/util/LockedUserState.kt b/src/com/android/launcher3/util/LockedUserState.kt
index 94f9e4f..2737249 100644
--- a/src/com/android/launcher3/util/LockedUserState.kt
+++ b/src/com/android/launcher3/util/LockedUserState.kt
@@ -25,6 +25,7 @@
val isUserUnlockedAtLauncherStartup: Boolean
var isUserUnlocked: Boolean
private set
+
private val mUserUnlockedActions: RunnableList = RunnableList()
@VisibleForTesting
@@ -50,22 +51,18 @@
if (isUserUnlocked) {
notifyUserUnlocked()
} else {
- mUserUnlockedReceiver.register(mContext, Intent.ACTION_USER_UNLOCKED)
+ mUserUnlockedReceiver.registerAsync(mContext, Intent.ACTION_USER_UNLOCKED)
}
}
private fun notifyUserUnlocked() {
mUserUnlockedActions.executeAllAndDestroy()
- Executors.THREAD_POOL_EXECUTOR.execute {
- mUserUnlockedReceiver.unregisterReceiverSafely(mContext)
- }
+ mUserUnlockedReceiver.unregisterReceiverSafelyAsync(mContext)
}
/** Stops the receiver from listening for ACTION_USER_UNLOCK broadcasts. */
override fun close() {
- Executors.THREAD_POOL_EXECUTOR.execute {
- mUserUnlockedReceiver.unregisterReceiverSafely(mContext)
- }
+ mUserUnlockedReceiver.unregisterReceiverSafelyAsync(mContext)
}
/**
diff --git a/src/com/android/launcher3/util/ScreenOnTracker.java b/src/com/android/launcher3/util/ScreenOnTracker.java
index e16e477..c1d192c 100644
--- a/src/com/android/launcher3/util/ScreenOnTracker.java
+++ b/src/com/android/launcher3/util/ScreenOnTracker.java
@@ -42,12 +42,12 @@
// Assume that the screen is on to begin with
mContext = context;
mIsScreenOn = true;
- mReceiver.register(context, ACTION_SCREEN_ON, ACTION_SCREEN_OFF, ACTION_USER_PRESENT);
+ mReceiver.registerAsync(context, ACTION_SCREEN_ON, ACTION_SCREEN_OFF, ACTION_USER_PRESENT);
}
@Override
public void close() {
- mReceiver.unregisterReceiverSafely(mContext);
+ mReceiver.unregisterReceiverSafelyAsync(mContext);
}
private void onReceive(Intent intent) {
diff --git a/src/com/android/launcher3/util/SimpleBroadcastReceiver.java b/src/com/android/launcher3/util/SimpleBroadcastReceiver.java
index 064bcd0..5f39cce 100644
--- a/src/com/android/launcher3/util/SimpleBroadcastReceiver.java
+++ b/src/com/android/launcher3/util/SimpleBroadcastReceiver.java
@@ -15,14 +15,21 @@
*/
package com.android.launcher3.util;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.os.Looper;
import android.os.PatternMatcher;
import android.text.TextUtils;
import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
+import androidx.annotation.WorkerThread;
+
+import com.android.launcher3.BuildConfig;
import java.util.function.Consumer;
@@ -39,21 +46,63 @@
mIntentConsumer.accept(intent);
}
- /**
- * Helper method to register multiple actions
- */
- public void register(Context context, String... actions) {
+ /** Helper method to register multiple actions. Caller should be on main thread. */
+ @UiThread
+ public void registerAsync(Context context, String... actions) {
+ assertOnMainThread();
+ UI_HELPER_EXECUTOR.execute(() -> registerSync(context, actions));
+ }
+
+ /** Helper method to register multiple actions. Caller should be on main thread. */
+ @WorkerThread
+ public void registerSync(Context context, String... actions) {
+ assertOnBgThread();
context.registerReceiver(this, getFilter(actions));
}
/**
- * Helper method to register multiple actions associated with a paction
+ * Helper method to register multiple actions associated with a action. Caller should be from
+ * main thread.
*/
- public void registerPkgActions(Context context, @Nullable String pkg, String... actions) {
+ @UiThread
+ public void registerPkgActionsAsync(Context context, @Nullable String pkg, String... actions) {
+ assertOnMainThread();
+ UI_HELPER_EXECUTOR.execute(() -> registerPkgActionsSync(context, pkg, actions));
+ }
+
+ /**
+ * Helper method to register multiple actions associated with a action. Caller should be from
+ * bg thread.
+ */
+ @WorkerThread
+ public void registerPkgActionsSync(Context context, @Nullable String pkg, String... actions) {
+ assertOnBgThread();
context.registerReceiver(this, getPackageFilter(pkg, actions));
}
/**
+ * Unregisters the receiver ignoring any errors on bg thread. Caller should be on main thread.
+ */
+ @UiThread
+ public void unregisterReceiverSafelyAsync(Context context) {
+ assertOnMainThread();
+ UI_HELPER_EXECUTOR.execute(() -> unregisterReceiverSafelySync(context));
+ }
+
+ /**
+ * Unregisters the receiver ignoring any errors on bg thread. Caller should be on bg thread.
+ */
+ @WorkerThread
+ public void unregisterReceiverSafelySync(Context context) {
+ assertOnBgThread();
+ try {
+ context.unregisterReceiver(this);
+ } catch (IllegalArgumentException e) {
+ // It was probably never registered or already unregistered. Ignore.
+ }
+ }
+
+ /**
* Creates an intent filter to listen for actions with a specific package in the data field.
*/
public static IntentFilter getPackageFilter(String pkg, String... actions) {
@@ -73,14 +122,19 @@
return filter;
}
- /**
- * Unregisters the receiver ignoring any errors
- */
- public void unregisterReceiverSafely(Context context) {
- try {
- context.unregisterReceiver(this);
- } catch (IllegalArgumentException e) {
- // It was probably never registered or already unregistered. Ignore.
+ private static void assertOnBgThread() {
+ if (BuildConfig.IS_STUDIO_BUILD && isMainThread()) {
+ throw new IllegalStateException("Should not be called from main thread!");
}
}
+
+ private static void assertOnMainThread() {
+ if (BuildConfig.IS_STUDIO_BUILD && !isMainThread()) {
+ throw new IllegalStateException("Should not be called from bg thread!");
+ }
+ }
+
+ private static boolean isMainThread() {
+ return Thread.currentThread() == Looper.getMainLooper().getThread();
+ }
}
diff --git a/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java b/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java
index b97b889..a2277a0 100644
--- a/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java
+++ b/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java
@@ -198,10 +198,11 @@
public void setWindowToken(IBinder token) {
mWindowToken = token;
if (mWindowToken == null && mRegistered) {
- mWallpaperChangeReceiver.unregisterReceiverSafely(mWorkspace.getContext());
+ mWallpaperChangeReceiver.unregisterReceiverSafelyAsync(mWorkspace.getContext());
mRegistered = false;
} else if (mWindowToken != null && !mRegistered) {
- mWallpaperChangeReceiver.register(mWorkspace.getContext(), ACTION_WALLPAPER_CHANGED);
+ mWallpaperChangeReceiver.registerAsync(
+ mWorkspace.getContext(), ACTION_WALLPAPER_CHANGED);
onWallpaperChanged();
mRegistered = true;
}
diff --git a/tests/src/com/android/launcher3/allapps/PrivateSpaceSettingsButtonTest.java b/tests/multivalentTests/src/com/android/launcher3/allapps/PrivateSpaceSettingsButtonTest.java
similarity index 96%
rename from tests/src/com/android/launcher3/allapps/PrivateSpaceSettingsButtonTest.java
rename to tests/multivalentTests/src/com/android/launcher3/allapps/PrivateSpaceSettingsButtonTest.java
index 9537e1c..1eb4173 100644
--- a/tests/src/com/android/launcher3/allapps/PrivateSpaceSettingsButtonTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/allapps/PrivateSpaceSettingsButtonTest.java
@@ -24,7 +24,7 @@
import android.content.Context;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.util.ActivityContextWrapper;
diff --git a/tests/src/com/android/launcher3/folder/FolderNameProviderTest.java b/tests/multivalentTests/src/com/android/launcher3/folder/FolderNameProviderTest.java
similarity index 100%
rename from tests/src/com/android/launcher3/folder/FolderNameProviderTest.java
rename to tests/multivalentTests/src/com/android/launcher3/folder/FolderNameProviderTest.java
diff --git a/tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java b/tests/multivalentTests/src/com/android/launcher3/model/DbDowngradeHelperTest.java
similarity index 100%
rename from tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java
rename to tests/multivalentTests/src/com/android/launcher3/model/DbDowngradeHelperTest.java
diff --git a/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java b/tests/multivalentTests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
similarity index 100%
rename from tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
rename to tests/multivalentTests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index a6f4441..6e01f9e 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -239,7 +239,8 @@
final CountDownLatch count = new CountDownLatch(2);
final SimpleBroadcastReceiver broadcastReceiver =
new SimpleBroadcastReceiver(i -> count.countDown());
- broadcastReceiver.registerPkgActions(mTargetContext, pkg,
+ // We OK to make binder calls on main thread in test.
+ broadcastReceiver.registerPkgActionsSync(mTargetContext, pkg,
Intent.ACTION_PACKAGE_RESTARTED, Intent.ACTION_PACKAGE_DATA_CLEARED);
mDevice.executeShellCommand("pm clear " + pkg);