diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto index fb47b0aad6..475b5bead0 100644 --- a/protos/launcher_atom.proto +++ b/protos/launcher_atom.proto @@ -162,6 +162,7 @@ message Widget { optional int32 app_widget_id = 3; optional string package_name = 4; // only populated during snapshot if from workspace optional string component_name = 5; // only populated during snapshot if from workspace + optional int32 widget_features = 6; } // Tasks handled by PackageManager diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java index 3b26108aab..719cb0a032 100644 --- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java +++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java @@ -354,6 +354,10 @@ public class StatsLogCompatManager extends StatsLogManager { } private static int getCardinality(LauncherAtom.ItemInfo info) { + // TODO(b/187734511): Implement a unified solution for 1x1 widgets in folders/hotseat. + if (info.getItemCase().equals(LauncherAtom.ItemInfo.ItemCase.WIDGET)) { + return info.getWidget().getWidgetFeatures(); + } switch (info.getContainerInfo().getContainerCase()) { case PREDICTED_HOTSEAT_CONTAINER: return info.getContainerInfo().getPredictedHotseatContainer().getCardinality(); diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 7b67807697..16ffd7a42c 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -1307,8 +1307,15 @@ public class Launcher extends StatefulActivity implements Launche appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(appWidgetId); } + if (hostView == null) { + // Perform actual inflation because we're live + hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo); + } + LauncherAppWidgetInfo launcherInfo; - launcherInfo = new LauncherAppWidgetInfo(appWidgetId, appWidgetInfo.provider); + launcherInfo = + new LauncherAppWidgetInfo( + appWidgetId, appWidgetInfo.provider, appWidgetInfo, hostView); launcherInfo.spanX = itemInfo.spanX; launcherInfo.spanY = itemInfo.spanY; launcherInfo.minSpanX = itemInfo.minSpanX; @@ -1318,10 +1325,6 @@ public class Launcher extends StatefulActivity implements Launche getModelWriter().addItemToDatabase(launcherInfo, itemInfo.container, itemInfo.screenId, itemInfo.cellX, itemInfo.cellY); - if (hostView == null) { - // Perform actual inflation because we're live - hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo); - } hostView.setVisibility(View.VISIBLE); prepareAppWidget(hostView, launcherInfo); mWorkspace.addInScreen(hostView, launcherInfo); diff --git a/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java b/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java index c04b7f0ffa..003b3bdc22 100644 --- a/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java +++ b/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java @@ -16,9 +16,12 @@ package com.android.launcher3.model.data; +import static com.android.launcher3.Utilities.ATLEAST_S; + import android.appwidget.AppWidgetHostView; import android.content.ComponentName; import android.content.Intent; +import android.content.res.Resources; import android.os.Process; import androidx.annotation.Nullable; @@ -26,7 +29,10 @@ import androidx.annotation.Nullable; import com.android.launcher3.AppWidgetResizeFrame; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherSettings; +import com.android.launcher3.logger.LauncherAtom; import com.android.launcher3.util.ContentWriter; +import com.android.launcher3.widget.LauncherAppWidgetHostView; +import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; /** * Represents a widget (either instantiated or about to be) in the Launcher. @@ -80,6 +86,18 @@ public class LauncherAppWidgetInfo extends ItemInfo { */ public static final int CUSTOM_WIDGET_ID = -100; + /** + * Flags for recording all the features that a widget has enabled. + * @see widgetFeatures + */ + public static final int FEATURE_RECONFIGURABLE = 1; + public static final int FEATURE_OPTIONAL_CONFIGURATION = 1 << 1; + public static final int FEATURE_PREVIEW_LAYOUT = 1 << 2; + public static final int FEATURE_TARGET_CELL_SIZE = 1 << 3; + public static final int FEATURE_MIN_SIZE = 1 << 4; + public static final int FEATURE_MAX_SIZE = 1 << 5; + public static final int FEATURE_ROUNDED_CORNERS = 1 << 6; + /** * Identifier for this widget when talking with * {@link android.appwidget.AppWidgetManager} for updates. @@ -113,6 +131,12 @@ public class LauncherAppWidgetInfo extends ItemInfo { */ public PackageItemInfo pendingItemInfo; + /** + * Contains a binary representation indicating which widget features are enabled. This value is + * -1 if widget features could not be identified. + */ + private int widgetFeatures; + private boolean mHasNotifiedInitialWidgetSizeChanged; public LauncherAppWidgetInfo(int appWidgetId, ComponentName providerName) { @@ -129,11 +153,18 @@ public class LauncherAppWidgetInfo extends ItemInfo { // to indicate that they should be calculated based on the layout and minWidth/minHeight spanX = -1; spanY = -1; + widgetFeatures = -1; // We only support app widgets on current user. user = Process.myUserHandle(); restoreStatus = RESTORE_COMPLETED; } + public LauncherAppWidgetInfo(int appWidgetId, ComponentName providerName, + LauncherAppWidgetProviderInfo providerInfo, AppWidgetHostView hostView) { + this(appWidgetId, providerName); + widgetFeatures = computeWidgetFeatures(providerInfo, hostView); + } + /** Used for testing **/ public LauncherAppWidgetInfo() { itemType = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET; @@ -194,4 +225,41 @@ public class LauncherAppWidgetInfo extends ItemInfo { public final boolean hasOptionFlag(int option) { return (options & option) != 0; } + + @SuppressWarnings("NewApi") + private static int computeWidgetFeatures( + LauncherAppWidgetProviderInfo providerInfo, AppWidgetHostView hostView) { + int widgetFeatures = 0; + if (providerInfo.isReconfigurable()) { + widgetFeatures |= FEATURE_RECONFIGURABLE; + } + if (providerInfo.isConfigurationOptional()) { + widgetFeatures |= FEATURE_OPTIONAL_CONFIGURATION; + } + if (ATLEAST_S && providerInfo.previewLayout != Resources.ID_NULL) { + widgetFeatures |= FEATURE_PREVIEW_LAYOUT; + } + if (ATLEAST_S && providerInfo.targetCellWidth > 0 || providerInfo.targetCellHeight > 0) { + widgetFeatures |= FEATURE_TARGET_CELL_SIZE; + } + if (providerInfo.minResizeWidth > 0 || providerInfo.minResizeHeight > 0) { + widgetFeatures |= FEATURE_MIN_SIZE; + } + if (ATLEAST_S && providerInfo.maxResizeWidth > 0 || providerInfo.maxResizeHeight > 0) { + widgetFeatures |= FEATURE_MAX_SIZE; + } + if (hostView instanceof LauncherAppWidgetHostView && + ((LauncherAppWidgetHostView) hostView).hasEnforcedCornerRadius()) { + widgetFeatures |= FEATURE_ROUNDED_CORNERS; + } + return widgetFeatures; + } + + @Override + public LauncherAtom.ItemInfo buildProto(FolderInfo folderInfo) { + LauncherAtom.ItemInfo info = super.buildProto(folderInfo); + return info.toBuilder() + .setWidget(info.getWidget().toBuilder().setWidgetFeatures(widgetFeatures)) + .build(); + } } diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java index de511cd00b..53b5fec31c 100644 --- a/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java +++ b/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java @@ -149,6 +149,12 @@ public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo return configure != null && (getWidgetFeatures() & WIDGET_FEATURE_RECONFIGURABLE) != 0; } + public boolean isConfigurationOptional() { + return ATLEAST_S + && isReconfigurable() + && (getWidgetFeatures() & WIDGET_FEATURE_CONFIGURATION_OPTIONAL) != 0; + } + @Override public final ComponentName getComponent() { return provider;