Loading src/com/android/launcher3/BaseDraggingActivity.java +1 −6 Original line number Diff line number Diff line Loading @@ -39,7 +39,6 @@ import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener; import com.android.launcher3.util.DisplayController.Info; import com.android.launcher3.util.OnColorHintListener; import com.android.launcher3.util.Themes; import com.android.launcher3.util.TraceHelper; import com.android.launcher3.util.WallpaperColorHints; import com.android.launcher3.util.WindowBounds; Loading @@ -55,16 +54,12 @@ public abstract class BaseDraggingActivity extends BaseActivity public static final Object AUTO_CANCEL_ACTION_MODE = new Object(); private ActionMode mCurrentActionMode; protected boolean mIsSafeModeEnabled; private int mThemeRes = R.style.AppTheme; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mIsSafeModeEnabled = TraceHelper.allowIpcs("isSafeMode", () -> getPackageManager().isSafeMode()); DisplayController.INSTANCE.get(this).addChangeListener(this); // Update theme Loading Loading @@ -183,6 +178,6 @@ public abstract class BaseDraggingActivity extends BaseActivity @Override public boolean isAppBlockedForSafeMode() { return mIsSafeModeEnabled; return LauncherAppState.getInstance(this).isSafeModeEnabled(); } } src/com/android/launcher3/Launcher.java +18 −144 Original line number Diff line number Diff line Loading @@ -89,7 +89,6 @@ import static com.android.launcher3.logging.StatsLogManager.StatsLatencyLogger.L import static com.android.launcher3.logging.StatsLogManager.StatsLatencyLogger.LatencyType.WARM; import static com.android.launcher3.model.ItemInstallQueue.FLAG_ACTIVITY_PAUSED; import static com.android.launcher3.model.ItemInstallQueue.FLAG_DRAG_AND_DROP; import static com.android.launcher3.model.data.LauncherAppWidgetInfo.CUSTOM_WIDGET_ID; import static com.android.launcher3.popup.SystemShortcut.APP_INFO; import static com.android.launcher3.popup.SystemShortcut.INSTALL; import static com.android.launcher3.popup.SystemShortcut.WIDGETS; Loading Loading @@ -196,7 +195,6 @@ import com.android.launcher3.model.BgDataModel.Callbacks; import com.android.launcher3.model.ItemInstallQueue; import com.android.launcher3.model.ModelWriter; import com.android.launcher3.model.StringCache; import com.android.launcher3.model.WidgetsModel; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.FolderInfo; import com.android.launcher3.model.data.ItemInfo; Loading @@ -208,7 +206,6 @@ import com.android.launcher3.pm.PinRequestHelper; import com.android.launcher3.popup.ArrowPopup; import com.android.launcher3.popup.PopupDataProvider; import com.android.launcher3.popup.SystemShortcut; import com.android.launcher3.qsb.QsbContainerView; import com.android.launcher3.statemanager.StateManager; import com.android.launcher3.statemanager.StateManager.StateHandler; import com.android.launcher3.statemanager.StatefulActivity; Loading Loading @@ -252,6 +249,8 @@ import com.android.launcher3.widget.PendingAddShortcutInfo; import com.android.launcher3.widget.PendingAddWidgetInfo; import com.android.launcher3.widget.PendingAppWidgetHostView; import com.android.launcher3.widget.WidgetAddFlowHandler; import com.android.launcher3.widget.WidgetInflater; import com.android.launcher3.widget.WidgetInflater.InflationResult; import com.android.launcher3.widget.WidgetManagerHelper; import com.android.launcher3.widget.custom.CustomWidgetManager; import com.android.launcher3.widget.model.WidgetsListBaseEntry; Loading Loading @@ -333,6 +332,7 @@ public class Launcher extends StatefulActivity<LauncherState> private WidgetManagerHelper mAppWidgetManager; private LauncherWidgetHolder mAppWidgetHolder; private WidgetInflater mWidgetInflater; private final int[] mTmpAddItemCellCoordinates = new int[2]; Loading Loading @@ -517,6 +517,7 @@ public class Launcher extends StatefulActivity<LauncherState> setupViews(); mAppWidgetManager = new WidgetManagerHelper(this); mWidgetInflater = new WidgetInflater(this); mAppWidgetHolder = createAppWidgetHolder(); mAppWidgetHolder.startListening(); mAppWidgetHolder.addProviderChangeListener(() -> refreshAndBindWidgetsForPackageUser(null)); Loading Loading @@ -2309,64 +2310,11 @@ public class Launcher extends StatefulActivity<LauncherState> private View inflateAppWidget(LauncherAppWidgetInfo item, @Nullable LauncherRestoreEventLogger restoreEventLogger) { if (item.hasOptionFlag(LauncherAppWidgetInfo.OPTION_SEARCH_WIDGET)) { item.providerName = QsbContainerView.getSearchComponentName(this); if (item.providerName == null) { getModelWriter().deleteItemFromDatabase(item, "search widget removed because search component cannot be found"); return null; } } final AppWidgetHostView view; if (mIsSafeModeEnabled) { view = new PendingAppWidgetHostView(this, item, null, true); prepareAppWidget(view, item); return view; } TraceHelper.INSTANCE.beginSection("BIND_WIDGET_id=" + item.appWidgetId); try { final LauncherAppWidgetProviderInfo appWidgetInfo; String removalReason = ""; if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) { // If the provider is not ready, bind as a pending widget. appWidgetInfo = null; removalReason = "the provider isn't ready."; } else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) { // The widget id is not valid. Try to find the widget based on the provider info. appWidgetInfo = mAppWidgetManager.findProvider(item.providerName, item.user); if (appWidgetInfo == null) { if (WidgetsModel.GO_DISABLE_WIDGETS) { removalReason = "widgets are disabled on go device."; } else { removalReason = "WidgetManagerHelper cannot find a provider from provider info."; } } } else { appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(item.appWidgetId, item.getTargetComponent()); if (appWidgetInfo == null) { if (item.appWidgetId <= CUSTOM_WIDGET_ID) { removalReason = "CustomWidgetManager cannot find provider from that widget id."; } else { removalReason = "AppWidgetManager cannot find provider for that widget id." + " It could be because AppWidgetService is not available, or the" + " appWidgetId has not been bound to a the provider yet, or you" + " don't have access to that appWidgetId."; } } } // If the provider is ready, but the width is not yet restored, try to restore it. if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) && (item.restoreStatus != LauncherAppWidgetInfo.RESTORE_COMPLETED)) { if (appWidgetInfo == null) { getModelWriter().deleteItemFromDatabase(item, "Removing restored widget: id=" + item.appWidgetId + " belongs to component " + item.providerName + " user " + item.user + ", as the provider is null and " + removalReason); InflationResult inflationResult = mWidgetInflater.inflateAppWidget(item); if (inflationResult.getType() == WidgetInflater.TYPE_DELETE) { getModelWriter().deleteItemFromDatabase(item, inflationResult.getReason()); if (restoreEventLogger != null) { restoreEventLogger.logSingleFavoritesItemRestoreFailed( ITEM_TYPE_APPWIDGET, RESTORE_ERROR_BIND_FAILURE); Loading @@ -2374,92 +2322,18 @@ public class Launcher extends StatefulActivity<LauncherState> return null; } // If we do not have a valid id, try to bind an id. if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) { if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_ALLOCATED)) { // Id has not been allocated yet. Allocate a new id. item.appWidgetId = mAppWidgetHolder.allocateAppWidgetId(); item.restoreStatus |= LauncherAppWidgetInfo.FLAG_ID_ALLOCATED; // Also try to bind the widget. If the bind fails, the user will be shown // a click to setup UI, which will ask for the bind permission. PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(appWidgetInfo, item.sourceContainer); pendingInfo.spanX = item.spanX; pendingInfo.spanY = item.spanY; pendingInfo.minSpanX = item.minSpanX; pendingInfo.minSpanY = item.minSpanY; Bundle options = pendingInfo.getDefaultSizeOptions(this); boolean isDirectConfig = item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG); if (isDirectConfig && item.bindOptions != null) { Bundle newOptions = item.bindOptions.getExtras(); if (options != null) { newOptions.putAll(options); } options = newOptions; } boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed( item.appWidgetId, appWidgetInfo, options); // We tried to bind once. If we were not able to bind, we would need to // go through the permission dialog, which means we cannot skip the config // activity. item.bindOptions = null; item.restoreStatus &= ~LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG; // Bind succeeded if (success) { // If the widget has a configure activity, it is still needs to set it // up, otherwise the widget is ready to go. item.restoreStatus = (appWidgetInfo.configure == null) || isDirectConfig ? LauncherAppWidgetInfo.RESTORE_COMPLETED : LauncherAppWidgetInfo.FLAG_UI_NOT_READY; } getModelWriter().updateItemInDatabase(item); } } else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_UI_NOT_READY) && (appWidgetInfo.configure == null)) { // The widget was marked as UI not ready, but there is no configure activity to // update the UI. item.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED; getModelWriter().updateItemInDatabase(item); } else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_UI_NOT_READY) && appWidgetInfo.configure != null) { if (mAppWidgetManager.isAppWidgetRestored(item.appWidgetId)) { item.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED; if (inflationResult.isUpdate()) { getModelWriter().updateItemInDatabase(item); } } } if (item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) { // Verify that we own the widget if (appWidgetInfo == null) { FileLog.e(TAG, "Removing invalid widget: id=" + item.appWidgetId); getModelWriter().deleteWidgetInfo(item, getAppWidgetHolder(), removalReason); if (restoreEventLogger != null) { restoreEventLogger.logSingleFavoritesItemRestoreFailed( ITEM_TYPE_APPWIDGET, RESTORE_ERROR_BIND_FAILURE); } return null; } item.minSpanX = appWidgetInfo.minSpanX; item.minSpanY = appWidgetInfo.minSpanY; view = mAppWidgetHolder.createView(item.appWidgetId, appWidgetInfo); } else { view = new PendingAppWidgetHostView(this, item, appWidgetInfo, false); } AppWidgetHostView view = inflationResult.getType() == WidgetInflater.TYPE_PENDING ? new PendingAppWidgetHostView(this, item, inflationResult.getWidgetInfo()) : mAppWidgetHolder.createView( item.appWidgetId, inflationResult.getWidgetInfo()); prepareAppWidget(view, item); return view; } finally { TraceHelper.INSTANCE.endSection(); } return view; } /** Loading src/com/android/launcher3/LauncherAppState.java +9 −0 Original line number Diff line number Diff line Loading @@ -56,6 +56,7 @@ import com.android.launcher3.util.SafeCloseable; import com.android.launcher3.util.SettingsCache; import com.android.launcher3.util.SimpleBroadcastReceiver; import com.android.launcher3.util.Themes; import com.android.launcher3.util.TraceHelper; import com.android.launcher3.widget.custom.CustomWidgetManager; public class LauncherAppState implements SafeCloseable { Loading @@ -71,6 +72,8 @@ public class LauncherAppState implements SafeCloseable { 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(final Context context) { Loading @@ -90,6 +93,8 @@ public class LauncherAppState implements SafeCloseable { Log.v(Launcher.TAG, "LauncherAppState initiated"); Preconditions.assertUIThread(); mIsSafeModeEnabled = TraceHelper.allowIpcs("isSafeMode", () -> context.getPackageManager().isSafeMode()); mInvariantDeviceProfile.addOnChangeListener(modelPropertiesChanged -> { if (modelPropertiesChanged) { refreshAndReloadLauncher(); Loading Loading @@ -224,6 +229,10 @@ public class LauncherAppState implements SafeCloseable { return mInvariantDeviceProfile; } public boolean isSafeModeEnabled() { return mIsSafeModeEnabled; } /** * Shorthand for {@link #getInvariantDeviceProfile()} */ Loading src/com/android/launcher3/widget/PendingAppWidgetHostView.java +5 −6 Original line number Diff line number Diff line Loading @@ -88,8 +88,8 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView private Layout mSetupTextLayout; public PendingAppWidgetHostView(Context context, LauncherAppWidgetInfo info, @Nullable LauncherAppWidgetProviderInfo appWidget, boolean disabledForSafeMode) { this(context, info, disabledForSafeMode, appWidget, @Nullable LauncherAppWidgetProviderInfo appWidget) { this(context, info, appWidget, context.getResources().getText(R.string.gadget_complete_setup_text)); super.updateAppWidget(null); Loading @@ -107,7 +107,7 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView public PendingAppWidgetHostView( Context context, int appWidgetId, @NonNull LauncherAppWidgetProviderInfo appWidget) { this(context, new LauncherAppWidgetInfo(appWidgetId, appWidget.provider), false, this(context, new LauncherAppWidgetInfo(appWidgetId, appWidget.provider), appWidget, appWidget.label); getBackground().mutate().setAlpha(DEFERRED_ALPHA); Loading @@ -117,14 +117,13 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView } private PendingAppWidgetHostView(Context context, LauncherAppWidgetInfo info, boolean disabledForSafeMode, LauncherAppWidgetProviderInfo appwidget, CharSequence label) { LauncherAppWidgetProviderInfo appwidget, CharSequence label) { super(new ContextThemeWrapper(context, R.style.WidgetContainerTheme)); mAppwidget = appwidget; mInfo = info; mStartState = info.restoreStatus; mDisabledForSafeMode = disabledForSafeMode; mDisabledForSafeMode = LauncherAppState.getInstance(context).isSafeModeEnabled(); mLabel = label; mPaint = new TextPaint(); Loading src/com/android/launcher3/widget/WidgetInflater.kt 0 → 100644 +196 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.launcher3.widget import android.content.Context import com.android.launcher3.Launcher import com.android.launcher3.LauncherAppState import com.android.launcher3.logging.FileLog import com.android.launcher3.model.WidgetsModel import com.android.launcher3.model.data.LauncherAppWidgetInfo import com.android.launcher3.qsb.QsbContainerView /** Utility class for handling widget inflation taking into account all the restore state updates */ class WidgetInflater(private val context: Context) { private val widgetHelper = WidgetManagerHelper(context) fun inflateAppWidget( item: LauncherAppWidgetInfo, ): InflationResult { if (item.hasOptionFlag(LauncherAppWidgetInfo.OPTION_SEARCH_WIDGET)) { item.providerName = QsbContainerView.getSearchComponentName(context) if (item.providerName == null) { return InflationResult( TYPE_DELETE, reason = "search widget removed because search component cannot be found" ) } } if (LauncherAppState.INSTANCE.get(context).isSafeModeEnabled) { return InflationResult(TYPE_PENDING) } val appWidgetInfo: LauncherAppWidgetProviderInfo? var removalReason = "" if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) { // If the provider is not ready, bind as a pending widget. appWidgetInfo = null removalReason = "the provider isn't ready." } else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) { // The widget id is not valid. Try to find the widget based on the provider info. appWidgetInfo = widgetHelper.findProvider(item.providerName, item.user) if (appWidgetInfo == null) { if (WidgetsModel.GO_DISABLE_WIDGETS) { removalReason = "widgets are disabled on go device." } else { removalReason = "WidgetManagerHelper cannot find a provider from provider info." } } } else { appWidgetInfo = widgetHelper.getLauncherAppWidgetInfo(item.appWidgetId, item.targetComponent) if (appWidgetInfo == null) { if (item.appWidgetId <= LauncherAppWidgetInfo.CUSTOM_WIDGET_ID) { removalReason = "CustomWidgetManager cannot find provider from that widget id." } else { removalReason = ("AppWidgetManager cannot find provider for that widget id." + " It could be because AppWidgetService is not available, or the" + " appWidgetId has not been bound to a the provider yet, or you" + " don't have access to that appWidgetId.") } } } var update = false // If the provider is ready, but the width is not yet restored, try to restore it. if ( !item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) && item.restoreStatus != LauncherAppWidgetInfo.RESTORE_COMPLETED ) { if (appWidgetInfo == null) { return InflationResult( type = TYPE_DELETE, reason = "Removing restored widget: id=${item.appWidgetId} belongs to component" + " ${item.providerName} user ${item.user}" + ", as the provider is null and $removalReason" ) } // If we do not have a valid id, try to bind an id. if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) { if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_ALLOCATED)) { // Id has not been allocated yet. Allocate a new id. LauncherWidgetHolder.newInstance(context).let { item.appWidgetId = it.allocateAppWidgetId() it.destroy() } item.restoreStatus = item.restoreStatus or LauncherAppWidgetInfo.FLAG_ID_ALLOCATED // Also try to bind the widget. If the bind fails, the user will be shown // a click to setup UI, which will ask for the bind permission. val pendingInfo = PendingAddWidgetInfo(appWidgetInfo, item.sourceContainer) pendingInfo.spanX = item.spanX pendingInfo.spanY = item.spanY pendingInfo.minSpanX = item.minSpanX pendingInfo.minSpanY = item.minSpanY var options = pendingInfo.getDefaultSizeOptions(context) val isDirectConfig = item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG) if (isDirectConfig && item.bindOptions != null) { val newOptions = item.bindOptions.extras if (options != null) { newOptions!!.putAll(options) } options = newOptions } val success = widgetHelper.bindAppWidgetIdIfAllowed( item.appWidgetId, appWidgetInfo, options ) // We tried to bind once. If we were not able to bind, we would need to // go through the permission dialog, which means we cannot skip the config // activity. item.bindOptions = null item.restoreStatus = item.restoreStatus and LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG.inv() // Bind succeeded if (success) { // If the widget has a configure activity, it is still needs to set it // up, otherwise the widget is ready to go. item.restoreStatus = if ((appWidgetInfo.configure == null) || isDirectConfig) LauncherAppWidgetInfo.RESTORE_COMPLETED else LauncherAppWidgetInfo.FLAG_UI_NOT_READY } update = true } } else if ( (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_UI_NOT_READY) && (appWidgetInfo.configure == null)) ) { // The widget was marked as UI not ready, but there is no configure activity to // update the UI. item.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED update = true } else if ( (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_UI_NOT_READY) && appWidgetInfo.configure != null) ) { if (widgetHelper.isAppWidgetRestored(item.appWidgetId)) { item.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED update = true } } } if (item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) { // Verify that we own the widget if (appWidgetInfo == null) { FileLog.e(Launcher.TAG, "Removing invalid widget: id=" + item.appWidgetId) return InflationResult(TYPE_DELETE, reason = removalReason) } item.minSpanX = appWidgetInfo.minSpanX item.minSpanY = appWidgetInfo.minSpanY return InflationResult(TYPE_REAL, isUpdate = update, widgetInfo = appWidgetInfo) } else { return InflationResult(TYPE_PENDING, isUpdate = update, widgetInfo = appWidgetInfo) } } data class InflationResult( val type: Int, val reason: String? = null, val isUpdate: Boolean = false, val widgetInfo: LauncherAppWidgetProviderInfo? = null ) companion object { const val TYPE_DELETE = 0 const val TYPE_PENDING = 1 const val TYPE_REAL = 2 } } Loading
src/com/android/launcher3/BaseDraggingActivity.java +1 −6 Original line number Diff line number Diff line Loading @@ -39,7 +39,6 @@ import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener; import com.android.launcher3.util.DisplayController.Info; import com.android.launcher3.util.OnColorHintListener; import com.android.launcher3.util.Themes; import com.android.launcher3.util.TraceHelper; import com.android.launcher3.util.WallpaperColorHints; import com.android.launcher3.util.WindowBounds; Loading @@ -55,16 +54,12 @@ public abstract class BaseDraggingActivity extends BaseActivity public static final Object AUTO_CANCEL_ACTION_MODE = new Object(); private ActionMode mCurrentActionMode; protected boolean mIsSafeModeEnabled; private int mThemeRes = R.style.AppTheme; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mIsSafeModeEnabled = TraceHelper.allowIpcs("isSafeMode", () -> getPackageManager().isSafeMode()); DisplayController.INSTANCE.get(this).addChangeListener(this); // Update theme Loading Loading @@ -183,6 +178,6 @@ public abstract class BaseDraggingActivity extends BaseActivity @Override public boolean isAppBlockedForSafeMode() { return mIsSafeModeEnabled; return LauncherAppState.getInstance(this).isSafeModeEnabled(); } }
src/com/android/launcher3/Launcher.java +18 −144 Original line number Diff line number Diff line Loading @@ -89,7 +89,6 @@ import static com.android.launcher3.logging.StatsLogManager.StatsLatencyLogger.L import static com.android.launcher3.logging.StatsLogManager.StatsLatencyLogger.LatencyType.WARM; import static com.android.launcher3.model.ItemInstallQueue.FLAG_ACTIVITY_PAUSED; import static com.android.launcher3.model.ItemInstallQueue.FLAG_DRAG_AND_DROP; import static com.android.launcher3.model.data.LauncherAppWidgetInfo.CUSTOM_WIDGET_ID; import static com.android.launcher3.popup.SystemShortcut.APP_INFO; import static com.android.launcher3.popup.SystemShortcut.INSTALL; import static com.android.launcher3.popup.SystemShortcut.WIDGETS; Loading Loading @@ -196,7 +195,6 @@ import com.android.launcher3.model.BgDataModel.Callbacks; import com.android.launcher3.model.ItemInstallQueue; import com.android.launcher3.model.ModelWriter; import com.android.launcher3.model.StringCache; import com.android.launcher3.model.WidgetsModel; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.FolderInfo; import com.android.launcher3.model.data.ItemInfo; Loading @@ -208,7 +206,6 @@ import com.android.launcher3.pm.PinRequestHelper; import com.android.launcher3.popup.ArrowPopup; import com.android.launcher3.popup.PopupDataProvider; import com.android.launcher3.popup.SystemShortcut; import com.android.launcher3.qsb.QsbContainerView; import com.android.launcher3.statemanager.StateManager; import com.android.launcher3.statemanager.StateManager.StateHandler; import com.android.launcher3.statemanager.StatefulActivity; Loading Loading @@ -252,6 +249,8 @@ import com.android.launcher3.widget.PendingAddShortcutInfo; import com.android.launcher3.widget.PendingAddWidgetInfo; import com.android.launcher3.widget.PendingAppWidgetHostView; import com.android.launcher3.widget.WidgetAddFlowHandler; import com.android.launcher3.widget.WidgetInflater; import com.android.launcher3.widget.WidgetInflater.InflationResult; import com.android.launcher3.widget.WidgetManagerHelper; import com.android.launcher3.widget.custom.CustomWidgetManager; import com.android.launcher3.widget.model.WidgetsListBaseEntry; Loading Loading @@ -333,6 +332,7 @@ public class Launcher extends StatefulActivity<LauncherState> private WidgetManagerHelper mAppWidgetManager; private LauncherWidgetHolder mAppWidgetHolder; private WidgetInflater mWidgetInflater; private final int[] mTmpAddItemCellCoordinates = new int[2]; Loading Loading @@ -517,6 +517,7 @@ public class Launcher extends StatefulActivity<LauncherState> setupViews(); mAppWidgetManager = new WidgetManagerHelper(this); mWidgetInflater = new WidgetInflater(this); mAppWidgetHolder = createAppWidgetHolder(); mAppWidgetHolder.startListening(); mAppWidgetHolder.addProviderChangeListener(() -> refreshAndBindWidgetsForPackageUser(null)); Loading Loading @@ -2309,64 +2310,11 @@ public class Launcher extends StatefulActivity<LauncherState> private View inflateAppWidget(LauncherAppWidgetInfo item, @Nullable LauncherRestoreEventLogger restoreEventLogger) { if (item.hasOptionFlag(LauncherAppWidgetInfo.OPTION_SEARCH_WIDGET)) { item.providerName = QsbContainerView.getSearchComponentName(this); if (item.providerName == null) { getModelWriter().deleteItemFromDatabase(item, "search widget removed because search component cannot be found"); return null; } } final AppWidgetHostView view; if (mIsSafeModeEnabled) { view = new PendingAppWidgetHostView(this, item, null, true); prepareAppWidget(view, item); return view; } TraceHelper.INSTANCE.beginSection("BIND_WIDGET_id=" + item.appWidgetId); try { final LauncherAppWidgetProviderInfo appWidgetInfo; String removalReason = ""; if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) { // If the provider is not ready, bind as a pending widget. appWidgetInfo = null; removalReason = "the provider isn't ready."; } else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) { // The widget id is not valid. Try to find the widget based on the provider info. appWidgetInfo = mAppWidgetManager.findProvider(item.providerName, item.user); if (appWidgetInfo == null) { if (WidgetsModel.GO_DISABLE_WIDGETS) { removalReason = "widgets are disabled on go device."; } else { removalReason = "WidgetManagerHelper cannot find a provider from provider info."; } } } else { appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(item.appWidgetId, item.getTargetComponent()); if (appWidgetInfo == null) { if (item.appWidgetId <= CUSTOM_WIDGET_ID) { removalReason = "CustomWidgetManager cannot find provider from that widget id."; } else { removalReason = "AppWidgetManager cannot find provider for that widget id." + " It could be because AppWidgetService is not available, or the" + " appWidgetId has not been bound to a the provider yet, or you" + " don't have access to that appWidgetId."; } } } // If the provider is ready, but the width is not yet restored, try to restore it. if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) && (item.restoreStatus != LauncherAppWidgetInfo.RESTORE_COMPLETED)) { if (appWidgetInfo == null) { getModelWriter().deleteItemFromDatabase(item, "Removing restored widget: id=" + item.appWidgetId + " belongs to component " + item.providerName + " user " + item.user + ", as the provider is null and " + removalReason); InflationResult inflationResult = mWidgetInflater.inflateAppWidget(item); if (inflationResult.getType() == WidgetInflater.TYPE_DELETE) { getModelWriter().deleteItemFromDatabase(item, inflationResult.getReason()); if (restoreEventLogger != null) { restoreEventLogger.logSingleFavoritesItemRestoreFailed( ITEM_TYPE_APPWIDGET, RESTORE_ERROR_BIND_FAILURE); Loading @@ -2374,92 +2322,18 @@ public class Launcher extends StatefulActivity<LauncherState> return null; } // If we do not have a valid id, try to bind an id. if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) { if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_ALLOCATED)) { // Id has not been allocated yet. Allocate a new id. item.appWidgetId = mAppWidgetHolder.allocateAppWidgetId(); item.restoreStatus |= LauncherAppWidgetInfo.FLAG_ID_ALLOCATED; // Also try to bind the widget. If the bind fails, the user will be shown // a click to setup UI, which will ask for the bind permission. PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(appWidgetInfo, item.sourceContainer); pendingInfo.spanX = item.spanX; pendingInfo.spanY = item.spanY; pendingInfo.minSpanX = item.minSpanX; pendingInfo.minSpanY = item.minSpanY; Bundle options = pendingInfo.getDefaultSizeOptions(this); boolean isDirectConfig = item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG); if (isDirectConfig && item.bindOptions != null) { Bundle newOptions = item.bindOptions.getExtras(); if (options != null) { newOptions.putAll(options); } options = newOptions; } boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed( item.appWidgetId, appWidgetInfo, options); // We tried to bind once. If we were not able to bind, we would need to // go through the permission dialog, which means we cannot skip the config // activity. item.bindOptions = null; item.restoreStatus &= ~LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG; // Bind succeeded if (success) { // If the widget has a configure activity, it is still needs to set it // up, otherwise the widget is ready to go. item.restoreStatus = (appWidgetInfo.configure == null) || isDirectConfig ? LauncherAppWidgetInfo.RESTORE_COMPLETED : LauncherAppWidgetInfo.FLAG_UI_NOT_READY; } getModelWriter().updateItemInDatabase(item); } } else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_UI_NOT_READY) && (appWidgetInfo.configure == null)) { // The widget was marked as UI not ready, but there is no configure activity to // update the UI. item.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED; getModelWriter().updateItemInDatabase(item); } else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_UI_NOT_READY) && appWidgetInfo.configure != null) { if (mAppWidgetManager.isAppWidgetRestored(item.appWidgetId)) { item.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED; if (inflationResult.isUpdate()) { getModelWriter().updateItemInDatabase(item); } } } if (item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) { // Verify that we own the widget if (appWidgetInfo == null) { FileLog.e(TAG, "Removing invalid widget: id=" + item.appWidgetId); getModelWriter().deleteWidgetInfo(item, getAppWidgetHolder(), removalReason); if (restoreEventLogger != null) { restoreEventLogger.logSingleFavoritesItemRestoreFailed( ITEM_TYPE_APPWIDGET, RESTORE_ERROR_BIND_FAILURE); } return null; } item.minSpanX = appWidgetInfo.minSpanX; item.minSpanY = appWidgetInfo.minSpanY; view = mAppWidgetHolder.createView(item.appWidgetId, appWidgetInfo); } else { view = new PendingAppWidgetHostView(this, item, appWidgetInfo, false); } AppWidgetHostView view = inflationResult.getType() == WidgetInflater.TYPE_PENDING ? new PendingAppWidgetHostView(this, item, inflationResult.getWidgetInfo()) : mAppWidgetHolder.createView( item.appWidgetId, inflationResult.getWidgetInfo()); prepareAppWidget(view, item); return view; } finally { TraceHelper.INSTANCE.endSection(); } return view; } /** Loading
src/com/android/launcher3/LauncherAppState.java +9 −0 Original line number Diff line number Diff line Loading @@ -56,6 +56,7 @@ import com.android.launcher3.util.SafeCloseable; import com.android.launcher3.util.SettingsCache; import com.android.launcher3.util.SimpleBroadcastReceiver; import com.android.launcher3.util.Themes; import com.android.launcher3.util.TraceHelper; import com.android.launcher3.widget.custom.CustomWidgetManager; public class LauncherAppState implements SafeCloseable { Loading @@ -71,6 +72,8 @@ public class LauncherAppState implements SafeCloseable { 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(final Context context) { Loading @@ -90,6 +93,8 @@ public class LauncherAppState implements SafeCloseable { Log.v(Launcher.TAG, "LauncherAppState initiated"); Preconditions.assertUIThread(); mIsSafeModeEnabled = TraceHelper.allowIpcs("isSafeMode", () -> context.getPackageManager().isSafeMode()); mInvariantDeviceProfile.addOnChangeListener(modelPropertiesChanged -> { if (modelPropertiesChanged) { refreshAndReloadLauncher(); Loading Loading @@ -224,6 +229,10 @@ public class LauncherAppState implements SafeCloseable { return mInvariantDeviceProfile; } public boolean isSafeModeEnabled() { return mIsSafeModeEnabled; } /** * Shorthand for {@link #getInvariantDeviceProfile()} */ Loading
src/com/android/launcher3/widget/PendingAppWidgetHostView.java +5 −6 Original line number Diff line number Diff line Loading @@ -88,8 +88,8 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView private Layout mSetupTextLayout; public PendingAppWidgetHostView(Context context, LauncherAppWidgetInfo info, @Nullable LauncherAppWidgetProviderInfo appWidget, boolean disabledForSafeMode) { this(context, info, disabledForSafeMode, appWidget, @Nullable LauncherAppWidgetProviderInfo appWidget) { this(context, info, appWidget, context.getResources().getText(R.string.gadget_complete_setup_text)); super.updateAppWidget(null); Loading @@ -107,7 +107,7 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView public PendingAppWidgetHostView( Context context, int appWidgetId, @NonNull LauncherAppWidgetProviderInfo appWidget) { this(context, new LauncherAppWidgetInfo(appWidgetId, appWidget.provider), false, this(context, new LauncherAppWidgetInfo(appWidgetId, appWidget.provider), appWidget, appWidget.label); getBackground().mutate().setAlpha(DEFERRED_ALPHA); Loading @@ -117,14 +117,13 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView } private PendingAppWidgetHostView(Context context, LauncherAppWidgetInfo info, boolean disabledForSafeMode, LauncherAppWidgetProviderInfo appwidget, CharSequence label) { LauncherAppWidgetProviderInfo appwidget, CharSequence label) { super(new ContextThemeWrapper(context, R.style.WidgetContainerTheme)); mAppwidget = appwidget; mInfo = info; mStartState = info.restoreStatus; mDisabledForSafeMode = disabledForSafeMode; mDisabledForSafeMode = LauncherAppState.getInstance(context).isSafeModeEnabled(); mLabel = label; mPaint = new TextPaint(); Loading
src/com/android/launcher3/widget/WidgetInflater.kt 0 → 100644 +196 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.launcher3.widget import android.content.Context import com.android.launcher3.Launcher import com.android.launcher3.LauncherAppState import com.android.launcher3.logging.FileLog import com.android.launcher3.model.WidgetsModel import com.android.launcher3.model.data.LauncherAppWidgetInfo import com.android.launcher3.qsb.QsbContainerView /** Utility class for handling widget inflation taking into account all the restore state updates */ class WidgetInflater(private val context: Context) { private val widgetHelper = WidgetManagerHelper(context) fun inflateAppWidget( item: LauncherAppWidgetInfo, ): InflationResult { if (item.hasOptionFlag(LauncherAppWidgetInfo.OPTION_SEARCH_WIDGET)) { item.providerName = QsbContainerView.getSearchComponentName(context) if (item.providerName == null) { return InflationResult( TYPE_DELETE, reason = "search widget removed because search component cannot be found" ) } } if (LauncherAppState.INSTANCE.get(context).isSafeModeEnabled) { return InflationResult(TYPE_PENDING) } val appWidgetInfo: LauncherAppWidgetProviderInfo? var removalReason = "" if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) { // If the provider is not ready, bind as a pending widget. appWidgetInfo = null removalReason = "the provider isn't ready." } else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) { // The widget id is not valid. Try to find the widget based on the provider info. appWidgetInfo = widgetHelper.findProvider(item.providerName, item.user) if (appWidgetInfo == null) { if (WidgetsModel.GO_DISABLE_WIDGETS) { removalReason = "widgets are disabled on go device." } else { removalReason = "WidgetManagerHelper cannot find a provider from provider info." } } } else { appWidgetInfo = widgetHelper.getLauncherAppWidgetInfo(item.appWidgetId, item.targetComponent) if (appWidgetInfo == null) { if (item.appWidgetId <= LauncherAppWidgetInfo.CUSTOM_WIDGET_ID) { removalReason = "CustomWidgetManager cannot find provider from that widget id." } else { removalReason = ("AppWidgetManager cannot find provider for that widget id." + " It could be because AppWidgetService is not available, or the" + " appWidgetId has not been bound to a the provider yet, or you" + " don't have access to that appWidgetId.") } } } var update = false // If the provider is ready, but the width is not yet restored, try to restore it. if ( !item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) && item.restoreStatus != LauncherAppWidgetInfo.RESTORE_COMPLETED ) { if (appWidgetInfo == null) { return InflationResult( type = TYPE_DELETE, reason = "Removing restored widget: id=${item.appWidgetId} belongs to component" + " ${item.providerName} user ${item.user}" + ", as the provider is null and $removalReason" ) } // If we do not have a valid id, try to bind an id. if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) { if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_ALLOCATED)) { // Id has not been allocated yet. Allocate a new id. LauncherWidgetHolder.newInstance(context).let { item.appWidgetId = it.allocateAppWidgetId() it.destroy() } item.restoreStatus = item.restoreStatus or LauncherAppWidgetInfo.FLAG_ID_ALLOCATED // Also try to bind the widget. If the bind fails, the user will be shown // a click to setup UI, which will ask for the bind permission. val pendingInfo = PendingAddWidgetInfo(appWidgetInfo, item.sourceContainer) pendingInfo.spanX = item.spanX pendingInfo.spanY = item.spanY pendingInfo.minSpanX = item.minSpanX pendingInfo.minSpanY = item.minSpanY var options = pendingInfo.getDefaultSizeOptions(context) val isDirectConfig = item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG) if (isDirectConfig && item.bindOptions != null) { val newOptions = item.bindOptions.extras if (options != null) { newOptions!!.putAll(options) } options = newOptions } val success = widgetHelper.bindAppWidgetIdIfAllowed( item.appWidgetId, appWidgetInfo, options ) // We tried to bind once. If we were not able to bind, we would need to // go through the permission dialog, which means we cannot skip the config // activity. item.bindOptions = null item.restoreStatus = item.restoreStatus and LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG.inv() // Bind succeeded if (success) { // If the widget has a configure activity, it is still needs to set it // up, otherwise the widget is ready to go. item.restoreStatus = if ((appWidgetInfo.configure == null) || isDirectConfig) LauncherAppWidgetInfo.RESTORE_COMPLETED else LauncherAppWidgetInfo.FLAG_UI_NOT_READY } update = true } } else if ( (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_UI_NOT_READY) && (appWidgetInfo.configure == null)) ) { // The widget was marked as UI not ready, but there is no configure activity to // update the UI. item.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED update = true } else if ( (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_UI_NOT_READY) && appWidgetInfo.configure != null) ) { if (widgetHelper.isAppWidgetRestored(item.appWidgetId)) { item.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED update = true } } } if (item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) { // Verify that we own the widget if (appWidgetInfo == null) { FileLog.e(Launcher.TAG, "Removing invalid widget: id=" + item.appWidgetId) return InflationResult(TYPE_DELETE, reason = removalReason) } item.minSpanX = appWidgetInfo.minSpanX item.minSpanY = appWidgetInfo.minSpanY return InflationResult(TYPE_REAL, isUpdate = update, widgetInfo = appWidgetInfo) } else { return InflationResult(TYPE_PENDING, isUpdate = update, widgetInfo = appWidgetInfo) } } data class InflationResult( val type: Int, val reason: String? = null, val isUpdate: Boolean = false, val widgetInfo: LauncherAppWidgetProviderInfo? = null ) companion object { const val TYPE_DELETE = 0 const val TYPE_PENDING = 1 const val TYPE_REAL = 2 } }