Merge 24Q3 (ab/AP3A.240905.001) to aosp-main-future

Bug: 347831320
Merged-In: I0898d202b9dd4742c0c7d2c0d6b340c748e8acf7
Change-Id: I9b2cede0bdd9bb9a06ee825be7de3df030f51065
This commit is contained in:
Xin Li
2024-08-08 11:16:15 -07:00
17 changed files with 282 additions and 61 deletions
@@ -27,6 +27,7 @@ import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.animation.Interpolator
import android.window.OnBackInvokedDispatcher
import androidx.core.view.updateLayoutParams
import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
import com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE
@@ -66,11 +67,14 @@ constructor(
/** Container where the tooltip's body should be inflated. */
lateinit var content: ViewGroup
private set
private lateinit var arrow: View
/** Callback invoked when the tooltip is being closed. */
var onCloseCallback: () -> Unit = {}
private var openCloseAnimator: AnimatorSet? = null
/** Used to set whether users can tap outside the current tooltip window to dismiss it */
var allowTouchDismissal = true
/** Animates the tooltip into view. */
fun show() {
@@ -134,14 +138,25 @@ constructor(
override fun isOfType(type: Int): Boolean = type and TYPE_TASKBAR_EDUCATION_DIALOG != 0
override fun onControllerInterceptTouchEvent(ev: MotionEvent?): Boolean {
if (ev?.action == ACTION_DOWN && !activityContext.dragLayer.isEventOverView(this, ev)) {
if (
ev?.action == ACTION_DOWN &&
!activityContext.dragLayer.isEventOverView(this, ev) &&
allowTouchDismissal
) {
close(true)
}
return false
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
findOnBackInvokedDispatcher()
?.registerOnBackInvokedCallback(OnBackInvokedDispatcher.PRIORITY_DEFAULT, this)
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
findOnBackInvokedDispatcher()?.unregisterOnBackInvokedCallback(this)
Settings.Secure.putInt(mContext.contentResolver, LAUNCHER_TASKBAR_EDUCATION_SHOWING, 0)
}
@@ -86,10 +86,13 @@ open class TaskbarEduTooltipController(context: Context) :
!activityContext.isPhoneMode &&
!activityContext.isTinyTaskbar
}
private val isOpen: Boolean
get() = tooltip?.isOpen ?: false
val isBeforeTooltipFeaturesStep: Boolean
get() = isTooltipEnabled && tooltipStep <= TOOLTIP_STEP_FEATURES
private lateinit var controllers: TaskbarControllers
// Keep track of whether the user has seen the Search Edu
@@ -152,6 +155,7 @@ open class TaskbarEduTooltipController(context: Context) :
tooltipStep = TOOLTIP_STEP_NONE
inflateTooltip(R.layout.taskbar_edu_features)
tooltip?.run {
allowTouchDismissal = false
val splitscreenAnim = requireViewById<LottieAnimationView>(R.id.splitscreen_animation)
val suggestionsAnim = requireViewById<LottieAnimationView>(R.id.suggestions_animation)
val pinningAnim = requireViewById<LottieAnimationView>(R.id.pinning_animation)
@@ -216,6 +220,7 @@ open class TaskbarEduTooltipController(context: Context) :
inflateTooltip(R.layout.taskbar_edu_pinning)
tooltip?.run {
allowTouchDismissal = true
requireViewById<LottieAnimationView>(R.id.standalone_pinning_animation)
.supportLightTheme()
@@ -260,6 +265,7 @@ open class TaskbarEduTooltipController(context: Context) :
userHasSeenSearchEdu = true
inflateTooltip(R.layout.taskbar_edu_search)
tooltip?.run {
allowTouchDismissal = true
requireViewById<LottieAnimationView>(R.id.search_edu_animation).supportLightTheme()
val eduSubtitle: TextView = requireViewById(R.id.search_edu_text)
showDisclosureText(eduSubtitle)
@@ -332,7 +338,9 @@ open class TaskbarEduTooltipController(context: Context) :
}
/** Closes the current [tooltip]. */
fun hide() = tooltip?.close(true)
fun hide() {
tooltip?.close(true)
}
/** Initializes [tooltip] with content from [contentResId]. */
private fun inflateTooltip(@LayoutRes contentResId: Int) {
@@ -35,7 +35,6 @@ import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Flags;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.LauncherInitListener;
@@ -213,10 +212,10 @@ public final class LauncherActivityInterface extends
if (launcher.isStarted() && (isInLiveTileMode() || launcher.hasBeenResumed())) {
return launcher;
}
if (Flags.useActivityOverlay()
&& SystemUiProxy.INSTANCE.get(launcher).getHomeVisibilityState().isHomeVisible()) {
if (isInMinusOne()) {
return launcher;
}
return null;
}
@@ -293,6 +292,15 @@ public final class LauncherActivityInterface extends
&& TopTaskTracker.INSTANCE.get(launcher).getCachedTopTask(false).isHomeTask();
}
private boolean isInMinusOne() {
QuickstepLauncher launcher = getCreatedContainer();
return launcher != null
&& launcher.getStateManager().getState() == NORMAL
&& !launcher.isStarted()
&& TopTaskTracker.INSTANCE.get(launcher).getCachedTopTask(false).isHomeTask();
}
@Override
public void onLaunchTaskFailed() {
QuickstepLauncher launcher = getCreatedContainer();
@@ -132,6 +132,13 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> implem
* Init drag layer and overview panel views.
*/
protected void setupViews() {
SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.get(this);
// SplitSelectStateController needs to be created before setContentView()
mSplitSelectStateController =
new SplitSelectStateController(this, mHandler, getStateManager(),
null /* depthController */, getStatsLogManager(),
systemUiProxy, RecentsModel.INSTANCE.get(this),
null /*activityBackCallback*/);
inflateRootView(R.layout.fallback_recents_activity);
setContentView(getRootView());
mDragLayer = findViewById(R.id.drag_layer);
@@ -139,12 +146,6 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> implem
mFallbackRecentsView = findViewById(R.id.overview_panel);
mActionsView = findViewById(R.id.overview_actions_view);
getRootView().getSysUiScrim().getSysUIProgress().updateValue(0);
SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.get(this);
mSplitSelectStateController =
new SplitSelectStateController(this, mHandler, getStateManager(),
null /* depthController */, getStatsLogManager(),
systemUiProxy, RecentsModel.INSTANCE.get(this),
null /*activityBackCallback*/);
mDragLayer.recreateControllers();
if (enableDesktopWindowingMode()) {
mDesktopRecentsTransitionController = new DesktopRecentsTransitionController(
@@ -17,6 +17,7 @@ package com.android.quickstep;
import static com.android.app.animation.Interpolators.ACCELERATE_1_5;
import static com.android.app.animation.Interpolators.LINEAR;
import static com.android.launcher3.Flags.enableAdditionalHomeAnimations;
import static com.android.launcher3.PagedView.INVALID_PAGE;
import android.animation.Animator;
@@ -449,7 +450,7 @@ public abstract class SwipeUpAnimationLogic implements
float alpha = mAnimationFactory.getWindowAlpha(progress);
mHomeAnim.setPlayFraction(progress);
if (mTargetTaskView == null) {
if (!enableAdditionalHomeAnimations() || mTargetTaskView == null) {
mHomeToWindowPositionMap.mapRect(mWindowCurrentRect, currentRect);
mMatrix.setRectToRect(mCropRectF, mWindowCurrentRect, ScaleToFit.FILL);
mLocalTransformParams
@@ -464,10 +465,15 @@ public abstract class SwipeUpAnimationLogic implements
mLocalTransformParams.applySurfaceParams(
mLocalTransformParams.createSurfaceParams(this));
mAnimationFactory.update(
currentRect, progress, mMatrix.mapRadius(cornerRadius), (int) (alpha * 255));
if (mTargetTaskView == null) {
mAnimationFactory.update(
currentRect,
progress,
mMatrix.mapRadius(cornerRadius),
!enableAdditionalHomeAnimations() || mTargetTaskView == null
? 0 : (int) (alpha * 255));
if (!enableAdditionalHomeAnimations() || mTargetTaskView == null) {
return;
}
if (mAnimationFactory.isAnimatingIntoIcon() && mAnimationFactory.isAnimationReady()) {
@@ -506,7 +512,7 @@ public abstract class SwipeUpAnimationLogic implements
public void onAnimationStart(Animator animation) {
setUp();
mHomeAnim.dispatchOnStart();
if (mTargetTaskView == null) {
if (!enableAdditionalHomeAnimations() || mTargetTaskView == null) {
return;
}
Rect thumbnailBounds = new Rect();
@@ -521,7 +527,7 @@ public abstract class SwipeUpAnimationLogic implements
}
private void setUp() {
if (mTargetTaskView == null) {
if (!enableAdditionalHomeAnimations() || mTargetTaskView == null) {
return;
}
RecentsView recentsView = mTargetTaskView.getRecentsView();
@@ -542,7 +548,7 @@ public abstract class SwipeUpAnimationLogic implements
}
private void cleanUp() {
if (mTargetTaskView == null) {
if (!enableAdditionalHomeAnimations() || mTargetTaskView == null) {
return;
}
RecentsView recentsView = mTargetTaskView.getRecentsView();
@@ -492,18 +492,8 @@ public interface TaskShortcutFactory {
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 =
@@ -259,7 +259,8 @@ public class PortraitPagedViewHandler extends DefaultPagedViewHandler implements
return new Pair<>(translationX, translationY);
}
bannerParams.gravity = BOTTOM | ((deviceProfile.isLandscape) ? START : CENTER_HORIZONTAL);
bannerParams.gravity =
BOTTOM | (deviceProfile.isLeftRightSplit ? START : CENTER_HORIZONTAL);
// Set correct width
if (desiredTaskId == splitBounds.leftTopTaskId) {
@@ -50,7 +50,7 @@ private constructor(
private val disappearanceDurationMs: Long,
private val interpolator: Interpolator,
) {
private val borderAnimationProgress = AnimatedFloat { updateOutline() }
private val borderAnimationProgress = AnimatedFloat { _ -> updateOutline() }
private val borderPaint =
Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = borderColor
@@ -224,6 +224,7 @@ private constructor(
val borderWidth: Float
get() = borderWidthPx * animationProgress
val alignmentAdjustment: Float
// Outset the border by half the width to create an outwards-growth animation
get() = -borderWidth / 2f + alignmentAdjustmentInset
@@ -31,9 +31,11 @@ import static com.android.app.animation.Interpolators.FINAL_FRAME;
import static com.android.app.animation.Interpolators.LINEAR;
import static com.android.app.animation.Interpolators.OVERSHOOT_0_75;
import static com.android.app.animation.Interpolators.clampToProgress;
import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
import static com.android.launcher3.AbstractFloatingView.TYPE_TASK_MENU;
import static com.android.launcher3.AbstractFloatingView.getTopOpenViewWithType;
import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
import static com.android.launcher3.Flags.enableAdditionalHomeAnimations;
import static com.android.launcher3.Flags.enableGridOnlyOverview;
import static com.android.launcher3.Flags.enableRefactorTaskThumbnail;
import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS;
@@ -130,6 +132,7 @@ import androidx.annotation.UiThread;
import androidx.core.graphics.ColorUtils;
import com.android.internal.jank.Cuj;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseActivity.MultiWindowModeChangedListener;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Flags;
@@ -2688,6 +2691,7 @@ public abstract class RecentsView<CONTAINER_TYPE extends Context & RecentsViewCo
}
private void animateRotation(int newRotation) {
AbstractFloatingView.closeAllOpenViewsExcept(mContainer, false, TYPE_REBIND_SAFE);
AnimatorSet pa = setRecentsChangedOrientation(true);
pa.addListener(AnimatorListeners.forSuccessCallback(() -> {
setLayoutRotation(newRotation, mOrientationState.getDisplayRotation());
@@ -3801,7 +3805,7 @@ public abstract class RecentsView<CONTAINER_TYPE extends Context & RecentsViewCo
anim.setFloat(taskView, taskView.getSecondaryDismissTranslationProperty(),
secondaryTranslation, clampToProgress(LINEAR, animationStartProgress,
dismissTranslationInterpolationEnd));
anim.setFloat(taskView, TaskView.SCALE_AND_DIM_OUT, 0f,
anim.add(taskView.getFocusTransitionScaleAndDimOutAnimator(),
clampToProgress(LINEAR, 0f, ANIMATION_DISMISS_PROGRESS_MIDPOINT));
} else {
float primaryTranslation =
@@ -4520,6 +4524,9 @@ public abstract class RecentsView<CONTAINER_TYPE extends Context & RecentsViewCo
* than the running task, when updating page offsets.
*/
public void setOffsetMidpointIndexOverride(int offsetMidpointIndexOverride) {
if (!enableAdditionalHomeAnimations()) {
return;
}
mOffsetMidpointIndexOverride = offsetMidpointIndexOverride;
updatePageOffsets();
}
@@ -54,6 +54,7 @@ import com.android.launcher3.Flags.privateSpaceRestrictAccessibilityDrag
import com.android.launcher3.LauncherSettings
import com.android.launcher3.R
import com.android.launcher3.Utilities
import com.android.launcher3.anim.AnimatedFloat
import com.android.launcher3.config.FeatureFlags.ENABLE_KEYBOARD_QUICK_SWITCH
import com.android.launcher3.logging.StatsLogManager.LauncherEvent
import com.android.launcher3.model.data.ItemInfo
@@ -438,17 +439,17 @@ constructor(
focusTransitionPropertyFactory.get(FOCUS_TRANSITION_INDEX_FULLSCREEN)
private val focusTransitionScaleAndDim =
focusTransitionPropertyFactory.get(FOCUS_TRANSITION_INDEX_SCALE_AND_DIM)
/**
* Variant of [focusTransitionScaleAndDim] that has a built-in interpolator, to be used with
* [com.android.launcher3.anim.PendingAnimation] via [SCALE_AND_DIM_OUT] only. PendingAnimation
* doesn't support interpolator per animation, so we'll have to interpolate inside the property.
* Returns an animator of [focusTransitionScaleAndDim] that transition out with a built-in
* interpolator.
*/
private var focusTransitionScaleAndDimOut = focusTransitionScaleAndDim.value
set(value) {
field = value
focusTransitionScaleAndDim.value =
FOCUS_TRANSITION_FAST_OUT_INTERPOLATOR.getInterpolation(field)
}
fun getFocusTransitionScaleAndDimOutAnimator(): ObjectAnimator =
AnimatedFloat { v ->
focusTransitionScaleAndDim.value =
FOCUS_TRANSITION_FAST_OUT_INTERPOLATOR.getInterpolation(v)
}
.animateToValue(1f, 0f)
private var iconAndDimAnimator: ObjectAnimator? = null
// The current background requests to load the task thumbnail and icon
@@ -1700,16 +1701,6 @@ constructor(
override fun get(taskView: TaskView) = taskView.focusTransitionProgress
}
@JvmField
val SCALE_AND_DIM_OUT: FloatProperty<TaskView> =
object : FloatProperty<TaskView>("scaleAndDimFastOut") {
override fun setValue(taskView: TaskView, v: Float) {
taskView.focusTransitionScaleAndDimOut = v
}
override fun get(taskView: TaskView) = taskView.focusTransitionScaleAndDimOut
}
private val SPLIT_SELECT_TRANSLATION_X: FloatProperty<TaskView> =
object : FloatProperty<TaskView>("splitSelectTranslationX") {
override fun setValue(taskView: TaskView, v: Float) {
@@ -305,9 +305,7 @@ public class AllAppsRecyclerView extends FastScrollRecyclerView {
@Override
public int getScrollBarTop() {
return ActivityContext.lookupContext(getContext()).getAppsView().isSearchSupported()
? getResources().getDimensionPixelOffset(R.dimen.all_apps_header_top_padding)
: 0;
return getResources().getDimensionPixelOffset(R.dimen.all_apps_header_top_padding);
}
@Override
@@ -20,6 +20,8 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.util.FloatProperty;
import java.util.function.Consumer;
/**
* A mutable float which allows animating the value
*/
@@ -38,9 +40,9 @@ public class AnimatedFloat {
}
};
private static final Runnable NO_OP = () -> { };
private static final Consumer<Float> NO_OP = t -> { };
private final Runnable mUpdateCallback;
private final Consumer<Float> mUpdateCallback;
private ObjectAnimator mValueAnimator;
// Only non-null when an animation is playing to this value.
private Float mEndValue;
@@ -52,6 +54,10 @@ public class AnimatedFloat {
}
public AnimatedFloat(Runnable updateCallback) {
this(v -> updateCallback.run());
}
public AnimatedFloat(Consumer<Float> updateCallback) {
mUpdateCallback = updateCallback;
}
@@ -60,6 +66,11 @@ public class AnimatedFloat {
value = initialValue;
}
public AnimatedFloat(Consumer<Float> updateCallback, float initialValue) {
this(updateCallback);
value = initialValue;
}
/**
* Returns an animation from the current value to the given value.
*/
@@ -99,7 +110,7 @@ public class AnimatedFloat {
public void updateValue(float v) {
if (Float.compare(v, value) != 0) {
value = v;
mUpdateCallback.run();
mUpdateCallback.accept(value);
}
}
@@ -59,6 +59,13 @@ public class PendingAnimation extends AnimatedPropertySetter {
add(anim, springProperty);
}
/**
* Utility method to sent an interpolator on an animation and add it to the list
*/
public void add(Animator anim, TimeInterpolator interpolator) {
add(anim, interpolator, SpringProperty.DEFAULT);
}
@Override
public void add(Animator anim) {
add(anim, SpringProperty.DEFAULT);
@@ -209,7 +209,7 @@ public class LoaderTask implements Runnable {
mApp.getContext().getContentResolver(),
"launcher_broadcast_installed_apps",
/* def= */ 0);
if (launcherBroadcastInstalledApps == 1) {
if (launcherBroadcastInstalledApps == 1 && mIsRestoreFromBackup) {
List<FirstScreenBroadcastModel> broadcastModels =
FirstScreenBroadcastHelper.createModelsForFirstScreenBroadcast(
mPmHelper,
@@ -16,6 +16,7 @@
package com.android.launcher3.views;
import static com.android.app.animation.Interpolators.LINEAR;
import static com.android.launcher3.Flags.enableAdditionalHomeAnimations;
import static com.android.launcher3.Utilities.boundToRange;
import static com.android.launcher3.Utilities.mapToRange;
import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
@@ -97,6 +98,9 @@ public class ClipIconView extends View implements ClipPathView {
* within the clip bounds of this view.
*/
public void setTaskViewArtist(TaskViewArtist taskViewArtist) {
if (!enableAdditionalHomeAnimations()) {
return;
}
mTaskViewArtist = taskViewArtist;
invalidate();
}
@@ -18,6 +18,7 @@ package com.android.launcher3.views;
import static android.view.Gravity.LEFT;
import static com.android.app.animation.Interpolators.LINEAR;
import static com.android.launcher3.Flags.enableAdditionalHomeAnimations;
import static com.android.launcher3.Utilities.getFullDrawable;
import static com.android.launcher3.Utilities.mapToRange;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
@@ -164,7 +165,12 @@ public class FloatingIconView extends FrameLayout implements
*/
public void update(float alpha, RectF rect, float progress, float shapeProgressStart,
float cornerRadius, boolean isOpening, int taskViewDrawAlpha) {
setAlpha(isLaidOut() ? alpha : 0f);
// The non-running task home animation has some very funky first few frames because this
// FIV hasn't fully laid out. During those frames, hide this FIV and continue drawing the
// TaskView directly while transforming it in the place of this FIV. However, if we fade
// the TaskView at all, we need to display this FIV regardless.
setAlpha(!enableAdditionalHomeAnimations() || isLaidOut() || taskViewDrawAlpha < 255
? alpha : 0f);
mClipIconView.update(rect, progress, shapeProgressStart, cornerRadius, isOpening, this,
mLauncher.getDeviceProfile(), taskViewDrawAlpha);
@@ -1,10 +1,13 @@
package com.android.launcher3.model
import android.appwidget.AppWidgetManager
import android.content.Intent
import android.os.UserHandle
import android.platform.test.flag.junit.SetFlagsRule
import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.launcher3.Flags
import com.android.launcher3.InvariantDeviceProfile
import com.android.launcher3.LauncherAppState
@@ -14,6 +17,7 @@ import com.android.launcher3.icons.IconCache
import com.android.launcher3.icons.cache.CachingLogic
import com.android.launcher3.icons.cache.IconCacheUpdateHandler
import com.android.launcher3.pm.UserCache
import com.android.launcher3.provider.RestoreDbTask
import com.android.launcher3.ui.TestViewHelpers
import com.android.launcher3.util.Executors.MODEL_EXECUTOR
import com.android.launcher3.util.LauncherModelHelper.SandboxModelContext
@@ -21,21 +25,30 @@ import com.android.launcher3.util.LooperIdleLock
import com.android.launcher3.util.UserIconInfo
import com.google.common.truth.Truth
import java.util.concurrent.CountDownLatch
import junit.framework.Assert.assertEquals
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyList
import org.mockito.ArgumentMatchers.anyMap
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
import org.mockito.MockitoSession
import org.mockito.Spy
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.spy
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import org.mockito.quality.Strictness
private const val INSERTION_STATEMENT_FILE = "databases/workspace_items.sql"
@@ -43,6 +56,20 @@ private const val INSERTION_STATEMENT_FILE = "databases/workspace_items.sql"
@RunWith(AndroidJUnit4::class)
class LoaderTaskTest {
private var context = SandboxModelContext()
private val expectedBroadcastModel =
FirstScreenBroadcastModel(
installerPackage = "installerPackage",
pendingCollectionItems = mutableSetOf("pendingCollectionItem"),
pendingWidgetItems = mutableSetOf("pendingWidgetItem"),
pendingHotseatItems = mutableSetOf("pendingHotseatItem"),
pendingWorkspaceItems = mutableSetOf("pendingWorkspaceItem"),
installedHotseatItems = mutableSetOf("installedHotseatItem"),
installedWorkspaceItems = mutableSetOf("installedWorkspaceItem"),
firstScreenInstalledWidgets = mutableSetOf("installedFirstScreenWidget"),
secondaryScreenInstalledWidgets = mutableSetOf("installedSecondaryScreenWidget")
)
private lateinit var mockitoSession: MockitoSession
@Mock private lateinit var app: LauncherAppState
@Mock private lateinit var bgAllAppsList: AllAppsList
@Mock private lateinit var modelDelegate: ModelDelegate
@@ -61,7 +88,11 @@ class LoaderTaskTest {
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
mockitoSession =
ExtendedMockito.mockitoSession()
.strictness(Strictness.LENIENT)
.mockStatic(FirstScreenBroadcastHelper::class.java)
.startMocking()
val idp =
InvariantDeviceProfile().apply {
numRows = 5
@@ -90,6 +121,7 @@ class LoaderTaskTest {
@After
fun tearDown() {
context.onDestroy()
mockitoSession.finishMocking()
}
@Test
@@ -166,6 +198,141 @@ class LoaderTaskTest {
verify(bgAllAppsList, Mockito.never())
.setFlags(BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED, true)
}
@Test
fun `When launcher_broadcast_installed_apps and is restore then send installed item broadcast`() {
// Given
val spyContext = spy(context)
`when`(app.context).thenReturn(spyContext)
whenever(
FirstScreenBroadcastHelper.createModelsForFirstScreenBroadcast(
anyOrNull(),
anyList(),
anyMap(),
anyList()
)
)
.thenReturn(listOf(expectedBroadcastModel))
whenever(
FirstScreenBroadcastHelper.sendBroadcastsForModels(
spyContext,
listOf(expectedBroadcastModel)
)
)
.thenCallRealMethod()
Settings.Secure.putInt(spyContext.contentResolver, "launcher_broadcast_installed_apps", 1)
RestoreDbTask.setPending(spyContext)
// When
LoaderTask(app, bgAllAppsList, BgDataModel(), modelDelegate, launcherBinder)
.runSyncOnBackgroundThread()
// Then
val argumentCaptor = ArgumentCaptor.forClass(Intent::class.java)
verify(spyContext).sendBroadcast(argumentCaptor.capture())
val actualBroadcastIntent = argumentCaptor.value
assertEquals(expectedBroadcastModel.installerPackage, actualBroadcastIntent.`package`)
assertEquals(
ArrayList(expectedBroadcastModel.installedWorkspaceItems),
actualBroadcastIntent.getStringArrayListExtra("workspaceInstalledItems")
)
assertEquals(
ArrayList(expectedBroadcastModel.installedHotseatItems),
actualBroadcastIntent.getStringArrayListExtra("hotseatInstalledItems")
)
assertEquals(
ArrayList(
expectedBroadcastModel.firstScreenInstalledWidgets +
expectedBroadcastModel.secondaryScreenInstalledWidgets
),
actualBroadcastIntent.getStringArrayListExtra("widgetInstalledItems")
)
assertEquals(
ArrayList(expectedBroadcastModel.pendingCollectionItems),
actualBroadcastIntent.getStringArrayListExtra("folderItem")
)
assertEquals(
ArrayList(expectedBroadcastModel.pendingWorkspaceItems),
actualBroadcastIntent.getStringArrayListExtra("workspaceItem")
)
assertEquals(
ArrayList(expectedBroadcastModel.pendingHotseatItems),
actualBroadcastIntent.getStringArrayListExtra("hotseatItem")
)
assertEquals(
ArrayList(expectedBroadcastModel.pendingWidgetItems),
actualBroadcastIntent.getStringArrayListExtra("widgetItem")
)
}
@Test
fun `When not a restore then installed item broadcast not sent`() {
// Given
val spyContext = spy(context)
`when`(app.context).thenReturn(spyContext)
whenever(
FirstScreenBroadcastHelper.createModelsForFirstScreenBroadcast(
anyOrNull(),
anyList(),
anyMap(),
anyList()
)
)
.thenReturn(listOf(expectedBroadcastModel))
whenever(
FirstScreenBroadcastHelper.sendBroadcastsForModels(
spyContext,
listOf(expectedBroadcastModel)
)
)
.thenCallRealMethod()
Settings.Secure.putInt(spyContext.contentResolver, "launcher_broadcast_installed_apps", 1)
// When
LoaderTask(app, bgAllAppsList, BgDataModel(), modelDelegate, launcherBinder)
.runSyncOnBackgroundThread()
// Then
verify(spyContext, times(0)).sendBroadcast(any(Intent::class.java))
}
@Test
fun `When launcher_broadcast_installed_apps false then installed item broadcast not sent`() {
// Given
val spyContext = spy(context)
`when`(app.context).thenReturn(spyContext)
whenever(
FirstScreenBroadcastHelper.createModelsForFirstScreenBroadcast(
anyOrNull(),
anyList(),
anyMap(),
anyList()
)
)
.thenReturn(listOf(expectedBroadcastModel))
whenever(
FirstScreenBroadcastHelper.sendBroadcastsForModels(
spyContext,
listOf(expectedBroadcastModel)
)
)
.thenCallRealMethod()
Settings.Secure.putInt(spyContext.contentResolver, "launcher_broadcast_installed_apps", 0)
RestoreDbTask.setPending(spyContext)
// When
LoaderTask(app, bgAllAppsList, BgDataModel(), modelDelegate, launcherBinder)
.runSyncOnBackgroundThread()
// Then
verify(spyContext, times(0)).sendBroadcast(any(Intent::class.java))
}
}
private fun LoaderTask.runSyncOnBackgroundThread() {