Merge "[Launcher Jank] Avoid LauncherWidgetHolder and QuickstepWidgetHolder calling IAppWidgetService$Stub$Proxy.setAppWidgetHidden from main thread" into main
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
index 747612d..4c24d95 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
@@ -19,6 +19,7 @@
import static com.android.app.animation.Interpolators.LINEAR;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_DEPTH;
import static com.android.launcher3.states.StateAnimationConfig.SKIP_DEPTH_CONTROLLER;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
import android.animation.Animator;
@@ -74,8 +75,9 @@
mOnAttachListener = new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View view) {
- CrossWindowBlurListeners.getInstance().addListener(mLauncher.getMainExecutor(),
- mCrossWindowBlurListener);
+ UI_HELPER_EXECUTOR.execute(() ->
+ CrossWindowBlurListeners.getInstance().addListener(
+ mLauncher.getMainExecutor(), mCrossWindowBlurListener));
mLauncher.getScrimView().addOpaquenessListener(mOpaquenessListener);
// To handle the case where window token is invalid during last setDepth call.
@@ -108,7 +110,9 @@
private void removeSecondaryListeners() {
if (mCrossWindowBlurListener != null) {
- CrossWindowBlurListeners.getInstance().removeListener(mCrossWindowBlurListener);
+ UI_HELPER_EXECUTOR.execute(() ->
+ CrossWindowBlurListeners.getInstance()
+ .removeListener(mCrossWindowBlurListener));
}
if (mOpaquenessListener != null) {
mLauncher.getScrimView().removeOpaquenessListener(mOpaquenessListener);
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 4e566ab..d905801 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -2797,9 +2797,11 @@
}
private void updateDisallowBack() {
- if (BuildCompat.isAtLeastV() && Flags.enableDesktopWindowingMode()
- && mDeviceProfile.isTablet) {
- // TODO(b/330183377) disable back in launcher when when we productionize
+ if (BuildCompat.isAtLeastV()
+ && Flags.enableDesktopWindowingMode()
+ && !Flags.enableDesktopWindowingWallpaperActivity()
+ && mDeviceProfile.isTablet) {
+ // TODO(b/333533253): Clean up after desktop wallpaper activity flag is rolled out
return;
}
LauncherRootView rv = getRootView();
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index dc6968c..312c6f4 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -209,7 +209,7 @@
mApp.getContext().getContentResolver(),
"launcher_broadcast_installed_apps",
/* def= */ 0);
- if (launcherBroadcastInstalledApps == 1) {
+ if (launcherBroadcastInstalledApps == 1 && mIsRestoreFromBackup) {
List<FirstScreenBroadcastModel> broadcastModels =
FirstScreenBroadcastHelper.createModelsForFirstScreenBroadcast(
mPmHelper,
diff --git a/tests/src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java b/tests/src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java
index 362596c..405dae7 100644
--- a/tests/src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java
+++ b/tests/src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java
@@ -159,13 +159,13 @@
mLauncher.getWorkspace().getWorkspaceIconsPositions();
assertThat(initialPositions.keySet()).containsAtLeastElementsIn(appNames);
- mLauncher.getWorkspace().getWorkspaceAppIcon(DUMMY_APP_NAME).uninstall();
- mLauncher.getWorkspace().verifyWorkspaceAppIconIsGone(
+ final Workspace workspace = mLauncher.getWorkspace().getWorkspaceAppIcon(
+ DUMMY_APP_NAME).uninstall();
+ workspace.verifyWorkspaceAppIconIsGone(
DUMMY_APP_NAME + " was expected to disappear after uninstall.", DUMMY_APP_NAME);
Log.d(UIOBJECT_STALE_ELEMENT, "second getWorkspaceIconsPositions()");
- Map<String, Point> finalPositions =
- mLauncher.getWorkspace().getWorkspaceIconsPositions();
+ Map<String, Point> finalPositions = workspace.getWorkspaceIconsPositions();
assertThat(finalPositions).doesNotContainKey(DUMMY_APP_NAME);
} finally {
TestUtil.uninstallDummyApp();
diff --git a/tests/src/com/android/launcher3/model/LoaderTaskTest.kt b/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
index 28a001f..d16674c 100644
--- a/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
+++ b/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
@@ -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.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.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 @@
@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 @@
@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 @@
@After
fun tearDown() {
context.onDestroy()
+ mockitoSession.finishMocking()
}
@Test
@@ -166,6 +198,141 @@
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() {