Add a resource override for providing default widgets filter.

The implementation in this CL is no-op i.e. all widgets will be shown

Bug: 356127021
Flag: com.android.launcher3.enable_tiered_widgets_by_default_in_picker
Test: Verified with other child cls
Change-Id: I0a6f8973dc8ec58fd87d29b3f291b5e290ea988d
This commit is contained in:
Shamali P
2024-11-04 21:35:38 +00:00
parent 4f793a31d8
commit ea078cb647
15 changed files with 318 additions and 37 deletions
@@ -23,6 +23,8 @@ import static android.view.WindowInsets.Type.statusBars;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import static java.util.Collections.emptyList;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ClipData;
@@ -44,6 +46,7 @@ import com.android.launcher3.dragndrop.SimpleDragLayer;
import com.android.launcher3.model.StringCache;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.model.WidgetPredictionsRequester;
import com.android.launcher3.model.WidgetsFilterDataProvider;
import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.PackageItemInfo;
@@ -112,6 +115,7 @@ public class WidgetPickerActivity extends BaseActivity {
private WidgetPredictionsRequester mWidgetPredictionsRequester;
private final WidgetPickerDataProvider mWidgetPickerDataProvider =
new WidgetPickerDataProvider();
private WidgetsFilterDataProvider mWidgetsFilterDataProvider;
private int mDesiredWidgetWidth;
private int mDesiredWidgetHeight;
@@ -133,13 +137,13 @@ public class WidgetPickerActivity extends BaseActivity {
@Nullable
private WidgetsFullSheet mWidgetSheet;
private final Predicate<WidgetItem> mWidgetsFilter = widget -> {
private final Predicate<WidgetItem> mNoShortcutsFilter = widget -> {
final WidgetAcceptabilityVerdict verdict =
isWidgetAcceptable(widget, /* applySizeFilter=*/ false);
verdict.maybeLogVerdict();
return verdict.isAcceptable;
};
private final Predicate<WidgetItem> mDefaultWidgetsFilter = widget -> {
private final Predicate<WidgetItem> mHostSizeAndNoShortcutsFilter = widget -> {
final WidgetAcceptabilityVerdict verdict =
isWidgetAcceptable(widget, /* applySizeFilter=*/ true);
verdict.maybeLogVerdict();
@@ -157,6 +161,7 @@ public class WidgetPickerActivity extends BaseActivity {
InvariantDeviceProfile idp = mApp.getInvariantDeviceProfile();
mDeviceProfile = idp.getDeviceProfile(this);
mModel = new WidgetsModel();
mWidgetsFilterDataProvider = WidgetsFilterDataProvider.Companion.newInstance(this);
setContentView(R.layout.widget_picker_activity);
mDragLayer = findViewById(R.id.drag_layer);
@@ -288,13 +293,16 @@ public class WidgetPickerActivity extends BaseActivity {
private void refreshAndBindWidgets() {
MODEL_EXECUTOR.execute(() -> {
LauncherAppState app = LauncherAppState.getInstance(this);
// Don't have to setup filters - its setup when launcher loads
// Just refresh filters with available cached info.
mModel.updateWidgetFilters(mWidgetsFilterDataProvider);
mModel.update(app, null);
StringCache stringCache = new StringCache();
stringCache.loadStrings(this);
bindStringCache(stringCache);
bindWidgets(mModel.getWidgetsByPackageItem());
bindWidgets(mModel.getWidgetsByPackageItem(), mModel.getDefaultWidgetsFilter());
// Open sheet once widgets are available, so that it doesn't interrupt the open
// animation.
openWidgetsSheet();
@@ -310,14 +318,23 @@ public class WidgetPickerActivity extends BaseActivity {
MAIN_EXECUTOR.execute(() -> mStringCache = stringCache);
}
private void bindWidgets(Map<PackageItemInfo, List<WidgetItem>> widgets) {
private void bindWidgets(Map<PackageItemInfo, List<WidgetItem>> widgets,
@Nullable Predicate<WidgetItem> defaultWidgetsFilter) {
WidgetsListBaseEntriesBuilder builder = new WidgetsListBaseEntriesBuilder(
mApp.getContext());
final List<WidgetsListBaseEntry> allWidgets = builder.build(widgets, mWidgetsFilter);
final List<WidgetsListBaseEntry> defaultWidgets =
shouldShowDefaultWidgets() ? builder.build(widgets,
mDefaultWidgetsFilter) : List.of();
final List<WidgetsListBaseEntry> allWidgets = builder.build(widgets, mNoShortcutsFilter);
// Default list is shown if either defaultWidgetsFilter exists or host has additionally
// enforced size filtering.
@Nullable Predicate<WidgetItem> defaultListFilter =
hasHostSizeFilters() ? mHostSizeAndNoShortcutsFilter : null;
if (defaultWidgetsFilter != null) {
defaultListFilter = defaultListFilter != null ? defaultListFilter.and(
defaultWidgetsFilter) : defaultWidgetsFilter;
}
final List<WidgetsListBaseEntry> defaultWidgets = defaultListFilter != null ? builder.build(
widgets, defaultListFilter) : emptyList();
MAIN_EXECUTOR.execute(
() -> mWidgetPickerDataProvider.setWidgets(allWidgets, defaultWidgets));
@@ -342,6 +359,7 @@ public class WidgetPickerActivity extends BaseActivity {
@Override
protected void onDestroy() {
super.onDestroy();
MODEL_EXECUTOR.execute(() -> mWidgetsFilterDataProvider.destroy());
if (mWidgetPredictionsRequester != null) {
mWidgetPredictionsRequester.clear();
}
@@ -398,7 +416,7 @@ public class WidgetPickerActivity extends BaseActivity {
}
}
private boolean shouldShowDefaultWidgets() {
private boolean hasHostSizeFilters() {
// If optional filters such as size filter are present, we display them as default widgets.
return mDesiredWidgetWidth != 0 || mDesiredWidgetHeight != 0;
}
+3
View File
@@ -85,6 +85,9 @@
<string name="local_colors_extraction_class" translatable="false"></string>
<string name="search_session_manager_class" translatable="false"></string>
<!-- Filters for widgets displayed in the widget picker -->
<string name="widgets_filter_data_provider_class" translatable="false"></string>
<!-- Scalable Grid configuration -->
<!-- This is a float because it is converted to dp later in DeviceProfile -->
<dimen name="hotseat_bar_bottom_space_default">48</dimen>
+3 -2
View File
@@ -2629,8 +2629,9 @@ public class Launcher extends StatefulActivity<LauncherState>
* See {@code LauncherBindingDelegate}
*/
@Override
public void bindAllWidgets(final List<WidgetsListBaseEntry> allWidgets) {
mModelCallbacks.bindAllWidgets(allWidgets);
public void bindAllWidgets(@NonNull final List<WidgetsListBaseEntry> allWidgets,
@NonNull final List<WidgetsListBaseEntry> defaultWidgets) {
mModelCallbacks.bindAllWidgets(allWidgets, defaultWidgets);
}
@Override
@@ -48,6 +48,7 @@ import com.android.launcher3.icons.IconProvider;
import com.android.launcher3.icons.LauncherIconProvider;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.model.ModelLauncherCallbacks;
import com.android.launcher3.model.WidgetsFilterDataProvider;
import com.android.launcher3.notification.NotificationListener;
import com.android.launcher3.pm.InstallSessionHelper;
import com.android.launcher3.pm.InstallSessionTracker;
@@ -197,7 +198,8 @@ public class LauncherAppState implements SafeCloseable {
mIconProvider = new LauncherIconProvider(context);
mIconCache = new IconCache(mContext, mInvariantDeviceProfile,
iconCacheFileName, mIconProvider);
mModel = new LauncherModel(context, this, mIconCache, new AppFilter(mContext),
mModel = new LauncherModel(context, this, mIconCache,
WidgetsFilterDataProvider.Companion.newInstance(context), new AppFilter(mContext),
PackageManagerHelper.INSTANCE.get(context), iconCacheFileName != null);
mOnTerminateCallback.add(mIconCache::close);
mOnTerminateCallback.add(mModel::destroy);
+20 -1
View File
@@ -42,6 +42,7 @@ import com.android.launcher3.model.PackageUpdatedTask
import com.android.launcher3.model.ReloadStringCacheTask
import com.android.launcher3.model.ShortcutsChangedTask
import com.android.launcher3.model.UserLockStateChangedTask
import com.android.launcher3.model.WidgetsFilterDataProvider
import com.android.launcher3.model.data.ItemInfo
import com.android.launcher3.model.data.WorkspaceItemInfo
import com.android.launcher3.pm.UserCache
@@ -66,6 +67,7 @@ class LauncherModel(
private val context: Context,
private val mApp: LauncherAppState,
private val iconCache: IconCache,
private val widgetsFilterDataProvider: WidgetsFilterDataProvider,
appFilter: AppFilter,
mPmHelper: PackageManagerHelper,
isPrimaryInstance: Boolean,
@@ -140,6 +142,11 @@ class LauncherModel(
owner: BgDataModel.Callbacks?,
) = ModelWriter(mApp.context, this, mBgDataModel, verifyChanges, cellPosMapper, owner)
/** Returns the [WidgetsFilterDataProvider] that manages widget filters. */
fun getWidgetsFilterDataProvider(): WidgetsFilterDataProvider {
return widgetsFilterDataProvider
}
/** Called when the icon for an app changes, outside of package event */
@WorkerThread
fun onAppIconChanged(packageName: String, user: UserHandle) {
@@ -160,7 +167,10 @@ class LauncherModel(
/** Called when the model is destroyed */
fun destroy() {
mModelDestroyed = true
MODEL_EXECUTOR.execute(modelDelegate::destroy)
MODEL_EXECUTOR.execute {
modelDelegate.destroy()
widgetsFilterDataProvider.destroy()
}
}
fun onBroadcastIntent(intent: Intent) {
@@ -312,6 +322,7 @@ class LauncherModel(
mBgDataModel,
this.modelDelegate,
launcherBinder,
widgetsFilterDataProvider,
)
// Always post the loader task, instead of running directly
@@ -417,6 +428,14 @@ class LauncherModel(
}
}
/** Called when the widget filters are refreshed and available to bind to the model. */
fun onWidgetFiltersLoaded() {
enqueueModelUpdateTask { taskController, dataModel, _ ->
dataModel.widgetsModel.updateWidgetFilters(widgetsFilterDataProvider)
taskController.bindUpdatedWidgets(dataModel)
}
}
fun enqueueModelUpdateTask(task: ModelUpdateTask) {
if (mModelDestroyed) {
return
+5 -2
View File
@@ -252,8 +252,11 @@ class ModelCallbacks(private var launcher: Launcher) : BgDataModel.Callbacks {
PopupContainerWithArrow.dismissInvalidPopup(launcher)
}
override fun bindAllWidgets(allWidgets: List<WidgetsListBaseEntry>) {
launcher.widgetPickerDataProvider.setWidgets(allWidgets, /* defaultWidgets= */ listOf())
override fun bindAllWidgets(
allWidgets: List<WidgetsListBaseEntry>,
defaultWidgets: List<WidgetsListBaseEntry>,
) {
launcher.widgetPickerDataProvider.setWidgets(allWidgets, defaultWidgets)
}
/** Returns the ids of the workspaces to bind. */
@@ -305,7 +305,9 @@ public class PreviewSurfaceRenderer {
bgModel,
LauncherAppState.getInstance(previewContext).getModel().getModelDelegate(),
new BaseLauncherBinder(LauncherAppState.getInstance(previewContext), bgModel,
/* bgAllAppsList= */ null, new Callbacks[0])) {
/* bgAllAppsList= */ null, new Callbacks[0]),
LauncherAppState.getInstance(
previewContext).getModel().getWidgetsFilterDataProvider()) {
@Override
public void run() {
@@ -24,6 +24,8 @@ import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import static java.util.Collections.emptyList;
import android.os.Process;
import android.os.Trace;
import android.util.Log;
@@ -43,6 +45,7 @@ import com.android.launcher3.model.BgDataModel.FixedContainerItems;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.PackageItemInfo;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSet;
@@ -62,6 +65,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
@@ -162,9 +166,17 @@ public class BaseLauncherBinder {
if (!WIDGETS_ENABLED) {
return;
}
Map<PackageItemInfo, List<WidgetItem>>
widgetsByPackageItem = mBgDataModel.widgetsModel.getWidgetsByPackageItem();
List<WidgetsListBaseEntry> widgets = new WidgetsListBaseEntriesBuilder(mApp.getContext())
.build(mBgDataModel.widgetsModel.getWidgetsByPackageItem());
executeCallbacksTask(c -> c.bindAllWidgets(widgets), mUiExecutor);
.build(widgetsByPackageItem);
Predicate<WidgetItem> filter = mBgDataModel.widgetsModel.getDefaultWidgetsFilter();
List<WidgetsListBaseEntry> defaultWidgets =
filter != null ? new WidgetsListBaseEntriesBuilder(
mApp.getContext()).build(widgetsByPackageItem,
mBgDataModel.widgetsModel.getDefaultWidgetsFilter()) : emptyList();
executeCallbacksTask(c -> c.bindAllWidgets(widgets, defaultWidgets), mUiExecutor);
}
/**
@@ -537,7 +537,13 @@ public class BgDataModel {
default void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets) { }
default void bindRestoreItemsChange(HashSet<ItemInfo> updates) { }
default void bindWorkspaceComponentsRemoved(Predicate<ItemInfo> matcher) { }
default void bindAllWidgets(List<WidgetsListBaseEntry> widgets) { }
/**
* Binds the app widgets to the providers that share widgets with the UI.
*/
default void bindAllWidgets(@NonNull List<WidgetsListBaseEntry> widgets,
@NonNull List<WidgetsListBaseEntry> defaultWidgets) {
}
default void bindSmartspaceWidget() { }
/** Called when workspace has been bound. */
@@ -20,6 +20,7 @@ import static com.android.launcher3.BuildConfig.WIDGET_ON_FIRST_SCREEN;
import static com.android.launcher3.Flags.enableLauncherBrMetricsFixed;
import static com.android.launcher3.Flags.enableSmartspaceAsAWidget;
import static com.android.launcher3.Flags.enableSmartspaceRemovalToggle;
import static com.android.launcher3.Flags.enableTieredWidgetsByDefaultInPicker;
import static com.android.launcher3.LauncherPrefs.IS_FIRST_LOAD_AFTER_RESTORE;
import static com.android.launcher3.LauncherPrefs.SHOULD_SHOW_SMARTSPACE;
import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
@@ -142,6 +143,7 @@ public class LoaderTask implements Runnable {
private final UserManager mUserManager;
private final UserCache mUserCache;
private final PackageManagerHelper mPmHelper;
private final WidgetsFilterDataProvider mWidgetsFilterDataProvider;
private final InstallSessionHelper mSessionHelper;
private final IconCache mIconCache;
@@ -158,13 +160,16 @@ public class LoaderTask implements Runnable {
private String mDbName;
public LoaderTask(@NonNull LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel bgModel,
ModelDelegate modelDelegate, @NonNull BaseLauncherBinder launcherBinder) {
this(app, bgAllAppsList, bgModel, modelDelegate, launcherBinder, new UserManagerState());
ModelDelegate modelDelegate, @NonNull BaseLauncherBinder launcherBinder,
@NonNull WidgetsFilterDataProvider widgetsFilterDataProvider) {
this(app, bgAllAppsList, bgModel, modelDelegate, launcherBinder, widgetsFilterDataProvider,
new UserManagerState());
}
@VisibleForTesting
LoaderTask(@NonNull LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel bgModel,
ModelDelegate modelDelegate, @NonNull BaseLauncherBinder launcherBinder,
WidgetsFilterDataProvider widgetsFilterDataProvider,
UserManagerState userManagerState) {
mApp = app;
mBgAllAppsList = bgAllAppsList;
@@ -179,6 +184,7 @@ public class LoaderTask implements Runnable {
mIconCache = mApp.getIconCache();
mUserManagerState = userManagerState;
mInstallingPkgsCached = null;
mWidgetsFilterDataProvider = widgetsFilterDataProvider;
}
protected synchronized void waitForIdle() {
@@ -330,7 +336,15 @@ public class LoaderTask implements Runnable {
verifyNotStopped();
// fourth step
List<CachedObject> allWidgetsList = mBgDataModel.widgetsModel.update(mApp, null);
WidgetsModel widgetsModel = mBgDataModel.widgetsModel;
if (enableTieredWidgetsByDefaultInPicker()) {
// Begin periodic refresh of filters
mWidgetsFilterDataProvider.initPeriodicDataRefresh(
mApp.getModel()::onWidgetFiltersLoaded);
// And, update model with currently cached data.
widgetsModel.updateWidgetFilters(mWidgetsFilterDataProvider);
}
List<CachedObject> allWidgetsList = widgetsModel.update(mApp, /*packageUser=*/null);
logASplit("load widgets");
verifyNotStopped();
@@ -35,7 +35,7 @@ class ModelTaskController(
val dataModel: BgDataModel,
val allAppsList: AllAppsList,
private val model: LauncherModel,
private val uiExecutor: Executor
private val uiExecutor: Executor,
) {
/** Schedules a {@param task} to be executed on the current callbacks. */
@@ -79,10 +79,19 @@ class ModelTaskController(
}
fun bindUpdatedWidgets(dataModel: BgDataModel) {
val widgets =
WidgetsListBaseEntriesBuilder(app.context)
.build(dataModel.widgetsModel.widgetsByPackageItem)
scheduleCallbackTask { it.bindAllWidgets(widgets) }
val widgetsByPackageItem = dataModel.widgetsModel.widgetsByPackageItem
val allWidgets = WidgetsListBaseEntriesBuilder(app.context).build(widgetsByPackageItem)
val defaultWidgetsFilter = dataModel.widgetsModel.defaultWidgetsFilter
val defaultWidgets =
if (defaultWidgetsFilter != null) {
WidgetsListBaseEntriesBuilder(app.context)
.build(widgetsByPackageItem, defaultWidgetsFilter)
} else {
emptyList()
}
scheduleCallbackTask { it.bindAllWidgets(allWidgets, defaultWidgets) }
}
fun deleteAndBindComponentsRemoved(matcher: Predicate<ItemInfo?>, reason: String?) {
@@ -99,7 +108,7 @@ class ModelTaskController(
val packageUserKeyToUidMap =
apps.associateBy(
keySelector = { PackageUserKey(it.componentName!!.packageName, it.user) },
valueTransform = { it.uid }
valueTransform = { it.uid },
)
scheduleCallbackTask { it.bindAllApplications(apps, flags, packageUserKeyToUidMap) }
}
@@ -0,0 +1,70 @@
/*
* 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.model
import android.content.Context
import androidx.annotation.WorkerThread
import com.android.launcher3.R
import com.android.launcher3.util.ResourceBasedOverride
import java.util.function.Predicate
/** Helper for the widgets model to load the filters that can be applied to available widgets. */
open class WidgetsFilterDataProvider(val context: Context) : ResourceBasedOverride {
/**
* Start regular periodic refresh of widget filtering data starting now (if not started
* already).
*/
@WorkerThread
open fun initPeriodicDataRefresh(callback: WidgetsFilterLoadedCallback? = null) {
// no-op
}
/**
* Returns a filter that should be applied to the widget predictions.
*
* @return null if no filter needs to be applied
*/
@WorkerThread open fun getPredictedWidgetsFilter(): Predicate<WidgetItem>? = null
/**
* Returns a filter that should be applied to the widgets list to see which widgets can be shown
* by default.
*
* @return null if no separate "default" list is supported
*/
@WorkerThread open fun getDefaultWidgetsFilter(): Predicate<WidgetItem>? = null
/** Called when filter data provider is no longer needed. */
open fun destroy() {}
companion object {
/** Returns a new instance of the [WidgetsFilterDataProvider] based on resource override. */
fun newInstance(context: Context?): WidgetsFilterDataProvider {
return ResourceBasedOverride.Overrides.getObject(
WidgetsFilterDataProvider::class.java,
context,
R.string.widgets_filter_data_provider_class,
)
}
}
}
/** Interface for the model callback to be invoked when filters are loaded. */
interface WidgetsFilterLoadedCallback {
/** Method called back when widget filters are loaded */
fun onWidgetsFilterLoaded()
}
@@ -18,6 +18,8 @@ import android.os.UserHandle;
import android.util.Log;
import android.util.Pair;
import androidx.annotation.AnyThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.collection.ArrayMap;
@@ -65,6 +67,8 @@ public class WidgetsModel {
/* Map of widgets and shortcuts that are tracked per package. */
private final Map<PackageItemInfo, List<WidgetItem>> mWidgetsByPackageItem = new HashMap<>();
@Nullable private Predicate<WidgetItem> mDefaultWidgetsFilter = null;
@Nullable private Predicate<WidgetItem> mPredictedWidgetsFilter = null;
/**
* Returns all widgets keyed by their component key.
@@ -91,6 +95,37 @@ public class WidgetsModel {
return new HashMap<>(mWidgetsByPackageItem);
}
/**
* Returns widget filter that can be applied to {@link WidgetItem}s to check if they can be
* shown in the default widgets list.
* <p>Returns null if filtering isn't available</p>
*/
@AnyThread
public @Nullable Predicate<WidgetItem> getDefaultWidgetsFilter() {
return mDefaultWidgetsFilter;
}
/**
* Returns widget filter that can be applied to {@link WidgetItem}s to check if they can be
* part of widget predictions.
* <p>Returns null if filter isn't available</p>
*/
@AnyThread
public @Nullable Predicate<WidgetItem> getPredictedWidgetsFilter() {
return mPredictedWidgetsFilter;
}
/**
* Updates model with latest filter data in cache.
*/
public void updateWidgetFilters(@NonNull WidgetsFilterDataProvider widgetsFilterDataProvider) {
if (!WIDGETS_ENABLED) {
return;
}
mDefaultWidgetsFilter = widgetsFilterDataProvider.getDefaultWidgetsFilter();
mPredictedWidgetsFilter = widgetsFilterDataProvider.getPredictedWidgetsFilter();
}
/**
* @param packageUser If null, all widgets and shortcuts are updated and returned, otherwise
* only widgets and shortcuts associated with the package/user are.
@@ -299,7 +334,7 @@ public class WidgetsModel {
if (pInfo == null) {
pInfo = new PackageItemInfo(key.mPackageName, key.mWidgetCategory, key.mUser);
pInfo.user = key.mUser;
mMap.put(key, pInfo);
mMap.put(key, pInfo);
}
return pInfo;
}
@@ -43,6 +43,7 @@ import com.android.launcher3.widget.WidgetSections.NO_CATEGORY
import com.google.common.truth.Truth.assertThat
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import java.util.function.Predicate
import org.junit.Assert.fail
import org.junit.Before
import org.junit.Rule
@@ -64,6 +65,7 @@ class WidgetsModelTest {
@Mock private lateinit var appWidgetManager: AppWidgetManager
@Mock private lateinit var app: LauncherAppState
@Mock private lateinit var iconCacheMock: IconCache
@Mock private lateinit var widgetsFilterDataProvider: WidgetsFilterDataProvider
private lateinit var context: Context
private lateinit var idp: InvariantDeviceProfile
@@ -215,6 +217,27 @@ class WidgetsModelTest {
// No exception
}
@Test
fun updateWidgetFilters_setsFiltersCorrectly() {
val testDefaultWidgetFilter = Predicate<WidgetItem> { w -> w.widgetInfo != null }
whenever(widgetsFilterDataProvider.getDefaultWidgetsFilter())
.thenReturn(testDefaultWidgetFilter)
val testPredicatedWidgetFilter = Predicate<WidgetItem> { w -> w.widgetInfo != null }
whenever(widgetsFilterDataProvider.getPredictedWidgetsFilter())
.thenReturn(testPredicatedWidgetFilter)
underTest.updateWidgetFilters(widgetsFilterDataProvider)
assertThat(underTest.defaultWidgetsFilter).isEqualTo(testDefaultWidgetFilter)
assertThat(underTest.predictedWidgetsFilter).isEqualTo(testPredicatedWidgetFilter)
}
@Test
fun widgetFilters_nullInitially() {
assertThat(underTest.defaultWidgetsFilter).isNull()
assertThat(underTest.predictedWidgetsFilter).isNull()
}
private fun loadWidgets() {
val latch = CountDownLatch(1)
Executors.MODEL_EXECUTOR.execute {
@@ -28,6 +28,7 @@ import com.android.launcher3.util.TestUtil
import com.android.launcher3.util.UserIconInfo
import com.google.common.truth.Truth
import java.util.concurrent.CountDownLatch
import java.util.function.Predicate
import junit.framework.Assert.assertEquals
import org.junit.After
import org.junit.Before
@@ -76,6 +77,7 @@ class LoaderTaskTest {
@Mock private lateinit var modelDelegate: ModelDelegate
@Mock private lateinit var launcherBinder: BaseLauncherBinder
private lateinit var launcherModel: LauncherModel
@Mock private lateinit var widgetsFilterDataProvider: WidgetsFilterDataProvider
@Mock private lateinit var transaction: LoaderTransaction
@Mock private lateinit var iconCache: IconCache
@Mock private lateinit var idleLock: LooperIdleLock
@@ -89,6 +91,7 @@ class LoaderTaskTest {
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
setFlagsRule.enableFlags(Flags.FLAG_ENABLE_TIERED_WIDGETS_BY_DEFAULT_IN_PICKER)
launcherModel = mock(LauncherModel::class.java)
mockitoSession =
ExtendedMockito.mockitoSession()
@@ -118,6 +121,7 @@ class LoaderTaskTest {
`when`(launcherBinder.newIdleLock(any())).thenReturn(idleLock)
`when`(idleLock.awaitLocked(1000)).thenReturn(false)
`when`(iconCache.updateHandler).thenReturn(iconCacheUpdateHandler)
`when`(widgetsFilterDataProvider.getDefaultWidgetsFilter()).thenReturn(Predicate { true })
context.putObject(UserCache.INSTANCE, userCache)
TestUtil.grantWriteSecurePermission()
@@ -136,17 +140,32 @@ class LoaderTaskTest {
val mockUserHandles = arrayListOf<UserHandle>(MAIN_HANDLE)
`when`(userCache.userProfiles).thenReturn(mockUserHandles)
`when`(userCache.getUserInfo(MAIN_HANDLE)).thenReturn(UserIconInfo(MAIN_HANDLE, 1))
LoaderTask(app, bgAllAppsList, this, modelDelegate, launcherBinder)
LoaderTask(
app,
bgAllAppsList,
this,
modelDelegate,
launcherBinder,
widgetsFilterDataProvider,
)
.runSyncOnBackgroundThread()
Truth.assertThat(workspaceItems.size).isAtLeast(25)
Truth.assertThat(appWidgets.size).isAtLeast(7)
Truth.assertThat(collections.size()).isAtLeast(8)
Truth.assertThat(itemsIdMap.size()).isAtLeast(40)
Truth.assertThat(widgetsModel.defaultWidgetsFilter).isNotNull()
}
@Test
fun bindsLoadedDataCorrectly() {
LoaderTask(app, bgAllAppsList, BgDataModel(), modelDelegate, launcherBinder)
LoaderTask(
app,
bgAllAppsList,
BgDataModel(),
modelDelegate,
launcherBinder,
widgetsFilterDataProvider,
)
.runSyncOnBackgroundThread()
verify(launcherBinder).bindWorkspace(true, false)
@@ -155,6 +174,7 @@ class LoaderTaskTest {
verify(launcherBinder).bindAllApps()
verify(iconCacheUpdateHandler, times(4)).updateIcons(any(), any<CachingLogic<Any>>(), any())
verify(launcherBinder).bindDeepShortcuts()
verify(widgetsFilterDataProvider).initPeriodicDataRefresh(any())
verify(launcherBinder).bindWidgets()
verify(modelDelegate).loadAndBindOtherItems(anyOrNull())
verify(iconCacheUpdateHandler).finish()
@@ -172,7 +192,15 @@ class LoaderTaskTest {
`when`(userManagerState?.isUserQuiet(MAIN_HANDLE)).thenReturn(true)
`when`(userCache.getUserInfo(MAIN_HANDLE)).thenReturn(UserIconInfo(MAIN_HANDLE, 1))
LoaderTask(app, bgAllAppsList, this, modelDelegate, launcherBinder, userManagerState)
LoaderTask(
app,
bgAllAppsList,
this,
modelDelegate,
launcherBinder,
widgetsFilterDataProvider,
userManagerState,
)
.runSyncOnBackgroundThread()
verify(bgAllAppsList)
@@ -193,7 +221,15 @@ class LoaderTaskTest {
`when`(userManagerState?.isUserQuiet(MAIN_HANDLE)).thenReturn(true)
`when`(userCache.getUserInfo(MAIN_HANDLE)).thenReturn(UserIconInfo(MAIN_HANDLE, 3))
LoaderTask(app, bgAllAppsList, this, modelDelegate, launcherBinder, userManagerState)
LoaderTask(
app,
bgAllAppsList,
this,
modelDelegate,
launcherBinder,
widgetsFilterDataProvider,
userManagerState,
)
.runSyncOnBackgroundThread()
verify(bgAllAppsList)
@@ -232,7 +268,14 @@ class LoaderTaskTest {
RestoreDbTask.setPending(spyContext)
// When
LoaderTask(app, bgAllAppsList, BgDataModel(), modelDelegate, launcherBinder)
LoaderTask(
app,
bgAllAppsList,
BgDataModel(),
modelDelegate,
launcherBinder,
widgetsFilterDataProvider,
)
.runSyncOnBackgroundThread()
// Then
@@ -301,7 +344,14 @@ class LoaderTaskTest {
RestoreDbTask.setPending(spyContext)
// When
LoaderTask(app, bgAllAppsList, BgDataModel(), modelDelegate, launcherBinder)
LoaderTask(
app,
bgAllAppsList,
BgDataModel(),
modelDelegate,
launcherBinder,
widgetsFilterDataProvider,
)
.runSyncOnBackgroundThread()
// Then
@@ -369,7 +419,14 @@ class LoaderTaskTest {
Settings.Secure.putInt(spyContext.contentResolver, "launcher_broadcast_installed_apps", 1)
// When
LoaderTask(app, bgAllAppsList, BgDataModel(), modelDelegate, launcherBinder)
LoaderTask(
app,
bgAllAppsList,
BgDataModel(),
modelDelegate,
launcherBinder,
widgetsFilterDataProvider,
)
.runSyncOnBackgroundThread()
// Then
@@ -404,7 +461,14 @@ class LoaderTaskTest {
RestoreDbTask.setPending(spyContext)
// When
LoaderTask(app, bgAllAppsList, BgDataModel(), modelDelegate, launcherBinder)
LoaderTask(
app,
bgAllAppsList,
BgDataModel(),
modelDelegate,
launcherBinder,
widgetsFilterDataProvider,
)
.runSyncOnBackgroundThread()
// Then