diff --git a/quickstep/dagger/com/android/launcher3/dagger/AppModule.kt b/quickstep/dagger/com/android/launcher3/dagger/AppModule.kt index 29586c4afc..d88fc94d5b 100644 --- a/quickstep/dagger/com/android/launcher3/dagger/AppModule.kt +++ b/quickstep/dagger/com/android/launcher3/dagger/AppModule.kt @@ -16,10 +16,17 @@ package com.android.launcher3.dagger +import com.android.launcher3.model.ModelDelegate +import com.android.launcher3.model.QuickstepModelDelegate +import dagger.Binds import dagger.Module /** * Module containing bindings for the final derivative app, an implementation of this module should * be included in the final app code. */ -@Module abstract class AppModule {} +@Module +abstract class AppModule { + + @Binds abstract fun bindModelDelegate(impl: QuickstepModelDelegate): ModelDelegate +} diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml index a530325f04..d699cdf49e 100644 --- a/quickstep/res/values/config.xml +++ b/quickstep/res/values/config.xml @@ -27,7 +27,6 @@ com.android.quickstep.InstantAppResolverImpl com.android.launcher3.appprediction.PredictionAppTracker com.android.quickstep.QuickstepProcessInitializer - com.android.launcher3.model.QuickstepModelDelegate com.android.launcher3.secondarydisplay.SecondaryDisplayPredictionsImpl com.android.launcher3.taskbar.TaskbarModelCallbacksFactory com.android.launcher3.taskbar.TaskbarViewCallbacksFactory diff --git a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java index 40e8fc2484..74b73d4bdf 100644 --- a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java +++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java @@ -47,6 +47,7 @@ import android.content.pm.LauncherApps; import android.content.pm.ShortcutInfo; import android.os.Bundle; import android.os.UserHandle; +import android.text.TextUtils; import android.util.Log; import android.util.StatsEvent; @@ -61,6 +62,7 @@ import com.android.launcher3.ConstantItem; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherPrefs; +import com.android.launcher3.dagger.ApplicationContext; import com.android.launcher3.icons.cache.CacheLookupFlag; import com.android.launcher3.logger.LauncherAtom; import com.android.launcher3.logging.InstanceId; @@ -89,6 +91,9 @@ import java.util.Map; import java.util.Objects; import java.util.stream.IntStream; +import javax.inject.Inject; +import javax.inject.Named; + /** * Model delegate which loads prediction items */ @@ -114,18 +119,29 @@ public class QuickstepModelDelegate extends ModelDelegate { CONTAINER_WIDGETS_PREDICTION, "widgets_prediction", DESKTOP_ICON_FLAG); private final InvariantDeviceProfile mIDP; + private final PackageManagerHelper mPmHelper; private final AppEventProducer mAppEventProducer; + private final StatsManager mStatsManager; protected boolean mActive = false; - public QuickstepModelDelegate(Context context) { + @Inject + public QuickstepModelDelegate(@ApplicationContext Context context, + InvariantDeviceProfile idp, + PackageManagerHelper pmHelper, + @Nullable @Named("ICONS_DB") String dbFileName) { super(context); - mAppEventProducer = new AppEventProducer(context, this::onAppTargetEvent); + mIDP = idp; + mPmHelper = pmHelper; - mIDP = InvariantDeviceProfile.INSTANCE.get(context); + mAppEventProducer = new AppEventProducer(context, this::onAppTargetEvent); StatsLogCompatManager.LOGS_CONSUMER.add(mAppEventProducer); - mStatsManager = context.getSystemService(StatsManager.class); + + // Only register for launcher snapshot logging if this is the primary ModelDelegate + // instance, as there will be additional instances that may be destroyed at any time. + mStatsManager = TextUtils.isEmpty(dbFileName) + ? null : context.getSystemService(StatsManager.class); } @CallSuper @@ -154,10 +170,10 @@ public class QuickstepModelDelegate extends ModelDelegate { // TODO: Implement caching and preloading WorkspaceItemFactory factory = - new WorkspaceItemFactory(mApp, ums, mPmHelper, pinnedShortcuts, numColumns, + new WorkspaceItemFactory(mContext, ums, mPmHelper, pinnedShortcuts, numColumns, state.containerId, state.lookupFlag); FixedContainerItems fci = new FixedContainerItems(state.containerId, - state.storage.read(mApp.getContext(), factory, ums.allUsers::get)); + state.storage.read(mContext, factory, ums.allUsers::get)); mDataModel.extraItems.put(state.containerId, fci); } @@ -220,7 +236,7 @@ public class QuickstepModelDelegate extends ModelDelegate { super.modelLoadComplete(); // Log snapshot of the model - LauncherPrefs prefs = LauncherPrefs.get(mApp.getContext()); + LauncherPrefs prefs = LauncherPrefs.get(mContext); long lastSnapshotTimeMillis = prefs.get(LAST_SNAPSHOT_TIME_MILLIS); // Log snapshot only if previous snapshot was older than a day long now = System.currentTimeMillis(); @@ -245,11 +261,7 @@ public class QuickstepModelDelegate extends ModelDelegate { prefs.put(LAST_SNAPSHOT_TIME_MILLIS, now); } - // Only register for launcher snapshot logging if this is the primary ModelDelegate - // instance, as there will be additional instances that may be destroyed at any time. - if (mIsPrimaryInstance) { - registerSnapshotLoggingCallback(); - } + registerSnapshotLoggingCallback(); } protected void additionalSnapshotEvents(InstanceId snapshotInstanceId){} @@ -257,9 +269,9 @@ public class QuickstepModelDelegate extends ModelDelegate { /** * Registers a callback to log launcher workspace layout using Statsd pulled atom. */ - protected void registerSnapshotLoggingCallback() { + private void registerSnapshotLoggingCallback() { if (mStatsManager == null) { - Log.d(TAG, "Failed to get StatsManager"); + Log.d(TAG, "Skipping snapshot logging"); } try { @@ -332,7 +344,7 @@ public class QuickstepModelDelegate extends ModelDelegate { super.destroy(); mActive = false; StatsLogCompatManager.LOGS_CONSUMER.remove(mAppEventProducer); - if (mIsPrimaryInstance && mStatsManager != null) { + if (mStatsManager != null) { try { mStatsManager.clearPullAtomCallback(SysUiStatsLog.LAUNCHER_LAYOUT_SNAPSHOT); } catch (RuntimeException e) { @@ -354,25 +366,24 @@ public class QuickstepModelDelegate extends ModelDelegate { if (!mActive) { return; } - Context context = mApp.getContext(); - AppPredictionManager apm = context.getSystemService(AppPredictionManager.class); + AppPredictionManager apm = mContext.getSystemService(AppPredictionManager.class); if (apm == null) { return; } registerPredictor(mAllAppsState, apm.createAppPredictionSession( - new AppPredictionContext.Builder(context) + new AppPredictionContext.Builder(mContext) .setUiSurface("home") .setPredictedTargetCount(mIDP.numDatabaseAllAppsColumns) .build())); // TODO: get bundle - registerHotseatPredictor(apm, context); + registerHotseatPredictor(apm, mContext); registerWidgetsPredictor(apm.createAppPredictionSession( - new AppPredictionContext.Builder(context) + new AppPredictionContext.Builder(mContext) .setUiSurface("widgets") - .setExtras(getBundleForWidgetsOnWorkspace(context, mDataModel)) + .setExtras(getBundleForWidgetsOnWorkspace(mContext, mDataModel)) .setPredictedTargetCount(NUM_OF_RECOMMENDED_WIDGETS_PREDICATION) .build())); } @@ -383,12 +394,11 @@ public class QuickstepModelDelegate extends ModelDelegate { if (!mActive) { return; } - Context context = mApp.getContext(); - AppPredictionManager apm = context.getSystemService(AppPredictionManager.class); + AppPredictionManager apm = mContext.getSystemService(AppPredictionManager.class); if (apm == null) { return; } - registerHotseatPredictor(apm, context); + registerHotseatPredictor(apm, mContext); } private void registerHotseatPredictor(AppPredictionManager apm, Context context) { @@ -413,7 +423,7 @@ public class QuickstepModelDelegate extends ModelDelegate { // No diff, skip return; } - mApp.getModel().enqueueModelUpdateTask(new PredictionUpdateTask(state, targets)); + mModel.enqueueModelUpdateTask(new PredictionUpdateTask(state, targets)); } private void registerWidgetsPredictor(AppPredictor predictor) { @@ -424,7 +434,7 @@ public class QuickstepModelDelegate extends ModelDelegate { // No diff, skip return; } - mApp.getModel().enqueueModelUpdateTask( + mModel.enqueueModelUpdateTask( new WidgetsPredictionUpdateTask(mWidgetsRecommendationState, targets)); }); mWidgetsRecommendationState.predictor.requestPredictionUpdate(); @@ -536,7 +546,7 @@ public class QuickstepModelDelegate extends ModelDelegate { private static class WorkspaceItemFactory implements PersistedItemArray.ItemFactory { - private final LauncherAppState mAppState; + private final Context mContext; private final UserManagerState mUMS; private final PackageManagerHelper mPmHelper; private final Map mPinnedShortcuts; @@ -546,10 +556,11 @@ public class QuickstepModelDelegate extends ModelDelegate { private int mReadCount = 0; - protected WorkspaceItemFactory(LauncherAppState appState, UserManagerState ums, + protected WorkspaceItemFactory( + Context context, UserManagerState ums, PackageManagerHelper pmHelper, Map pinnedShortcuts, int maxCount, int container, CacheLookupFlag lookupFlag) { - mAppState = appState; + mContext = context; mUMS = ums; mPmHelper = pmHelper; mPinnedShortcuts = pinnedShortcuts; @@ -566,7 +577,7 @@ public class QuickstepModelDelegate extends ModelDelegate { } switch (itemType) { case ITEM_TYPE_APPLICATION: { - LauncherActivityInfo lai = mAppState.getContext() + LauncherActivityInfo lai = mContext .getSystemService(LauncherApps.class) .resolveActivity(intent, user); if (lai == null) { @@ -574,14 +585,15 @@ public class QuickstepModelDelegate extends ModelDelegate { } AppInfo info = new AppInfo( lai, - UserCache.INSTANCE.get(mAppState.getContext()).getUserInfo(user), - ApiWrapper.INSTANCE.get(mAppState.getContext()), + UserCache.INSTANCE.get(mContext).getUserInfo(user), + ApiWrapper.INSTANCE.get(mContext), mPmHelper, mUMS.isUserQuiet(user)); info.container = mContainer; - mAppState.getIconCache().getTitleAndIcon(info, lai, mLookupFlag); + LauncherAppState.getInstance(mContext).getIconCache() + .getTitleAndIcon(info, lai, mLookupFlag); mReadCount++; - return info.makeWorkspaceItem(mAppState.getContext()); + return info.makeWorkspaceItem(mContext); } case ITEM_TYPE_DEEP_SHORTCUT: { ShortcutKey key = ShortcutKey.fromIntent(intent, user); @@ -592,9 +604,9 @@ public class QuickstepModelDelegate extends ModelDelegate { if (si == null) { return null; } - WorkspaceItemInfo wii = new WorkspaceItemInfo(si, mAppState.getContext()); + WorkspaceItemInfo wii = new WorkspaceItemInfo(si, mContext); wii.container = mContainer; - mAppState.getIconCache().getShortcutIcon(wii, si); + LauncherAppState.getInstance(mContext).getIconCache().getShortcutIcon(wii, si); mReadCount++; return wii; } diff --git a/quickstep/src/com/android/launcher3/model/WidgetPredictionsRequester.java b/quickstep/src/com/android/launcher3/model/WidgetPredictionsRequester.java index 8c98babc0a..d3ac975052 100644 --- a/quickstep/src/com/android/launcher3/model/WidgetPredictionsRequester.java +++ b/quickstep/src/com/android/launcher3/model/WidgetPredictionsRequester.java @@ -203,7 +203,7 @@ public class WidgetPredictionsRequester { List items; if (enableCategorizedWidgetSuggestions()) { WidgetRecommendationCategoryProvider categoryProvider = - WidgetRecommendationCategoryProvider.newInstance(mContext); + new WidgetRecommendationCategoryProvider(); items = widgetItems.stream() .map(it -> new PendingAddWidgetInfo(it.widgetInfo, CONTAINER_WIDGETS_PREDICTION, categoryProvider.getWidgetRecommendationCategory(mContext, it))) diff --git a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java index 40e1c10d1c..aa81695d28 100644 --- a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java +++ b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java @@ -137,7 +137,7 @@ public final class WidgetsPredictionUpdateTask implements ModelUpdateTask { List items; if (enableCategorizedWidgetSuggestions()) { WidgetRecommendationCategoryProvider categoryProvider = - WidgetRecommendationCategoryProvider.newInstance(context); + new WidgetRecommendationCategoryProvider(); items = servicePredictedItems.stream() .map(it -> new PendingAddWidgetInfo(it.widgetInfo, CONTAINER_WIDGETS_PREDICTION, categoryProvider.getWidgetRecommendationCategory(context, it))) diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java index 594c99a9c8..58e54cffbf 100644 --- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java +++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java @@ -382,18 +382,15 @@ public class StatsLogCompatManager extends StatsLogManager { return; } - if (mItemInfo.container < 0 || !LauncherAppState.INSTANCE.executeIfCreated(app -> { - // Item is inside a collection, fetch collection info in a BG thread - // and then write to StatsLog. - app.getModel().enqueueModelUpdateTask((taskController, dataModel, apps) -> - write(event, applyOverwrites(mItemInfo.buildProto( - (CollectionInfo) dataModel.itemsIdMap.get(mItemInfo.container), - mContext)))); - })) { - // Write log on the model thread so that logs do not go out of order - // (for eg: drop comes after drag) - Executors.MODEL_EXECUTOR.execute( - () -> write(event, applyOverwrites(mItemInfo.buildProto(mContext)))); + // Item is inside a collection, fetch collection info in a BG thread + // and then write to StatsLog. + if (mItemInfo.container < 0) { + LauncherAppState.INSTANCE.get(mContext).getModel().enqueueModelUpdateTask( + (taskController, dataModel, apps) -> write(event, applyOverwrites( + mItemInfo.buildProto( + (CollectionInfo) dataModel.itemsIdMap + .get(mItemInfo.container), + mContext)))); } } diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/model/QuickstepModelDelegateTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/model/QuickstepModelDelegateTest.kt index 0005df6a6e..09c62aae1a 100644 --- a/quickstep/tests/multivalentTests/src/com/android/launcher3/model/QuickstepModelDelegateTest.kt +++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/model/QuickstepModelDelegateTest.kt @@ -19,7 +19,6 @@ import android.app.prediction.AppPredictor import android.app.prediction.AppTarget import android.app.prediction.AppTargetEvent import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.android.launcher3.LauncherAppState import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PREDICTION import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WALLPAPERS @@ -54,11 +53,17 @@ class QuickstepModelDelegateTest { fun setUp() { MockitoAnnotations.initMocks(this) modelHelper = LauncherModelHelper() - underTest = QuickstepModelDelegate(modelHelper.sandboxContext) + underTest = + QuickstepModelDelegate( + modelHelper.sandboxContext, + modelHelper.sandboxContext.appComponent.idp, + modelHelper.sandboxContext.appComponent.packageManagerHelper, + "", /* dbFileName */ + ) underTest.mAllAppsState.predictor = allAppsPredictor underTest.mHotseatState.predictor = hotseatPredictor underTest.mWidgetsRecommendationState.predictor = widgetRecommendationPredictor - underTest.mApp = LauncherAppState.getInstance(modelHelper.sandboxContext) + underTest.mModel = modelHelper.model underTest.mDataModel = BgDataModel() } diff --git a/res/values/config.xml b/res/values/config.xml index 07f97bc4b7..1a2ac9e928 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -67,7 +67,6 @@ - @@ -75,8 +74,6 @@ - - diff --git a/src/com/android/launcher3/AppFilter.java b/src/com/android/launcher3/AppFilter.java index 3db456cbdb..8ee7053ccb 100644 --- a/src/com/android/launcher3/AppFilter.java +++ b/src/com/android/launcher3/AppFilter.java @@ -3,10 +3,14 @@ package com.android.launcher3; import android.content.ComponentName; import android.content.Context; +import com.android.launcher3.dagger.ApplicationContext; + import java.util.Arrays; import java.util.Set; import java.util.stream.Collectors; +import javax.inject.Inject; + /** * Utility class to filter out components from various lists */ @@ -14,7 +18,8 @@ public class AppFilter { private final Set mFilteredComponents; - public AppFilter(Context context) { + @Inject + public AppFilter(@ApplicationContext Context context) { mFilteredComponents = Arrays.stream( context.getResources().getStringArray(R.array.filtered_components)) .map(ComponentName::unflattenFromString) diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java deleted file mode 100644 index bf2ad92e61..0000000000 --- a/src/com/android/launcher3/LauncherAppState.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (C) 2013 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; - -import android.content.Context; -import android.util.Log; - -import androidx.annotation.Nullable; - -import com.android.launcher3.dagger.LauncherComponentProvider; -import com.android.launcher3.graphics.ThemeManager; -import com.android.launcher3.icons.IconCache; -import com.android.launcher3.icons.IconProvider; -import com.android.launcher3.icons.LauncherIconProvider; -import com.android.launcher3.model.ModelInitializer; -import com.android.launcher3.model.WidgetsFilterDataProvider; -import com.android.launcher3.pm.InstallSessionHelper; -import com.android.launcher3.pm.UserCache; -import com.android.launcher3.util.MainThreadInitializedObject; -import com.android.launcher3.util.PackageManagerHelper; -import com.android.launcher3.util.Preconditions; -import com.android.launcher3.util.RunnableList; -import com.android.launcher3.util.SafeCloseable; -import com.android.launcher3.util.SettingsCache; -import com.android.launcher3.util.TraceHelper; -import com.android.launcher3.widget.custom.CustomWidgetManager; - -public class LauncherAppState implements SafeCloseable { - - public static final String TAG = "LauncherAppState"; - - // We do not need any synchronization for this variable as its only written on UI thread. - public static final MainThreadInitializedObject INSTANCE = - new MainThreadInitializedObject<>(LauncherAppState::new); - - private final Context mContext; - private final LauncherModel mModel; - private final LauncherIconProvider mIconProvider; - private final IconCache mIconCache; - private final InvariantDeviceProfile mInvariantDeviceProfile; - private boolean mIsSafeModeEnabled; - - private final RunnableList mOnTerminateCallback = new RunnableList(); - - public static LauncherAppState getInstance(Context context) { - return INSTANCE.get(context); - } - - public Context getContext() { - return mContext; - } - - @SuppressWarnings("NewApi") - public LauncherAppState(Context context) { - this(context, LauncherFiles.APP_ICONS_DB); - Log.v(Launcher.TAG, "LauncherAppState initiated"); - Preconditions.assertUIThread(); - - mIsSafeModeEnabled = TraceHelper.allowIpcs("isSafeMode", - () -> context.getPackageManager().isSafeMode()); - - ModelInitializer initializer = new ModelInitializer( - context, - LauncherComponentProvider.get(context).getIconPool(), - mIconCache, - mInvariantDeviceProfile, - ThemeManager.INSTANCE.get(context), - UserCache.INSTANCE.get(context), - SettingsCache.INSTANCE.get(context), - mIconProvider, - CustomWidgetManager.INSTANCE.get(context), - InstallSessionHelper.INSTANCE.get(context), - closeable -> mOnTerminateCallback.add(closeable::close) - ); - initializer.initialize(mModel); - } - - public LauncherAppState(Context context, @Nullable String iconCacheFileName) { - mContext = context; - - mInvariantDeviceProfile = InvariantDeviceProfile.INSTANCE.get(context); - mIconProvider = new LauncherIconProvider(context); - mIconCache = new IconCache(mContext, mInvariantDeviceProfile, - iconCacheFileName, mIconProvider); - 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); - } - - /** - * Call from Application.onTerminate(), which is not guaranteed to ever be called. - */ - @Override - public void close() { - mOnTerminateCallback.executeAllAndDestroy(); - } - - public IconProvider getIconProvider() { - return mIconProvider; - } - - public IconCache getIconCache() { - return mIconCache; - } - - public LauncherModel getModel() { - return mModel; - } - - public InvariantDeviceProfile getInvariantDeviceProfile() { - return mInvariantDeviceProfile; - } - - public boolean isSafeModeEnabled() { - return mIsSafeModeEnabled; - } - - /** - * Shorthand for {@link #getInvariantDeviceProfile()} - */ - public static InvariantDeviceProfile getIDP(Context context) { - return InvariantDeviceProfile.INSTANCE.get(context); - } -} diff --git a/src/com/android/launcher3/LauncherAppState.kt b/src/com/android/launcher3/LauncherAppState.kt new file mode 100644 index 0000000000..ff84c3c863 --- /dev/null +++ b/src/com/android/launcher3/LauncherAppState.kt @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2013 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 + +import android.content.Context +import com.android.launcher3.dagger.ApplicationContext +import com.android.launcher3.icons.IconCache +import com.android.launcher3.icons.LauncherIconProvider +import com.android.launcher3.util.DaggerSingletonObject +import javax.inject.Inject +import javax.inject.Named + +/** A collection of common dependencies used across Launcher */ +@Deprecated("Inject the specific targets directly instead of using LauncherAppState") +data class LauncherAppState +@Inject +constructor( + @ApplicationContext val context: Context, + val iconProvider: LauncherIconProvider, + val iconCache: IconCache, + val model: LauncherModel, + val invariantDeviceProfile: InvariantDeviceProfile, + @Named("SAFE_MODE") val isSafeModeEnabled: Boolean, +) { + + companion object { + + @JvmField var INSTANCE = DaggerSingletonObject { it.launcherAppState } + + @JvmStatic fun getInstance(context: Context) = INSTANCE[context] + + /** Shorthand for [.getInvariantDeviceProfile] */ + @JvmStatic fun getIDP(context: Context) = InvariantDeviceProfile.INSTANCE[context] + } +} diff --git a/src/com/android/launcher3/LauncherApplication.java b/src/com/android/launcher3/LauncherApplication.java index 678901b5b2..03eaeea178 100644 --- a/src/com/android/launcher3/LauncherApplication.java +++ b/src/com/android/launcher3/LauncherApplication.java @@ -20,6 +20,7 @@ import android.app.Application; import com.android.launcher3.dagger.DaggerLauncherAppComponent; import com.android.launcher3.dagger.LauncherAppComponent; import com.android.launcher3.dagger.LauncherBaseAppComponent; +import com.android.launcher3.util.TraceHelper; /** * Main application class for Launcher @@ -41,7 +42,8 @@ public class LauncherApplication extends Application { if (mAppComponent == null) { // Initialize the dagger component on demand as content providers can get // accessed before the Launcher application (b/36917845#comment4) - initDaggerComponent(DaggerLauncherAppComponent.builder()); + initDaggerComponent(DaggerLauncherAppComponent.builder() + .iconsDbName(LauncherFiles.APP_ICONS_DB)); } } } @@ -55,7 +57,11 @@ public class LauncherApplication extends Application { /** * Init with the desired dagger component. */ - public void initDaggerComponent(LauncherAppComponent.Builder componentBuilder) { - mAppComponent = componentBuilder.appContext(this).build(); + public void initDaggerComponent(LauncherBaseAppComponent.Builder componentBuilder) { + mAppComponent = componentBuilder + .appContext(this) + .setSafeModeEnabled(TraceHelper.allowIpcs( + "isSafeMode", () -> getPackageManager().isSafeMode())) + .build(); } } diff --git a/src/com/android/launcher3/LauncherModel.kt b/src/com/android/launcher3/LauncherModel.kt index 6e4276d3e9..33b63ce5d1 100644 --- a/src/com/android/launcher3/LauncherModel.kt +++ b/src/com/android/launcher3/LauncherModel.kt @@ -23,6 +23,8 @@ import android.text.TextUtils import android.util.Pair import androidx.annotation.WorkerThread import com.android.launcher3.celllayout.CellPosMapper +import com.android.launcher3.dagger.ApplicationContext +import com.android.launcher3.dagger.LauncherAppSingleton import com.android.launcher3.icons.IconCache import com.android.launcher3.model.AddWorkspaceItemsTask import com.android.launcher3.model.AllAppsList @@ -33,6 +35,7 @@ import com.android.launcher3.model.ItemInstallQueue import com.android.launcher3.model.LoaderTask import com.android.launcher3.model.ModelDbController import com.android.launcher3.model.ModelDelegate +import com.android.launcher3.model.ModelInitializer import com.android.launcher3.model.ModelLauncherCallbacks import com.android.launcher3.model.ModelTaskController import com.android.launcher3.model.ModelWriter @@ -45,31 +48,42 @@ import com.android.launcher3.model.data.ItemInfo import com.android.launcher3.model.data.WorkspaceItemInfo import com.android.launcher3.pm.UserCache import com.android.launcher3.shortcuts.ShortcutRequest +import com.android.launcher3.util.DaggerSingletonTracker import com.android.launcher3.util.Executors.MAIN_EXECUTOR import com.android.launcher3.util.Executors.MODEL_EXECUTOR -import com.android.launcher3.util.PackageManagerHelper import com.android.launcher3.util.PackageUserKey import com.android.launcher3.util.Preconditions import java.io.FileDescriptor import java.io.PrintWriter import java.util.concurrent.CancellationException import java.util.function.Consumer +import javax.inject.Inject +import javax.inject.Named +import javax.inject.Provider /** * Maintains in-memory state of the Launcher. It is expected that there should be only one * LauncherModel object held in a static. Also provide APIs for updating the database state for the * Launcher. */ -class LauncherModel( - private val context: Context, - private val mApp: LauncherAppState, +@LauncherAppSingleton +class LauncherModel +@Inject +constructor( + @ApplicationContext private val context: Context, + private val appProvider: Provider, private val iconCache: IconCache, - private val widgetsFilterDataProvider: WidgetsFilterDataProvider, + private val prefs: LauncherPrefs, + private val installQueue: ItemInstallQueue, appFilter: AppFilter, - mPmHelper: PackageManagerHelper, - isPrimaryInstance: Boolean, + @Named("ICONS_DB") dbFileName: String?, + initializer: ModelInitializer, + lifecycle: DaggerSingletonTracker, + val modelDelegate: ModelDelegate, ) { + private val widgetsFilterDataProvider = WidgetsFilterDataProvider.newInstance(context) + private val mCallbacksList = ArrayList(1) // < only access in worker thread > @@ -81,16 +95,6 @@ class LauncherModel( */ private val mBgDataModel = BgDataModel() - val modelDelegate: ModelDelegate = - ModelDelegate.newInstance( - context, - mApp, - mPmHelper, - mBgAllAppsList, - mBgDataModel, - isPrimaryInstance, - ) - val modelDbController = ModelDbController(context) private val mLock = Any() @@ -125,6 +129,14 @@ class LauncherModel( } } + init { + if (!dbFileName.isNullOrEmpty()) { + initializer.initialize(this) + } + lifecycle.addCloseable { destroy() } + modelDelegate.init(this, mBgAllAppsList, mBgDataModel) + } + fun newModelCallbacks() = ModelLauncherCallbacks(this::enqueueModelUpdateTask) /** Adds the provided items to the workspace. */ @@ -137,7 +149,7 @@ class LauncherModel( verifyChanges: Boolean, cellPosMapper: CellPosMapper?, owner: BgDataModel.Callbacks?, - ) = ModelWriter(mApp.context, this, mBgDataModel, verifyChanges, cellPosMapper, owner) + ) = ModelWriter(context, this, mBgDataModel, verifyChanges, cellPosMapper, owner) /** Returns the [WidgetsFilterDataProvider] that manages widget filters. */ fun getWidgetsFilterDataProvider(): WidgetsFilterDataProvider { @@ -202,7 +214,7 @@ class LauncherModel( UserCache.ACTION_PROFILE_UNLOCKED -> enqueueModelUpdateTask(UserLockStateChangedTask(user, true)) Intent.ACTION_MANAGED_PROFILE_REMOVED -> { - LauncherPrefs.get(mApp.context).put(LauncherPrefs.WORK_EDU_STEP, 0) + prefs.put(LauncherPrefs.WORK_EDU_STEP, 0) forceReload() } UserCache.ACTION_PROFILE_ADDED, @@ -233,6 +245,13 @@ class LauncherModel( rebindCallbacks() } + /** Reloads the model if it is already in use */ + fun reloadIfActive() { + val wasActive: Boolean + synchronized(mLock) { wasActive = mModelLoaded || stopLoader() } + if (wasActive) forceReload() + } + /** Rebinds all existing callbacks with already loaded model */ fun rebindCallbacks() { if (hasCallbacks()) { @@ -280,7 +299,7 @@ class LauncherModel( private fun startLoader(newCallbacks: Array): Boolean { // Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems - ItemInstallQueue.INSTANCE.get(context).pauseModelPush(ItemInstallQueue.FLAG_LOADER_RUNNING) + installQueue.pauseModelPush(ItemInstallQueue.FLAG_LOADER_RUNNING) synchronized(mLock) { // If there is already one running, tell it to stop. val wasRunning = stopLoader() @@ -292,7 +311,12 @@ class LauncherModel( callbacksList.forEach { MAIN_EXECUTOR.execute(it::clearPendingBinds) } val launcherBinder = - BaseLauncherBinder(mApp, mBgDataModel, mBgAllAppsList, callbacksList) + BaseLauncherBinder( + appProvider.get(), + mBgDataModel, + mBgAllAppsList, + callbacksList, + ) if (bindDirectly) { // Divide the set of loaded items into those that we are binding synchronously, // and everything else that is to be bound normally (asynchronously). @@ -306,7 +330,7 @@ class LauncherModel( } else { mLoaderTask = LoaderTask( - mApp, + appProvider.get(), mBgAllAppsList, mBgDataModel, this.modelDelegate, @@ -412,7 +436,7 @@ class LauncherModel( /** Called when the labels for the widgets has updated in the icon cache. */ fun onWidgetLabelsUpdated(updatedPackages: HashSet, user: UserHandle) { enqueueModelUpdateTask { taskController, dataModel, _ -> - dataModel.widgetsModel.onPackageIconsUpdated(updatedPackages, user, mApp) + dataModel.widgetsModel.onPackageIconsUpdated(updatedPackages, user, appProvider.get()) taskController.bindUpdatedWidgets(dataModel) } } @@ -435,7 +459,13 @@ class LauncherModel( return@execute } task.execute( - ModelTaskController(mApp, mBgDataModel, mBgAllAppsList, this, MAIN_EXECUTOR), + ModelTaskController( + appProvider.get(), + mBgDataModel, + mBgAllAppsList, + this, + MAIN_EXECUTOR, + ), mBgDataModel, mBgAllAppsList, ) @@ -496,8 +526,6 @@ class LauncherModel( } companion object { - private const val DEBUG_RECEIVER = false - const val TAG = "Launcher.Model" } } diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index a526b89532..03ecf14ef6 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -61,11 +61,10 @@ public class LauncherProvider extends ContentProvider { */ @Override public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { - LauncherAppState.INSTANCE.executeIfCreated(appState -> { - if (appState.getModel().isModelLoaded()) { - appState.getModel().dumpState("", fd, writer, args); - } - }); + LauncherModel model = LauncherAppState.INSTANCE.get(getContext()).getModel(); + if (model.isModelLoaded()) { + model.dumpState("", fd, writer, args); + } } @Override diff --git a/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java b/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java index 31d0da08db..be385d22c7 100644 --- a/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java +++ b/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java @@ -18,7 +18,10 @@ package com.android.launcher3.dagger; import android.content.Context; +import androidx.annotation.Nullable; + import com.android.launcher3.InvariantDeviceProfile; +import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherPrefs; import com.android.launcher3.graphics.ThemeManager; import com.android.launcher3.icons.LauncherIcons.IconPool; @@ -43,6 +46,8 @@ import com.android.launcher3.widget.custom.CustomWidgetManager; import dagger.BindsInstance; +import javax.inject.Named; + /** * Launcher base component for Dagger injection. * @@ -74,10 +79,13 @@ public interface LauncherBaseAppComponent { LockedUserState getLockedUserState(); InvariantDeviceProfile getIDP(); IconPool getIconPool(); + LauncherAppState getLauncherAppState(); /** Builder for LauncherBaseAppComponent. */ interface Builder { @BindsInstance Builder appContext(@ApplicationContext Context context); + @BindsInstance Builder iconsDbName(@Nullable @Named("ICONS_DB") String dbFileName); + @BindsInstance Builder setSafeModeEnabled(@Named("SAFE_MODE") boolean safeModeEnabled); LauncherBaseAppComponent build(); } } diff --git a/src/com/android/launcher3/dagger/LauncherComponentProvider.kt b/src/com/android/launcher3/dagger/LauncherComponentProvider.kt index 71e3354abf..61991494b4 100644 --- a/src/com/android/launcher3/dagger/LauncherComponentProvider.kt +++ b/src/com/android/launcher3/dagger/LauncherComponentProvider.kt @@ -39,8 +39,10 @@ object LauncherComponentProvider { // Create a new component return Holder( - DaggerLauncherAppComponent.builder().appContext(app).build() - as LauncherAppComponent, + DaggerLauncherAppComponent.builder() + .appContext(app) + .setSafeModeEnabled(true) + .build() as LauncherAppComponent, existingFilter, ) .apply { inflater.filter = this } diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java index 03f05820b7..332a6bbc36 100644 --- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java +++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java @@ -67,7 +67,6 @@ import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener; import com.android.launcher3.Hotseat; import com.android.launcher3.InsettableFrameLayout; import com.android.launcher3.InvariantDeviceProfile; -import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherPrefs; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.ProxyPrefs; @@ -146,8 +145,6 @@ public class LauncherPreviewRenderer extends ContextWrapper prefs.put(GRID_NAME, gridName); initDaggerComponent( DaggerLauncherPreviewRenderer_PreviewAppComponent.builder().bindPrefs(prefs)); - putObject(LauncherAppState.INSTANCE, - new LauncherAppState(this, null /* iconCacheFileName */)); } @Override diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java index 9f99e8f00a..fc0c1bbf57 100644 --- a/src/com/android/launcher3/icons/IconCache.java +++ b/src/com/android/launcher3/icons/IconCache.java @@ -51,6 +51,8 @@ import androidx.core.util.Pair; import com.android.launcher3.Flags; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.Utilities; +import com.android.launcher3.dagger.ApplicationContext; +import com.android.launcher3.dagger.LauncherAppSingleton; import com.android.launcher3.icons.cache.BaseIconCache; import com.android.launcher3.icons.cache.CacheLookupFlag; import com.android.launcher3.icons.cache.CachedObject; @@ -66,6 +68,7 @@ import com.android.launcher3.pm.InstallSessionHelper; import com.android.launcher3.pm.UserCache; import com.android.launcher3.util.CancellableTask; import com.android.launcher3.util.ComponentKey; +import com.android.launcher3.util.DaggerSingletonTracker; import com.android.launcher3.util.InstantAppResolver; import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.widget.WidgetSections; @@ -79,9 +82,13 @@ import java.util.function.Predicate; import java.util.function.Supplier; import java.util.stream.Stream; +import javax.inject.Inject; +import javax.inject.Named; + /** * Cache of application icons. Icons can be made from any thread. */ +@LauncherAppSingleton public class IconCache extends BaseIconCache { // Shortcut extra which can point to a packageName and can be used to indicate an alternate @@ -96,24 +103,39 @@ public class IconCache extends BaseIconCache { private final LauncherApps mLauncherApps; private final UserCache mUserManager; + private final InstallSessionHelper mInstallSessionHelper; private final InstantAppResolver mInstantAppResolver; private final CancellableTask mCancelledTask; + private final LauncherIcons.IconPool mIconPool; private final SparseArray mWidgetCategoryBitmapInfos; private int mPendingIconRequestCount = 0; - public IconCache(Context context, InvariantDeviceProfile idp, String dbFileName, - IconProvider iconProvider) { + @Inject + public IconCache( + @ApplicationContext Context context, + InvariantDeviceProfile idp, + @Nullable @Named("ICONS_DB") String dbFileName, + UserCache userCache, + LauncherIconProvider iconProvider, + InstallSessionHelper installSessionHelper, + LauncherIcons.IconPool iconPool, + DaggerSingletonTracker lifecycle) { super(context, dbFileName, MODEL_EXECUTOR.getLooper(), idp.fillResIconDpi, idp.iconBitmapSize, true /* inMemoryCache */, iconProvider); mLauncherApps = context.getSystemService(LauncherApps.class); - mUserManager = UserCache.INSTANCE.get(context); + mUserManager = userCache; + mInstallSessionHelper = installSessionHelper; + mIconPool = iconPool; + mInstantAppResolver = InstantAppResolver.newInstance(context); mWidgetCategoryBitmapInfos = new SparseArray<>(); mCancelledTask = new CancellableTask(() -> null, MAIN_EXECUTOR, c -> { }); mCancelledTask.cancel(); + + lifecycle.addCloseable(this::close); } @Override @@ -129,7 +151,7 @@ public class IconCache extends BaseIconCache { @NonNull @Override public BaseIconFactory getIconFactory() { - return LauncherIcons.obtain(context); + return mIconPool.obtain(); } /** @@ -279,8 +301,7 @@ public class IconCache extends BaseIconCache { String override = shortcutInfo.getExtras() == null ? null : shortcutInfo.getExtras().getString(EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE); if (!TextUtils.isEmpty(override) - && InstallSessionHelper.INSTANCE.get(context) - .isTrustedPackage(pkg, shortcutInfo.getUserHandle())) { + && mInstallSessionHelper.isTrustedPackage(pkg, shortcutInfo.getUserHandle())) { pkg = override; } else { // Try component based badge before trying the normal package badge @@ -536,7 +557,7 @@ public class IconCache extends BaseIconCache { return; } - try (LauncherIcons li = LauncherIcons.obtain(context)) { + try (LauncherIcons li = mIconPool.obtain()) { final BitmapInfo tempBitmap = li.createBadgedIconBitmap( context.getDrawable(widgetSection.mSectionDrawable), new BaseIconFactory.IconOptions()); diff --git a/src/com/android/launcher3/icons/LauncherIconProvider.java b/src/com/android/launcher3/icons/LauncherIconProvider.java index 836b7d1c00..72411984d7 100644 --- a/src/com/android/launcher3/icons/LauncherIconProvider.java +++ b/src/com/android/launcher3/icons/LauncherIconProvider.java @@ -30,6 +30,8 @@ import androidx.annotation.Nullable; import com.android.launcher3.R; import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.dagger.ApplicationContext; +import com.android.launcher3.dagger.LauncherAppSingleton; import com.android.launcher3.graphics.ShapeDelegate; import com.android.launcher3.graphics.ThemeManager; import com.android.launcher3.util.ApiWrapper; @@ -39,9 +41,12 @@ import org.xmlpull.v1.XmlPullParser; import java.util.Collections; import java.util.Map; +import javax.inject.Inject; + /** * Extension of {@link IconProvider} with support for overriding theme icons */ +@LauncherAppSingleton public class LauncherIconProvider extends IconProvider { private static final String TAG_ICON = "icon"; @@ -56,10 +61,14 @@ public class LauncherIconProvider extends IconProvider { private final ApiWrapper mApiWrapper; private final ThemeManager mThemeManager; - public LauncherIconProvider(Context context) { + @Inject + public LauncherIconProvider( + @ApplicationContext Context context, + ThemeManager themeManager, + ApiWrapper apiWrapper) { super(context); - mThemeManager = ThemeManager.INSTANCE.get(context); - mApiWrapper = ApiWrapper.INSTANCE.get(context); + mThemeManager = themeManager; + mApiWrapper = apiWrapper; setIconThemeSupported(mThemeManager.isMonoThemeEnabled()); } @@ -79,7 +88,7 @@ public class LauncherIconProvider extends IconProvider { @Override public void updateSystemState() { super.updateSystemState(); - mSystemState += "," + ThemeManager.INSTANCE.get(mContext).getIconState().toUniqueId(); + mSystemState += "," + mThemeManager.getIconState().toUniqueId(); } @Override diff --git a/src/com/android/launcher3/model/ModelDelegate.java b/src/com/android/launcher3/model/ModelDelegate.java index 5a2aef0ce9..52a2188ca2 100644 --- a/src/com/android/launcher3/model/ModelDelegate.java +++ b/src/com/android/launcher3/model/ModelDelegate.java @@ -23,62 +23,45 @@ import android.content.pm.ShortcutInfo; import androidx.annotation.NonNull; import androidx.annotation.WorkerThread; -import com.android.launcher3.LauncherAppState; -import com.android.launcher3.R; +import com.android.launcher3.LauncherModel; +import com.android.launcher3.dagger.ApplicationContext; import com.android.launcher3.shortcuts.ShortcutKey; -import com.android.launcher3.util.PackageManagerHelper; -import com.android.launcher3.util.ResourceBasedOverride; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.Map; +import javax.inject.Inject; + /** * Class to extend LauncherModel functionality to provide extra data */ -public class ModelDelegate implements ResourceBasedOverride { - - /** - * Creates and initializes a new instance of the delegate - */ - public static ModelDelegate newInstance( - Context context, LauncherAppState app, PackageManagerHelper pmHelper, - AllAppsList appsList, BgDataModel dataModel, boolean isPrimaryInstance) { - ModelDelegate delegate = Overrides.getObject( - ModelDelegate.class, context, R.string.model_delegate_class); - delegate.init(app, pmHelper, appsList, dataModel, isPrimaryInstance); - return delegate; - } +public class ModelDelegate { protected final Context mContext; - protected PackageManagerHelper mPmHelper; - protected LauncherAppState mApp; + protected LauncherModel mModel; protected AllAppsList mAppsList; protected BgDataModel mDataModel; - protected boolean mIsPrimaryInstance; - public ModelDelegate(Context context) { + @Inject + public ModelDelegate(@ApplicationContext Context context) { mContext = context; } /** * Initializes the object with the given params. */ - private void init(LauncherAppState app, PackageManagerHelper pmHelper, AllAppsList appsList, - BgDataModel dataModel, boolean isPrimaryInstance) { - this.mApp = app; - this.mPmHelper = pmHelper; + public void init(LauncherModel model, AllAppsList appsList, BgDataModel dataModel) { + this.mModel = model; this.mAppsList = appsList; this.mDataModel = dataModel; - this.mIsPrimaryInstance = isPrimaryInstance; } /** Called periodically to validate and update any data */ @WorkerThread public void validateData() { - if (hasShortcutsPermission(mApp.getContext()) - != mAppsList.hasShortcutHostPermission()) { - mApp.getModel().forceReload(); + if (hasShortcutsPermission(mContext) != mAppsList.hasShortcutHostPermission()) { + mModel.forceReload(); } } diff --git a/src/com/android/launcher3/model/ModelInitializer.kt b/src/com/android/launcher3/model/ModelInitializer.kt index 69a320a0e2..735a52aac6 100644 --- a/src/com/android/launcher3/model/ModelInitializer.kt +++ b/src/com/android/launcher3/model/ModelInitializer.kt @@ -38,18 +38,20 @@ import com.android.launcher3.icons.LauncherIcons.IconPool import com.android.launcher3.notification.NotificationListener import com.android.launcher3.pm.InstallSessionHelper import com.android.launcher3.pm.UserCache +import com.android.launcher3.util.DaggerSingletonTracker import com.android.launcher3.util.Executors.MODEL_EXECUTOR import com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR -import com.android.launcher3.util.SafeCloseable import com.android.launcher3.util.SettingsCache import com.android.launcher3.util.SettingsCache.NOTIFICATION_BADGING_URI import com.android.launcher3.util.SettingsCache.PRIVATE_SPACE_HIDE_WHEN_LOCKED_URI import com.android.launcher3.util.SimpleBroadcastReceiver import com.android.launcher3.widget.custom.CustomWidgetManager -import java.util.function.Consumer +import javax.inject.Inject /** Utility class for initializing all model callbacks */ -class ModelInitializer( +class ModelInitializer +@Inject +constructor( @ApplicationContext private val context: Context, private val iconPool: IconPool, private val iconCache: IconCache, @@ -60,7 +62,7 @@ class ModelInitializer( private val iconProvider: LauncherIconProvider, private val customWidgetManager: CustomWidgetManager, private val installSessionHelper: InstallSessionHelper, - private val closeActions: Consumer, + private val lifeCycle: DaggerSingletonTracker, ) { fun initialize(model: LauncherModel) { @@ -75,18 +77,18 @@ class ModelInitializer( if (modelChanged) refreshAndReloadLauncher() } idp.addOnChangeListener(idpChangeListener) - closeActions.accept { idp.removeOnChangeListener(idpChangeListener) } + lifeCycle.addCloseable { idp.removeOnChangeListener(idpChangeListener) } // Theme changes val themeChangeListener = ThemeChangeListener { refreshAndReloadLauncher() } themeManager.addChangeListener(themeChangeListener) - closeActions.accept { themeManager.removeChangeListener(themeChangeListener) } + lifeCycle.addCloseable { themeManager.removeChangeListener(themeChangeListener) } // System changes val modelCallbacks = model.newModelCallbacks() val launcherApps = context.getSystemService(LauncherApps::class.java)!! - launcherApps.registerCallback(modelCallbacks) - closeActions.accept { launcherApps.unregisterCallback(modelCallbacks) } + launcherApps.registerCallback(modelCallbacks, MODEL_EXECUTOR.handler) + lifeCycle.addCloseable { launcherApps.unregisterCallback(modelCallbacks) } if (Utilities.ATLEAST_V && Flags.enableSupportForArchiving()) { launcherApps.setArchiveCompatibility( @@ -101,23 +103,23 @@ class ModelInitializer( val dpUpdateReceiver = SimpleBroadcastReceiver(context, UI_HELPER_EXECUTOR) { model.reloadStringCache() } dpUpdateReceiver.register(ACTION_DEVICE_POLICY_RESOURCE_UPDATED) - closeActions.accept { dpUpdateReceiver.unregisterReceiverSafely() } + lifeCycle.addCloseable { dpUpdateReceiver.unregisterReceiverSafely() } // Development helper if (BuildConfig.IS_STUDIO_BUILD) { val reloadReceiver = SimpleBroadcastReceiver(context, UI_HELPER_EXECUTOR) { model.forceReload() } reloadReceiver.register(Context.RECEIVER_EXPORTED, ACTION_FORCE_RELOAD) - closeActions.accept { reloadReceiver.unregisterReceiverSafely() } + lifeCycle.addCloseable { reloadReceiver.unregisterReceiverSafely() } } // User changes - closeActions.accept(userCache.addUserEventListener(model::onUserEvent)) + lifeCycle.addCloseable(userCache.addUserEventListener(model::onUserEvent)) // Private space settings changes val psSettingsListener = SettingsCache.OnChangeListener { model.forceReload() } settingsCache.register(PRIVATE_SPACE_HIDE_WHEN_LOCKED_URI, psSettingsListener) - closeActions.accept { + lifeCycle.addCloseable { settingsCache.unregister(PRIVATE_SPACE_HIDE_WHEN_LOCKED_URI, psSettingsListener) } @@ -131,7 +133,7 @@ class ModelInitializer( } settingsCache.register(NOTIFICATION_BADGING_URI, notificationChanges) notificationChanges.onSettingsChanged(settingsCache.getValue(NOTIFICATION_BADGING_URI)) - closeActions.accept { + lifeCycle.addCloseable { settingsCache.unregister(NOTIFICATION_BADGING_URI, notificationChanges) } @@ -142,21 +144,21 @@ class ModelInitializer( if (LoaderTask.SMARTSPACE_ON_HOME_SCREEN == key) model.forceReload() } getPrefs(context).registerOnSharedPreferenceChangeListener(smartSpacePrefChanges) - closeActions.accept { + lifeCycle.addCloseable { getPrefs(context).unregisterOnSharedPreferenceChangeListener(smartSpacePrefChanges) } } // Custom widgets - closeActions.accept(customWidgetManager.addWidgetRefreshCallback(model::rebindCallbacks)) + lifeCycle.addCloseable(customWidgetManager.addWidgetRefreshCallback(model::rebindCallbacks)) // Icon changes - closeActions.accept( + lifeCycle.addCloseable( iconProvider.registerIconChangeListener(model::onAppIconChanged, MODEL_EXECUTOR.handler) ) // Install session changes - closeActions.accept(installSessionHelper.registerInstallTracker(modelCallbacks)) + lifeCycle.addCloseable(installSessionHelper.registerInstallTracker(modelCallbacks)) } companion object { diff --git a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java index ff40f30bba..b60b8cc670 100644 --- a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java +++ b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java @@ -22,6 +22,7 @@ import android.content.Context; import android.content.Intent; import android.os.Process; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.launcher3.Flags; @@ -44,6 +45,7 @@ public abstract class ItemInfoWithIcon extends ItemInfo { /** * The bitmap for the application icon */ + @NonNull public BitmapInfo bitmap = BitmapInfo.LOW_RES_INFO; /** diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java index 34c9117f1f..5ad8af2296 100644 --- a/src/com/android/launcher3/provider/RestoreDbTask.java +++ b/src/com/android/launcher3/provider/RestoreDbTask.java @@ -535,7 +535,7 @@ public class RestoreDbTask { } logFavoritesTable(controller.getDb(), "launcher db after remap widget ids", null, null); - LauncherAppState.INSTANCE.executeIfCreated(app -> app.getModel().forceReload()); + LauncherAppState.INSTANCE.get(context).getModel().reloadIfActive(); } private static void logDatabaseWidgetInfo(ModelDbController controller) { diff --git a/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProvider.java b/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProvider.java index f8dc6b0bb2..8f34fe3572 100644 --- a/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProvider.java +++ b/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProvider.java @@ -35,19 +35,7 @@ import com.android.launcher3.util.ResourceBasedOverride; * own implementation. Method {@code getWidgetRecommendationCategory} is called per widget to get * the category.

*/ -public class WidgetRecommendationCategoryProvider implements ResourceBasedOverride { - private static final String TAG = "WidgetRecommendationCategoryProvider"; - - /** - * Retrieve instance of this object that can be overridden in runtime based on the build - * variant of the application. - */ - public static WidgetRecommendationCategoryProvider newInstance(Context context) { - Preconditions.assertWorkerThread(); - return Overrides.getObject( - WidgetRecommendationCategoryProvider.class, context.getApplicationContext(), - R.string.widget_recommendation_category_provider_class); - } +public class WidgetRecommendationCategoryProvider { /** * Returns a {@link WidgetRecommendationCategory} for the provided widget item that can be used diff --git a/tests/multivalentTests/src/com/android/launcher3/icons/IconCacheTest.java b/tests/multivalentTests/src/com/android/launcher3/icons/IconCacheTest.java index 9026748f60..0aaf4d7c40 100644 --- a/tests/multivalentTests/src/com/android/launcher3/icons/IconCacheTest.java +++ b/tests/multivalentTests/src/com/android/launcher3/icons/IconCacheTest.java @@ -56,7 +56,7 @@ import androidx.annotation.Nullable; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.launcher3.InvariantDeviceProfile; +import com.android.launcher3.LauncherAppState; import com.android.launcher3.icons.cache.CachingLogic; import com.android.launcher3.icons.cache.IconCacheUpdateHandler; import com.android.launcher3.icons.cache.LauncherActivityCachingLogic; @@ -69,11 +69,13 @@ import com.android.launcher3.shortcuts.ShortcutKey; import com.android.launcher3.util.ApplicationInfoWrapper; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.PackageUserKey; +import com.android.launcher3.util.SandboxApplication; import com.google.common.truth.Truth; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -85,20 +87,18 @@ import java.util.Set; @RunWith(AndroidJUnit4.class) public class IconCacheTest { - private Context mContext; + @Rule public SandboxApplication mContext = new SandboxApplication(); + private IconCache mIconCache; private ComponentName mMyComponent; @Before public void setup() { - mContext = getInstrumentation().getTargetContext(); mMyComponent = new ComponentName(mContext, SettingsActivity.class); // In memory icon cache - mIconCache = new IconCache(mContext, - InvariantDeviceProfile.INSTANCE.get(mContext), null, - new LauncherIconProvider(mContext)); + mIconCache = LauncherAppState.getInstance(mContext).getIconCache(); } @After diff --git a/tests/multivalentTests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt index ce0468270d..08b8f81c11 100644 --- a/tests/multivalentTests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt +++ b/tests/multivalentTests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt @@ -155,7 +155,7 @@ class AddWorkspaceItemsTaskTest : AbstractWorkspaceModelTest() { private fun verifyItemSpaceFinderCall(nonEmptyScreenIds: List, numberOfExpectedCall: Int) { verify(mWorkspaceItemSpaceFinder, times(numberOfExpectedCall)) .findSpaceForItem( - same(mAppState), + eq(mAppState), same(mModelHelper.bgDataModel), eq(IntArray.wrap(*nonEmptyScreenIds.toIntArray())), eq(IntArray()), diff --git a/tests/multivalentTests/src/com/android/launcher3/model/LoaderCursorTest.java b/tests/multivalentTests/src/com/android/launcher3/model/LoaderCursorTest.java index 11047fb8d4..ad408185b2 100644 --- a/tests/multivalentTests/src/com/android/launcher3/model/LoaderCursorTest.java +++ b/tests/multivalentTests/src/com/android/launcher3/model/LoaderCursorTest.java @@ -70,6 +70,7 @@ import androidx.test.filters.SmallTest; import com.android.launcher3.Flags; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.LauncherAppState; +import com.android.launcher3.icons.BitmapInfo; import com.android.launcher3.icons.LauncherIcons; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; @@ -258,7 +259,6 @@ public class LoaderCursorTest { initCursor(ITEM_TYPE_APPLICATION, "title"); assertTrue(mLoaderCursor.moveToNext()); WorkspaceItemInfo itemInfo = new WorkspaceItemInfo(); - itemInfo.bitmap = null; itemInfo.runtimeStatusFlags |= FLAG_ARCHIVED; Bitmap expectedBitmap = LauncherIcons.obtain(mContext) .createIconBitmap(decodeByteArray(sTestBlob, 0, sTestBlob.length)) @@ -289,7 +289,7 @@ public class LoaderCursorTest { initCursor(ITEM_TYPE_APPLICATION, "title"); assertTrue(mLoaderCursor.moveToNext()); WorkspaceItemInfo itemInfo = new WorkspaceItemInfo(); - itemInfo.bitmap = null; + BitmapInfo original = itemInfo.bitmap; itemInfo.runtimeStatusFlags |= FLAG_ARCHIVED; Intent intent = new Intent(); intent.setComponent(new ComponentName("package", "class")); @@ -297,7 +297,7 @@ public class LoaderCursorTest { // When mLoaderCursor.loadWorkspaceTitleAndIcon(false, false, itemInfo); // Then - assertThat(itemInfo.bitmap).isNull(); + assertThat(itemInfo.bitmap).isEqualTo(original); } @Test diff --git a/tests/multivalentTests/src/com/android/launcher3/util/LauncherModelHelper.java b/tests/multivalentTests/src/com/android/launcher3/util/LauncherModelHelper.java index 09b9a3bf7c..cee5559c39 100644 --- a/tests/multivalentTests/src/com/android/launcher3/util/LauncherModelHelper.java +++ b/tests/multivalentTests/src/com/android/launcher3/util/LauncherModelHelper.java @@ -21,8 +21,8 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; -import static com.android.launcher3.util.TestUtil.runOnExecutorSync; import static com.android.launcher3.util.TestUtil.grantWriteSecurePermission; +import static com.android.launcher3.util.TestUtil.runOnExecutorSync; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; @@ -51,6 +51,7 @@ import androidx.test.core.app.ApplicationProvider; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherModel; +import com.android.launcher3.dagger.LauncherBaseAppComponent; import com.android.launcher3.model.BgDataModel; import com.android.launcher3.model.BgDataModel.Callbacks; import com.android.launcher3.model.ModelDbController; @@ -266,14 +267,6 @@ public class LauncherModelHelper { mDbDir = new File(getCacheDir(), UUID.randomUUID().toString()); } - @Override - public T createObject(MainThreadInitializedObject object) { - if (object == LauncherAppState.INSTANCE) { - return (T) new LauncherAppState(this, null /* iconCacheFileName */); - } - return super.createObject(object); - } - @Override public File getDatabasePath(String name) { if (!mDbDir.exists()) { @@ -342,5 +335,10 @@ public class LauncherModelHelper { } return success; } + + @Override + public void initDaggerComponent(LauncherBaseAppComponent.Builder componentBuilder) { + super.initDaggerComponent(componentBuilder.iconsDbName(null)); + } } } diff --git a/tests/multivalentTests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt b/tests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt similarity index 97% rename from tests/multivalentTests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt rename to tests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt index 15accbda2a..c956395503 100644 --- a/tests/multivalentTests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt +++ b/tests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 The Android Open Source Project + * 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. @@ -20,6 +20,7 @@ import android.R import android.os.Process import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn import com.android.launcher3.LauncherAppState import com.android.launcher3.LauncherPrefs import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_FOLDER @@ -61,11 +62,11 @@ import org.junit.rules.TestRule import org.junit.runner.Description import org.junit.runner.RunWith import org.junit.runners.model.Statement +import org.mockito.MockitoAnnotations import org.mockito.kotlin.any import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.doReturn import org.mockito.kotlin.eq -import org.mockito.kotlin.spy import org.mockito.kotlin.verify import org.mockito.kotlin.whenever @@ -85,6 +86,7 @@ class PreviewItemManagerTest { @Before fun setup() { + MockitoAnnotations.initMocks(this) modelHelper = LauncherModelHelper() context = modelHelper.sandboxContext context.initDaggerComponent(DaggerPreviewItemManagerTestComponent.builder()) @@ -93,10 +95,8 @@ class PreviewItemManagerTest { } folderIcon = FolderIcon(ActivityContextWrapper(context)) - val app = spy(LauncherAppState.getInstance(context)) - iconCache = spy(app.iconCache) - doReturn(iconCache).whenever(app).iconCache - context.putObject(LauncherAppState.INSTANCE, app) + iconCache = LauncherAppState.INSTANCE[context].iconCache + spyOn(iconCache) doReturn(null).whenever(iconCache).updateIconInBackground(any(), any()) previewItemManager = PreviewItemManager(folderIcon) diff --git a/tests/src/com/android/launcher3/model/LoaderTaskTest.kt b/tests/src/com/android/launcher3/model/LoaderTaskTest.kt index 8f64e841b8..582cf3c082 100644 --- a/tests/src/com/android/launcher3/model/LoaderTaskTest.kt +++ b/tests/src/com/android/launcher3/model/LoaderTaskTest.kt @@ -14,6 +14,7 @@ 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.dx.mockito.inline.extended.ExtendedMockito.spyOn import com.android.launcher3.Flags import com.android.launcher3.LauncherAppState import com.android.launcher3.LauncherModel @@ -67,7 +68,6 @@ import org.mockito.kotlin.any import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock -import org.mockito.kotlin.spy import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import org.mockito.quality.Strictness @@ -92,7 +92,6 @@ class LoaderTaskTest { ) private lateinit var mockitoSession: MockitoSession - @Mock private lateinit var app: LauncherAppState @Mock private lateinit var bgAllAppsList: AllAppsList @Mock private lateinit var modelDelegate: ModelDelegate @Mock private lateinit var launcherBinder: BaseLauncherBinder @@ -108,6 +107,9 @@ class LoaderTaskTest { @get:Rule val setFlagsRule = SetFlagsRule() + private val app: LauncherAppState + get() = context.appComponent.launcherAppState + @Before fun setup() { MockitoAnnotations.initMocks(this) @@ -118,32 +120,28 @@ class LoaderTaskTest { .strictness(Strictness.LENIENT) .mockStatic(FirstScreenBroadcastHelper::class.java) .startMocking() - val idp = - context.appComponent.idp.apply { - numRows = 5 - numColumns = 6 - numDatabaseHotseatIcons = 5 - } - context.putObject(LauncherAppState.INSTANCE, app) - doReturn(TestViewHelpers.findWidgetProvider(false)) .`when`(context.spyService(AppWidgetManager::class.java)) .getAppWidgetInfo(any()) - `when`(app.context).thenReturn(context) - `when`(app.model).thenReturn(launcherModel) `when`(launcherModel.beginLoader(any())).thenReturn(transaction) - `when`(app.iconCache).thenReturn(iconCache) `when`(launcherModel.modelDbController) .thenReturn(FactitiousDbController(context, INSERTION_STATEMENT_FILE)) - `when`(app.invariantDeviceProfile).thenReturn(idp) `when`(launcherBinder.newIdleLock(any())).thenReturn(idleLock) `when`(idleLock.awaitLocked(1000)).thenReturn(false) `when`(iconCache.getUpdateHandler()).thenReturn(iconCacheUpdateHandler) `when`(widgetsFilterDataProvider.getDefaultWidgetsFilter()).thenReturn(Predicate { true }) context.initDaggerComponent( - DaggerLoaderTaskTest_TestComponent.builder().bindUserCache(userCache) + DaggerLoaderTaskTest_TestComponent.builder() + .bindUserCache(userCache) + .bindIconCache(iconCache) + .bindLauncherModel(launcherModel) ) + context.appComponent.idp.apply { + numRows = 5 + numColumns = 6 + numDatabaseHotseatIcons = 5 + } TestUtil.grantWriteSecurePermission() } @@ -281,8 +279,8 @@ class LoaderTaskTest { @EnableFlags(Flags.FLAG_ENABLE_FIRST_SCREEN_BROADCAST_ARCHIVING_EXTRAS) fun `When broadcast flag on and is restore and secure setting off then send new broadcast`() { // Given - val spyContext = spy(context) - `when`(app.context).thenReturn(spyContext) + spyOn(context) + val spyContext = context whenever( FirstScreenBroadcastHelper.createModelsForFirstScreenBroadcast( any(), @@ -357,8 +355,8 @@ class LoaderTaskTest { @EnableFlags(Flags.FLAG_ENABLE_FIRST_SCREEN_BROADCAST_ARCHIVING_EXTRAS) fun `When not a restore then installed item broadcast not sent`() { // Given - val spyContext = spy(context) - `when`(app.context).thenReturn(spyContext) + spyOn(context) + val spyContext = context whenever( FirstScreenBroadcastHelper.createModelsForFirstScreenBroadcast( any(), @@ -398,8 +396,8 @@ class LoaderTaskTest { @DisableFlags(Flags.FLAG_ENABLE_FIRST_SCREEN_BROADCAST_ARCHIVING_EXTRAS) fun `When broadcast flag off then installed item broadcast not sent`() { // Given - val spyContext = spy(context) - `when`(app.context).thenReturn(spyContext) + spyOn(context) + val spyContext = context whenever( FirstScreenBroadcastHelper.createModelsForFirstScreenBroadcast( any(), @@ -444,8 +442,8 @@ class LoaderTaskTest { @EnableFlags(Flags.FLAG_ENABLE_FIRST_SCREEN_BROADCAST_ARCHIVING_EXTRAS) fun `When failsafe secure setting on then installed item broadcast not sent`() { // Given - val spyContext = spy(context) - `when`(app.context).thenReturn(spyContext) + spyOn(context) + val spyContext = context whenever( FirstScreenBroadcastHelper.createModelsForFirstScreenBroadcast( any(), @@ -644,6 +642,10 @@ class LoaderTaskTest { interface Builder : LauncherAppComponent.Builder { @BindsInstance fun bindUserCache(userCache: UserCache): Builder + @BindsInstance fun bindLauncherModel(model: LauncherModel): Builder + + @BindsInstance fun bindIconCache(iconCache: IconCache): Builder + override fun build(): TestComponent } }