From 869b20cb27bcf79be4154b853f233cab2c3d239a Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 13 May 2025 14:59:18 -0700 Subject: [PATCH] Adding repository for AppsList data Flag: com.android.launcher3.model_repository Test: atest AppsListRepositoryTest Bug: 390572144 Change-Id: I6e5993c91f77ed8f0b6f2c12c9d743c87a497134 --- .../launcher3/allapps/AllAppsStore.java | 11 ++-- .../allapps/PrivateProfileManager.java | 2 +- .../launcher3/allapps/WorkProfileManager.java | 8 +-- .../android/launcher3/model/AllAppsList.java | 48 ++++++++++------ .../launcher3/model/BaseLauncherBinder.java | 16 ++---- .../android/launcher3/model/BgDataModel.kt | 17 ------ .../android/launcher3/model/LoaderTask.java | 10 ++-- .../launcher3/model/ModelTaskController.kt | 23 +++++--- ...PackageIncrementalDownloadUpdatedTask.java | 13 ++--- .../model/PackageInstallStateChangedTask.java | 11 +--- .../launcher3/model/PackageUpdatedTask.java | 6 +- .../launcher3/model/data/AppsListData.kt | 50 ++++++++++++++++ .../model/repository/AppsListRepository.kt | 57 +++++++++++++++++++ .../allapps/PrivateProfileManagerTest.java | 2 +- .../launcher3/model/PackageUpdatedTaskTest.kt | 9 ++- .../android/launcher3/model/LoaderTaskTest.kt | 21 +++---- 16 files changed, 199 insertions(+), 105 deletions(-) create mode 100644 src/com/android/launcher3/model/data/AppsListData.kt create mode 100644 src/com/android/launcher3/model/repository/AppsListRepository.kt diff --git a/src/com/android/launcher3/allapps/AllAppsStore.java b/src/com/android/launcher3/allapps/AllAppsStore.java index 821027e2bd..e9b59dfbc1 100644 --- a/src/com/android/launcher3/allapps/AllAppsStore.java +++ b/src/com/android/launcher3/allapps/AllAppsStore.java @@ -127,12 +127,11 @@ public class AllAppsStore { } /** - * @see com.android.launcher3.model.BgDataModel.Callbacks#FLAG_QUIET_MODE_ENABLED - * @see com.android.launcher3.model.BgDataModel.Callbacks#FLAG_HAS_SHORTCUT_PERMISSION - * @see com.android.launcher3.model.BgDataModel.Callbacks#FLAG_QUIET_MODE_CHANGE_PERMISSION - * @see com.android.launcher3.model.BgDataModel.Callbacks#FLAG_WORK_PROFILE_QUIET_MODE_ENABLED - * @see - * com.android.launcher3.model.BgDataModel.Callbacks#FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED + * @see com.android.launcher3.model.data.AppsListData#FLAG_QUIET_MODE_ENABLED + * @see com.android.launcher3.model.data.AppsListData#FLAG_HAS_SHORTCUT_PERMISSION + * @see com.android.launcher3.model.data.AppsListData#FLAG_QUIET_MODE_CHANGE_PERMISSION + * @see com.android.launcher3.model.data.AppsListData#FLAG_WORK_PROFILE_QUIET_MODE_ENABLED + * @see com.android.launcher3.model.data.AppsListData#FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED */ public boolean hasModelFlag(int mask) { return (mModelFlags & mask) != 0; diff --git a/src/com/android/launcher3/allapps/PrivateProfileManager.java b/src/com/android/launcher3/allapps/PrivateProfileManager.java index 2bedb82179..0203bfe761 100644 --- a/src/com/android/launcher3/allapps/PrivateProfileManager.java +++ b/src/com/android/launcher3/allapps/PrivateProfileManager.java @@ -32,7 +32,7 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_UNLOCK_ANIMATION_BEGIN; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_UNLOCK_ANIMATION_END; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_UNLOCK_TAP; -import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED; +import static com.android.launcher3.model.data.AppsListData.FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED; import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_NOT_PINNABLE; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; diff --git a/src/com/android/launcher3/allapps/WorkProfileManager.java b/src/com/android/launcher3/allapps/WorkProfileManager.java index 920efa4f64..c20833aa4d 100644 --- a/src/com/android/launcher3/allapps/WorkProfileManager.java +++ b/src/com/android/launcher3/allapps/WorkProfileManager.java @@ -22,10 +22,10 @@ import static com.android.launcher3.allapps.ActivityAllAppsContainerView.Adapter import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_WORK_DISABLED_CARD; import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_WORK_EDU_CARD; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TURN_OFF_WORK_APPS_TAP; -import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_HAS_SHORTCUT_PERMISSION; -import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_CHANGE_PERMISSION; -import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED; -import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_WORK_PROFILE_QUIET_MODE_ENABLED; +import static com.android.launcher3.model.data.AppsListData.FLAG_HAS_SHORTCUT_PERMISSION; +import static com.android.launcher3.model.data.AppsListData.FLAG_QUIET_MODE_CHANGE_PERMISSION; +import static com.android.launcher3.model.data.AppsListData.FLAG_QUIET_MODE_ENABLED; +import static com.android.launcher3.model.data.AppsListData.FLAG_WORK_PROFILE_QUIET_MODE_ENABLED; import android.os.UserHandle; import android.os.UserManager; diff --git a/src/com/android/launcher3/model/AllAppsList.java b/src/com/android/launcher3/model/AllAppsList.java index 2311239025..cbb0cd01bc 100644 --- a/src/com/android/launcher3/model/AllAppsList.java +++ b/src/com/android/launcher3/model/AllAppsList.java @@ -33,12 +33,14 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.launcher3.AppFilter; +import com.android.launcher3.Flags; import com.android.launcher3.compat.AlphabeticIndexCompat; import com.android.launcher3.dagger.LauncherAppSingleton; import com.android.launcher3.icons.IconCache; -import com.android.launcher3.model.BgDataModel.Callbacks; import com.android.launcher3.model.data.AppInfo; +import com.android.launcher3.model.data.AppsListData; import com.android.launcher3.model.data.ItemInfo; +import com.android.launcher3.model.repository.AppsListRepository; import com.android.launcher3.pm.PackageInstallInfo; import com.android.launcher3.pm.UserCache; import com.android.launcher3.util.ApiWrapper; @@ -55,6 +57,7 @@ import java.util.function.Consumer; import java.util.function.Predicate; import javax.inject.Inject; +import javax.inject.Provider; /** @@ -78,17 +81,19 @@ public class AllAppsList { @NonNull private final AppFilter mAppFilter; + @NonNull private final Provider mRepo; + private boolean mDataChanged = false; private Consumer mRemoveListener = NO_OP_CONSUMER; private AlphabeticIndexCompat mIndex; /** - * @see Callbacks#FLAG_HAS_SHORTCUT_PERMISSION - * @see Callbacks#FLAG_QUIET_MODE_ENABLED - * @see Callbacks#FLAG_QUIET_MODE_CHANGE_PERMISSION - * @see Callbacks#FLAG_WORK_PROFILE_QUIET_MODE_ENABLED - * @see Callbacks#FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED + * @see AppsListData#FLAG_HAS_SHORTCUT_PERMISSION + * @see AppsListData#FLAG_QUIET_MODE_ENABLED + * @see AppsListData#FLAG_QUIET_MODE_CHANGE_PERMISSION + * @see AppsListData#FLAG_WORK_PROFILE_QUIET_MODE_ENABLED + * @see AppsListData#FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED */ private int mFlags; @@ -96,9 +101,12 @@ public class AllAppsList { * Boring constructor. */ @Inject - public AllAppsList(IconCache iconCache, AppFilter appFilter) { + public AllAppsList(@NonNull IconCache iconCache, + @NonNull AppFilter appFilter, + @NonNull Provider repositoryProvider) { mIconCache = iconCache; mAppFilter = appFilter; + mRepo = repositoryProvider; mIndex = new AlphabeticIndexCompat(LocaleList.getDefault()); } @@ -108,14 +116,18 @@ public class AllAppsList { public boolean getAndResetChangeFlag() { boolean result = mDataChanged; mDataChanged = false; + + if (Flags.modelRepository() && result) { + mRepo.get().dispatchChange(getImmutableData()); + } return result; } /** - * Helper to checking {@link Callbacks#FLAG_HAS_SHORTCUT_PERMISSION} + * Helper to checking {@link AppsListData#FLAG_HAS_SHORTCUT_PERMISSION} */ public boolean hasShortcutHostPermission() { - return (mFlags & Callbacks.FLAG_HAS_SHORTCUT_PERMISSION) != 0; + return (mFlags & AppsListData.FLAG_HAS_SHORTCUT_PERMISSION) != 0; } /** @@ -130,14 +142,11 @@ public class AllAppsList { mDataChanged = true; } - /** - * Returns the model flags - */ - public int getFlags() { - return mFlags; + /** Returns an immutable representation of the current data */ + public AppsListData getImmutableData() { + return new AppsListData(copyData(), mFlags); } - /** * Add the supplied ApplicationInfo objects to the list, and enqueue it into the * list to broadcast when notify() is called. @@ -200,7 +209,8 @@ public class AllAppsList { } /** Updates the given PackageInstallInfo's associated AppInfo's installation info. */ - public List updatePromiseInstallInfo(PackageInstallInfo installInfo) { + public List updatePromiseInstallInfo(PackageInstallInfo installInfo, + FlagOp runtimeFlagUpdate) { List updatedAppInfos = new ArrayList<>(); UserHandle user = installInfo.user; for (int i = data.size() - 1; i >= 0; i--) { @@ -222,7 +232,11 @@ public class AllAppsList { continue; } appInfo.setProgressLevel(installInfo); - + appInfo.runtimeStatusFlags = + runtimeFlagUpdate.apply(appInfo.runtimeStatusFlags); + if (Flags.modelRepository()) { + mRepo.get().dispatchIncrementationUpdate(appInfo); + } updatedAppInfos.add(appInfo); } else if (installInfo.state == PackageInstallInfo.STATUS_FAILED && !appInfo.isAppStartable()) { diff --git a/src/com/android/launcher3/model/BaseLauncherBinder.java b/src/com/android/launcher3/model/BaseLauncherBinder.java index 38d5195174..504930665c 100644 --- a/src/com/android/launcher3/model/BaseLauncherBinder.java +++ b/src/com/android/launcher3/model/BaseLauncherBinder.java @@ -28,12 +28,11 @@ import com.android.launcher3.LauncherModel.CallbackTask; import com.android.launcher3.dagger.ApplicationContext; import com.android.launcher3.model.BgDataModel.Callbacks; import com.android.launcher3.model.BgDataModel.FixedContainerItems; -import com.android.launcher3.model.data.AppInfo; +import com.android.launcher3.model.data.AppsListData; import com.android.launcher3.model.data.WorkspaceData; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.LooperExecutor; import com.android.launcher3.util.LooperIdleLock; -import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.widget.model.WidgetsListBaseEntriesBuilder; import com.android.launcher3.widget.model.WidgetsListBaseEntry; @@ -42,12 +41,9 @@ import dagger.assisted.AssistedFactory; import dagger.assisted.AssistedInject; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.concurrent.Executor; -import java.util.stream.Collectors; /** * Binds the results of {@link com.android.launcher3.model.LoaderTask} to the Callbacks objects. @@ -136,13 +132,9 @@ public class BaseLauncherBinder { */ public void bindAllApps() { // shallow copy - AppInfo[] apps = mBgAllAppsList.copyData(); - int flags = mBgAllAppsList.getFlags(); - Map packageUserKeytoUidMap = Arrays.stream(apps).collect( - Collectors.toMap( - appInfo -> new PackageUserKey(appInfo.componentName.getPackageName(), - appInfo.user), appInfo -> appInfo.uid, (a, b) -> a)); - executeCallbacksTask(c -> c.bindAllApplications(apps, flags, packageUserKeytoUidMap), + AppsListData data = mBgAllAppsList.getImmutableData(); + executeCallbacksTask(c -> c.bindAllApplications( + data.getApps(), data.getFlags(), data.getPackageUserKeyToUidMap()), mUiExecutor); } diff --git a/src/com/android/launcher3/model/BgDataModel.kt b/src/com/android/launcher3/model/BgDataModel.kt index 8ed748a6b7..f6f89b7771 100644 --- a/src/com/android/launcher3/model/BgDataModel.kt +++ b/src/com/android/launcher3/model/BgDataModel.kt @@ -418,23 +418,6 @@ constructor( /** Binds the cache of string resources */ fun bindStringCache(cache: StringCache) {} - - companion object { - // If the launcher has permission to access deep shortcuts. - const val FLAG_HAS_SHORTCUT_PERMISSION: Int = 1 shl 0 - - // If quiet mode is enabled for any user - const val FLAG_QUIET_MODE_ENABLED: Int = 1 shl 1 - - // If launcher can change quiet mode - const val FLAG_QUIET_MODE_CHANGE_PERMISSION: Int = 1 shl 2 - - // If quiet mode is enabled for work profile user - const val FLAG_WORK_PROFILE_QUIET_MODE_ENABLED: Int = 1 shl 3 - - // If quiet mode is enabled for private profile user - const val FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED: Int = 1 shl 4 - } } companion object { diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java index 614449c230..d81b7fdf46 100644 --- a/src/com/android/launcher3/model/LoaderTask.java +++ b/src/com/android/launcher3/model/LoaderTask.java @@ -20,13 +20,13 @@ import static com.android.launcher3.Flags.enableLauncherBrMetricsFixed; import static com.android.launcher3.LauncherPrefs.IS_FIRST_LOAD_AFTER_RESTORE; import static com.android.launcher3.icons.CacheableShortcutInfo.convertShortcutsToCacheableShortcuts; import static com.android.launcher3.icons.cache.CacheLookupFlag.DEFAULT_LOOKUP_FLAG; -import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_HAS_SHORTCUT_PERMISSION; -import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED; -import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_CHANGE_PERMISSION; -import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED; -import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_WORK_PROFILE_QUIET_MODE_ENABLED; import static com.android.launcher3.model.ModelUtils.WIDGET_FILTER; import static com.android.launcher3.model.ModelUtils.currentScreenContentFilter; +import static com.android.launcher3.model.data.AppsListData.FLAG_HAS_SHORTCUT_PERMISSION; +import static com.android.launcher3.model.data.AppsListData.FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED; +import static com.android.launcher3.model.data.AppsListData.FLAG_QUIET_MODE_CHANGE_PERMISSION; +import static com.android.launcher3.model.data.AppsListData.FLAG_QUIET_MODE_ENABLED; +import static com.android.launcher3.model.data.AppsListData.FLAG_WORK_PROFILE_QUIET_MODE_ENABLED; import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import static com.android.launcher3.util.LooperExecutor.CALLER_LOADER_TASK; diff --git a/src/com/android/launcher3/model/ModelTaskController.kt b/src/com/android/launcher3/model/ModelTaskController.kt index 6eedb73050..626b85dab4 100644 --- a/src/com/android/launcher3/model/ModelTaskController.kt +++ b/src/com/android/launcher3/model/ModelTaskController.kt @@ -23,9 +23,9 @@ import com.android.launcher3.celllayout.CellPosMapper import com.android.launcher3.dagger.ApplicationContext import com.android.launcher3.icons.IconCache 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.util.Executors.MAIN_EXECUTOR -import com.android.launcher3.util.PackageUserKey import com.android.launcher3.widget.model.WidgetsListBaseEntriesBuilder import java.util.function.Predicate import javax.inject.Inject @@ -90,14 +90,19 @@ constructor( fun bindApplicationsIfNeeded() { if (allAppsList.getAndResetChangeFlag()) { - val apps = allAppsList.copyData() - val flags = allAppsList.flags - val packageUserKeyToUidMap = - apps.associateBy( - keySelector = { PackageUserKey(it.componentName!!.packageName, it.user) }, - valueTransform = { it.uid }, - ) - scheduleCallbackTask { it.bindAllApplications(apps, flags, packageUserKeyToUidMap) } + // shallow copy + val data = allAppsList.immutableData + scheduleCallbackTask { + it.bindAllApplications(data.apps, data.flags, data.packageUserKeyToUidMap) + } + } + } + + fun bindIncrementalUpdates(updatedAppInfos: List) { + if (updatedAppInfos.isNotEmpty()) { + updatedAppInfos.forEach { info -> + scheduleCallbackTask { it.bindIncrementalDownloadProgressUpdated(info) } + } } } } diff --git a/src/com/android/launcher3/model/PackageIncrementalDownloadUpdatedTask.java b/src/com/android/launcher3/model/PackageIncrementalDownloadUpdatedTask.java index 3ea8dda524..65f62f86fc 100644 --- a/src/com/android/launcher3/model/PackageIncrementalDownloadUpdatedTask.java +++ b/src/com/android/launcher3/model/PackageIncrementalDownloadUpdatedTask.java @@ -20,10 +20,10 @@ import android.os.UserHandle; import androidx.annotation.NonNull; import com.android.launcher3.LauncherModel.ModelUpdateTask; -import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.ItemInfoWithIcon; import com.android.launcher3.pm.PackageInstallInfo; +import com.android.launcher3.util.FlagOp; import java.util.List; @@ -57,14 +57,9 @@ public class PackageIncrementalDownloadUpdatedTask implements ModelUpdateTask { mUser); synchronized (appsList) { - List updatedAppInfos = appsList.updatePromiseInstallInfo(downloadInfo); - if (!updatedAppInfos.isEmpty()) { - for (AppInfo appInfo : updatedAppInfos) { - appInfo.runtimeStatusFlags &= ~ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE; - taskController.scheduleCallbackTask( - c -> c.bindIncrementalDownloadProgressUpdated(appInfo)); - } - } + taskController.bindIncrementalUpdates(appsList.updatePromiseInstallInfo( + downloadInfo, + FlagOp.NO_OP.removeFlag(ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE))); taskController.bindApplicationsIfNeeded(); } diff --git a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java index e7192af443..6e02f4d665 100644 --- a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java +++ b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java @@ -22,9 +22,9 @@ import android.content.pm.PackageManager; import androidx.annotation.NonNull; import com.android.launcher3.LauncherModel.ModelUpdateTask; -import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.pm.PackageInstallInfo; +import com.android.launcher3.util.FlagOp; import com.android.launcher3.util.InstantAppResolver; import java.util.List; @@ -63,13 +63,8 @@ public class PackageInstallStateChangedTask implements ModelUpdateTask { } synchronized (apps) { - List updatedAppInfos = apps.updatePromiseInstallInfo(mInstallInfo); - if (!updatedAppInfos.isEmpty()) { - for (AppInfo appInfo : updatedAppInfos) { - taskController.scheduleCallbackTask( - c -> c.bindIncrementalDownloadProgressUpdated(appInfo)); - } - } + taskController.bindIncrementalUpdates( + apps.updatePromiseInstallInfo(mInstallInfo, FlagOp.NO_OP)); taskController.bindApplicationsIfNeeded(); } diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java index dc553a14c0..5fe53a0b70 100644 --- a/src/com/android/launcher3/model/PackageUpdatedTask.java +++ b/src/com/android/launcher3/model/PackageUpdatedTask.java @@ -16,9 +16,9 @@ package com.android.launcher3.model; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT; -import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED; -import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED; -import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_WORK_PROFILE_QUIET_MODE_ENABLED; +import static com.android.launcher3.model.data.AppsListData.FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED; +import static com.android.launcher3.model.data.AppsListData.FLAG_QUIET_MODE_ENABLED; +import static com.android.launcher3.model.data.AppsListData.FLAG_WORK_PROFILE_QUIET_MODE_ENABLED; import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_ARCHIVED; import static com.android.launcher3.model.data.LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY; import static com.android.launcher3.model.data.WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON; diff --git a/src/com/android/launcher3/model/data/AppsListData.kt b/src/com/android/launcher3/model/data/AppsListData.kt new file mode 100644 index 0000000000..fc0b65ad87 --- /dev/null +++ b/src/com/android/launcher3/model/data/AppsListData.kt @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2025 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.data + +import com.android.launcher3.util.PackageUserKey + +/** Immutable representation of all-apps list data */ +class AppsListData( + val apps: Array, + // Current model flags + val flags: Int, +) { + + val packageUserKeyToUidMap: Map = + apps.associateBy( + keySelector = { PackageUserKey(it.targetComponent.packageName, it.user) }, + valueTransform = { it.uid }, + ) + + companion object { + // If the launcher has permission to access deep shortcuts. + const val FLAG_HAS_SHORTCUT_PERMISSION: Int = 1 shl 0 + + // If quiet mode is enabled for any user + const val FLAG_QUIET_MODE_ENABLED: Int = 1 shl 1 + + // If launcher can change quiet mode + const val FLAG_QUIET_MODE_CHANGE_PERMISSION: Int = 1 shl 2 + + // If quiet mode is enabled for work profile user + const val FLAG_WORK_PROFILE_QUIET_MODE_ENABLED: Int = 1 shl 3 + + // If quiet mode is enabled for private profile user + const val FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED: Int = 1 shl 4 + } +} diff --git a/src/com/android/launcher3/model/repository/AppsListRepository.kt b/src/com/android/launcher3/model/repository/AppsListRepository.kt new file mode 100644 index 0000000000..4d2ef3472f --- /dev/null +++ b/src/com/android/launcher3/model/repository/AppsListRepository.kt @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2025 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.repository + +import com.android.launcher3.dagger.LauncherAppSingleton +import com.android.launcher3.model.data.AppInfo +import com.android.launcher3.model.data.AppsListData +import com.android.launcher3.util.Executors.MODEL_EXECUTOR +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.asCoroutineDispatcher +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch + +/** Repository for app-list daya. */ +@LauncherAppSingleton +class AppsListRepository(private val scope: CoroutineScope) { + + @Inject constructor() : this(CoroutineScope(MODEL_EXECUTOR.asCoroutineDispatcher())) + + private val mutableStateFlow: MutableStateFlow = + MutableStateFlow(AppsListData(emptyArray(), 0)) + + /** Represents the current home screen data model. There are two ways this can change: */ + val appsListStateFlow = mutableStateFlow.asStateFlow() + + /** sets a new value to [appsListStateFlow] */ + fun dispatchChange(appsListData: AppsListData) { + mutableStateFlow.value = appsListData + } + + private val mutableIncrementalUpdate = MutableSharedFlow() + /** Represents incremental download apps to apps list items */ + val incrementalUpdates = mutableIncrementalUpdate.asSharedFlow() + + /** Dispatches an incremental download update to [incrementalUpdates] */ + fun dispatchIncrementationUpdate(appInfo: AppInfo) { + scope.launch { mutableIncrementalUpdate.emit(appInfo) } + } +} diff --git a/tests/multivalentTests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java b/tests/multivalentTests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java index 98259938cd..9ad8700110 100644 --- a/tests/multivalentTests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java +++ b/tests/multivalentTests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java @@ -20,7 +20,7 @@ import static androidx.test.core.app.ApplicationProvider.getApplicationContext; import static com.android.launcher3.allapps.UserProfileManager.STATE_DISABLED; import static com.android.launcher3.allapps.UserProfileManager.STATE_ENABLED; -import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED; +import static com.android.launcher3.model.data.AppsListData.FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static org.junit.Assert.assertEquals; diff --git a/tests/multivalentTests/src/com/android/launcher3/model/PackageUpdatedTaskTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/PackageUpdatedTaskTest.kt index b8f553e921..872e67d2f4 100644 --- a/tests/multivalentTests/src/com/android/launcher3/model/PackageUpdatedTaskTest.kt +++ b/tests/multivalentTests/src/com/android/launcher3/model/PackageUpdatedTaskTest.kt @@ -26,6 +26,7 @@ import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.SetFlagsRule import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.launcher3.AppFilter +import com.android.launcher3.Flags import com.android.launcher3.Flags.FLAG_ENABLE_PRIVATE_SPACE import com.android.launcher3.LauncherSettings import com.android.launcher3.dagger.LauncherAppComponent @@ -40,6 +41,7 @@ import com.android.launcher3.model.PackageUpdatedTask.OP_UPDATE import com.android.launcher3.model.PackageUpdatedTask.OP_USER_AVAILABILITY_CHANGE import com.android.launcher3.model.data.AppInfo import com.android.launcher3.model.data.WorkspaceItemInfo +import com.android.launcher3.model.repository.AppsListRepository import com.android.launcher3.util.AllModulesForTest import com.android.launcher3.util.Executors import com.android.launcher3.util.SandboxApplication @@ -73,6 +75,7 @@ class PackageUpdatedTaskTest { private val mockIconCache: IconCache = mock() private val mockAppFilter: AppFilter = mock() + private val appsListRepo = AppsListRepository() private lateinit var mAllAppsList: AllAppsList private val mockApplicationInfo: ApplicationInfo = mock() @@ -83,7 +86,7 @@ class PackageUpdatedTaskTest { @Before fun setup() { - mAllAppsList = spy(AllAppsList(mockIconCache, mockAppFilter)) + mAllAppsList = spy(AllAppsList(mockIconCache, mockAppFilter) { appsListRepo }) mContext.initDaggerComponent( DaggerPackageUpdatedTaskTest_TestComponent.builder() .bindAllAppsList(mAllAppsList) @@ -186,6 +189,7 @@ class PackageUpdatedTaskTest { } @Test + @EnableFlags(Flags.FLAG_MODEL_REPOSITORY) fun `OP_SUSPEND triggers model callbacks and updates flags in AllAppsList`() { // Given val taskUnderTest = PackageUpdatedTask(OP_SUSPEND, mUser, expectedPackage) @@ -201,9 +205,11 @@ class PackageUpdatedTaskTest { verify(mAllAppsList).updateDisabledFlags(any(), any()) verify(mockTaskController).bindUpdatedWorkspaceItems(listOf(expectedWorkspaceItem)) assertThat(mAllAppsList.getAndResetChangeFlag()).isTrue() + assertThat(appsListRepo.appsListStateFlow.value.apps).isNotEmpty() } @Test + @EnableFlags(Flags.FLAG_MODEL_REPOSITORY) fun `OP_UNSUSPEND triggers no callbacks when app not suspended`() { // Given val taskUnderTest = PackageUpdatedTask(OP_UNSUSPEND, mUser, expectedPackage) @@ -218,6 +224,7 @@ class PackageUpdatedTaskTest { verify(mAllAppsList).updateDisabledFlags(any(), any()) verify(mockTaskController).bindUpdatedWorkspaceItems(emptyList()) assertThat(mAllAppsList.getAndResetChangeFlag()).isFalse() + assertThat(appsListRepo.appsListStateFlow.value.apps).isEmpty() } @EnableFlags(FLAG_ENABLE_PRIVATE_SPACE) diff --git a/tests/src/com/android/launcher3/model/LoaderTaskTest.kt b/tests/src/com/android/launcher3/model/LoaderTaskTest.kt index 4aacb1fa3c..f884d94162 100644 --- a/tests/src/com/android/launcher3/model/LoaderTaskTest.kt +++ b/tests/src/com/android/launcher3/model/LoaderTaskTest.kt @@ -35,6 +35,9 @@ import com.android.launcher3.icons.cache.CachingLogic import com.android.launcher3.icons.cache.IconCacheUpdateHandler import com.android.launcher3.model.LoaderTask.LoaderTaskFactory import com.android.launcher3.model.data.AppInfo +import com.android.launcher3.model.data.AppsListData.Companion.FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED +import com.android.launcher3.model.data.AppsListData.Companion.FLAG_QUIET_MODE_ENABLED +import com.android.launcher3.model.data.AppsListData.Companion.FLAG_WORK_PROFILE_QUIET_MODE_ENABLED import com.android.launcher3.model.data.IconRequestInfo import com.android.launcher3.model.data.WorkspaceItemInfo import com.android.launcher3.pm.UserCache @@ -243,12 +246,9 @@ class LoaderTaskTest { .newLoaderTask(launcherBinder, userManagerState) .runSyncOnBackgroundThread() - verify(bgAllAppsList) - .setFlags(BgDataModel.Callbacks.FLAG_WORK_PROFILE_QUIET_MODE_ENABLED, true) - verify(bgAllAppsList) - .setFlags(BgDataModel.Callbacks.FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED, false) - verify(bgAllAppsList, Mockito.never()) - .setFlags(BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED, true) + verify(bgAllAppsList).setFlags(FLAG_WORK_PROFILE_QUIET_MODE_ENABLED, true) + verify(bgAllAppsList).setFlags(FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED, false) + verify(bgAllAppsList, Mockito.never()).setFlags(FLAG_QUIET_MODE_ENABLED, true) } @Test @@ -266,12 +266,9 @@ class LoaderTaskTest { .newLoaderTask(launcherBinder, userManagerState) .runSyncOnBackgroundThread() - verify(bgAllAppsList) - .setFlags(BgDataModel.Callbacks.FLAG_WORK_PROFILE_QUIET_MODE_ENABLED, false) - verify(bgAllAppsList) - .setFlags(BgDataModel.Callbacks.FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED, true) - verify(bgAllAppsList, Mockito.never()) - .setFlags(BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED, true) + verify(bgAllAppsList).setFlags(FLAG_WORK_PROFILE_QUIET_MODE_ENABLED, false) + verify(bgAllAppsList).setFlags(FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED, true) + verify(bgAllAppsList, Mockito.never()).setFlags(FLAG_QUIET_MODE_ENABLED, true) } @Test