Snap for 11992154 from 622a7e37c5 to 24Q4-release
Change-Id: I765e9a3b2d6ec681c3501fd8d56222b9d12f5fbc
This commit is contained in:
@@ -17,7 +17,6 @@ package com.android.launcher3.uioverrides;
|
||||
|
||||
import static com.android.launcher3.BuildConfig.WIDGETS_ENABLED;
|
||||
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
|
||||
|
||||
import android.appwidget.AppWidgetHost;
|
||||
import android.appwidget.AppWidgetHostView;
|
||||
@@ -100,7 +99,7 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder {
|
||||
// for concurrent modification.
|
||||
new ArrayList<>(h.mProviderChangedListeners).forEach(
|
||||
ProviderChangedListener::notifyWidgetProvidersChanged))),
|
||||
UI_HELPER_EXECUTOR.getLooper());
|
||||
getWidgetHolderExecutor().getLooper());
|
||||
if (WIDGETS_ENABLED) {
|
||||
sWidgetHost.startListening();
|
||||
}
|
||||
@@ -199,8 +198,10 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder {
|
||||
return;
|
||||
}
|
||||
|
||||
sWidgetHost.setAppWidgetHidden();
|
||||
setListeningFlag(false);
|
||||
getWidgetHolderExecutor().execute(() -> {
|
||||
sWidgetHost.setAppWidgetHidden();
|
||||
setListeningFlag(false);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2797,9 +2797,11 @@ public class Launcher extends StatefulActivity<LauncherState>
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -20,6 +20,7 @@ import static android.app.Activity.RESULT_CANCELED;
|
||||
import static com.android.launcher3.BuildConfig.WIDGETS_ENABLED;
|
||||
import static com.android.launcher3.Flags.enableWorkspaceInflation;
|
||||
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
|
||||
import static com.android.launcher3.widget.LauncherAppWidgetProviderInfo.fromProviderInfo;
|
||||
|
||||
import android.appwidget.AppWidgetHost;
|
||||
@@ -36,6 +37,7 @@ import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.WorkerThread;
|
||||
|
||||
import com.android.launcher3.BaseActivity;
|
||||
import com.android.launcher3.BaseDraggingActivity;
|
||||
@@ -44,6 +46,7 @@ import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.testing.TestLogging;
|
||||
import com.android.launcher3.testing.shared.TestProtocol;
|
||||
import com.android.launcher3.util.LooperExecutor;
|
||||
import com.android.launcher3.util.ResourceBasedOverride;
|
||||
import com.android.launcher3.util.SafeCloseable;
|
||||
import com.android.launcher3.widget.LauncherAppWidgetHost.ListenableHostView;
|
||||
@@ -51,6 +54,7 @@ import com.android.launcher3.widget.custom.CustomWidgetManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.IntConsumer;
|
||||
|
||||
/**
|
||||
@@ -77,7 +81,7 @@ public class LauncherWidgetHolder {
|
||||
protected final SparseArray<LauncherAppWidgetHostView> mViews = new SparseArray<>();
|
||||
protected final List<ProviderChangedListener> mProviderChangedListeners = new ArrayList<>();
|
||||
|
||||
protected int mFlags = FLAG_STATE_IS_NORMAL;
|
||||
protected AtomicInteger mFlags = new AtomicInteger(FLAG_STATE_IS_NORMAL);
|
||||
|
||||
// TODO(b/191735836): Replace with ActivityOptions.KEY_SPLASH_SCREEN_STYLE when un-hidden
|
||||
private static final String KEY_SPLASH_SCREEN_STYLE = "android.activity.splashScreenStyle";
|
||||
@@ -96,6 +100,10 @@ public class LauncherWidgetHolder {
|
||||
context, appWidgetRemovedCallback, mProviderChangedListeners);
|
||||
}
|
||||
|
||||
protected LooperExecutor getWidgetHolderExecutor() {
|
||||
return UI_HELPER_EXECUTOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts listening to the widget updates from the server side
|
||||
*/
|
||||
@@ -104,21 +112,23 @@ public class LauncherWidgetHolder {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
mWidgetHost.startListening();
|
||||
} catch (Exception e) {
|
||||
if (!Utilities.isBinderSizeError(e)) {
|
||||
throw new RuntimeException(e);
|
||||
getWidgetHolderExecutor().execute(() -> {
|
||||
try {
|
||||
mWidgetHost.startListening();
|
||||
} catch (Exception e) {
|
||||
if (!Utilities.isBinderSizeError(e)) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
// We're willing to let this slide. The exception is being caused by the list of
|
||||
// RemoteViews which is being passed back. The startListening relationship will
|
||||
// have been established by this point, and we will end up populating the
|
||||
// widgets upon bind anyway. See issue 14255011 for more context.
|
||||
}
|
||||
// We're willing to let this slide. The exception is being caused by the list of
|
||||
// RemoteViews which is being passed back. The startListening relationship will
|
||||
// have been established by this point, and we will end up populating the
|
||||
// widgets upon bind anyway. See issue 14255011 for more context.
|
||||
}
|
||||
// TODO: Investigate why widgetHost.startListening() always return non-empty updates
|
||||
setListeningFlag(true);
|
||||
// TODO: Investigate why widgetHost.startListening() always return non-empty updates
|
||||
setListeningFlag(true);
|
||||
|
||||
updateDeferredView();
|
||||
MAIN_EXECUTOR.execute(() -> updateDeferredView());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -282,16 +292,23 @@ public class LauncherWidgetHolder {
|
||||
if (!WIDGETS_ENABLED) {
|
||||
return;
|
||||
}
|
||||
mWidgetHost.stopListening();
|
||||
setListeningFlag(false);
|
||||
getWidgetHolderExecutor().execute(() -> {
|
||||
mWidgetHost.stopListening();
|
||||
setListeningFlag(false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update {@link FLAG_LISTENING} on {@link mFlags} after making binder calls from
|
||||
* {@link sWidgetHost}.
|
||||
*/
|
||||
@WorkerThread
|
||||
protected void setListeningFlag(final boolean isListening) {
|
||||
if (isListening) {
|
||||
mFlags |= FLAG_LISTENING;
|
||||
mFlags.updateAndGet(old -> old | FLAG_LISTENING);
|
||||
return;
|
||||
}
|
||||
mFlags &= ~FLAG_LISTENING;
|
||||
mFlags.updateAndGet(old -> old & ~FLAG_LISTENING);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -373,7 +390,7 @@ public class LauncherWidgetHolder {
|
||||
* as a result of using the same flow.
|
||||
*/
|
||||
protected LauncherAppWidgetHostView recycleExistingView(LauncherAppWidgetHostView view) {
|
||||
if ((mFlags & FLAG_LISTENING) == 0) {
|
||||
if ((mFlags.get() & FLAG_LISTENING) == 0) {
|
||||
if (view instanceof PendingAppWidgetHostView pv && pv.isDeferredWidget()) {
|
||||
return view;
|
||||
} else {
|
||||
@@ -395,7 +412,7 @@ public class LauncherWidgetHolder {
|
||||
@NonNull
|
||||
protected LauncherAppWidgetHostView createViewInternal(
|
||||
int appWidgetId, @NonNull LauncherAppWidgetProviderInfo appWidget) {
|
||||
if ((mFlags & FLAG_LISTENING) == 0) {
|
||||
if ((mFlags.get() & FLAG_LISTENING) == 0) {
|
||||
// Since the launcher hasn't started listening to widget updates, we can't simply call
|
||||
// host.createView here because the later will make a binder call to retrieve
|
||||
// RemoteViews from system process.
|
||||
@@ -460,7 +477,7 @@ public class LauncherWidgetHolder {
|
||||
* @return True if the host is listening to the updates, false otherwise
|
||||
*/
|
||||
public boolean isListening() {
|
||||
return (mFlags & FLAG_LISTENING) != 0;
|
||||
return (mFlags.get() & FLAG_LISTENING) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -469,16 +486,17 @@ public class LauncherWidgetHolder {
|
||||
*/
|
||||
private void setShouldListenFlag(int flag, boolean on) {
|
||||
if (on) {
|
||||
mFlags |= flag;
|
||||
mFlags.updateAndGet(old -> old | flag);
|
||||
} else {
|
||||
mFlags &= ~flag;
|
||||
mFlags.updateAndGet(old -> old & ~flag);
|
||||
}
|
||||
|
||||
final boolean listening = isListening();
|
||||
if (!listening && shouldListen(mFlags)) {
|
||||
int currentFlag = mFlags.get();
|
||||
if (!listening && shouldListen(currentFlag)) {
|
||||
// Postpone starting listening until all flags are on.
|
||||
startListening();
|
||||
} else if (listening && (mFlags & FLAG_ACTIVITY_STARTED) == 0) {
|
||||
} else if (listening && (currentFlag & FLAG_ACTIVITY_STARTED) == 0) {
|
||||
// Postpone stopping listening until the activity is stopped.
|
||||
stopListening();
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user