Merge "Add smartspace custom widget" into main

This commit is contained in:
Federico Baron
2023-09-12 18:43:58 +00:00
committed by Android (Google) Code Review
12 changed files with 147 additions and 106 deletions

View File

@@ -37,6 +37,7 @@ import com.android.launcher3.util.IntSet;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.LauncherWidgetHolder;
import com.android.launcher3.widget.custom.CustomWidgetManager;
import java.util.ArrayList;
import java.util.Collections;
@@ -237,6 +238,14 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder {
@Override
public LauncherAppWidgetHostView createView(@NonNull Context context, int appWidgetId,
@NonNull LauncherAppWidgetProviderInfo appWidget) {
if (appWidget.isCustomWidget()) {
LauncherAppWidgetHostView lahv = new LauncherAppWidgetHostView(context);
lahv.setAppWidget(appWidgetId, appWidget);
CustomWidgetManager.INSTANCE.get(context).onViewCreated(lahv);
return lahv;
}
LauncherAppWidgetHostView widgetView = getPendingView(appWidgetId);
if (widgetView != null) {
removePendingView(appWidgetId);

View File

@@ -241,4 +241,7 @@
<item>@dimen/iconSize66dp</item>
<item>@dimen/iconSize72dp</item>
</integer-array>
<!-- Used for custom widgets -->
<array name="custom_widget_providers"/>
</resources>

View File

@@ -68,6 +68,7 @@ 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;
@@ -886,7 +887,7 @@ public class Launcher extends StatefulActivity<LauncherState>
if (widgetInfo != null) {
// Since the view was just bound, also launch the configure activity if needed
LauncherAppWidgetProviderInfo provider = mAppWidgetManager
.getLauncherAppWidgetInfo(widgetId);
.getLauncherAppWidgetInfo(widgetId, info.getTargetComponent());
if (provider != null) {
new WidgetAddFlowHandler(provider)
.startConfigActivity(this, widgetInfo,
@@ -1500,7 +1501,8 @@ public class Launcher extends StatefulActivity<LauncherState>
AppWidgetHostView hostView, LauncherAppWidgetProviderInfo appWidgetInfo) {
if (appWidgetInfo == null) {
appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(appWidgetId);
appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(appWidgetId,
itemInfo.getTargetComponent());
}
if (hostView == null) {
@@ -1995,8 +1997,8 @@ public class Launcher extends StatefulActivity<LauncherState>
// In this case, we either need to start an activity to get permission to bind
// the widget, or we need to start an activity to configure the widget, or both.
if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET) {
appWidgetId = CustomWidgetManager.INSTANCE.get(this).getWidgetIdForCustomProvider(
info.componentName);
appWidgetId = CustomWidgetManager.INSTANCE.get(this)
.allocateCustomAppWidgetId(info.componentName);
} else {
appWidgetId = getAppWidgetHolder().allocateAppWidgetId();
}
@@ -2642,9 +2644,10 @@ public class Launcher extends StatefulActivity<LauncherState>
}
}
} else {
appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(item.appWidgetId);
appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(item.appWidgetId,
item.getTargetComponent());
if (appWidgetInfo == null) {
if (item.appWidgetId <= LauncherAppWidgetInfo.CUSTOM_WIDGET_ID) {
if (item.appWidgetId <= CUSTOM_WIDGET_ID) {
removalReason =
"CustomWidgetManager cannot find provider from that widget id.";
} else {

View File

@@ -3413,7 +3413,8 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T>
if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
widgetInfo = widgetHelper.findProvider(item.providerName, item.user);
} else {
widgetInfo = widgetHelper.getLauncherAppWidgetInfo(item.appWidgetId);
widgetInfo = widgetHelper.getLauncherAppWidgetInfo(item.appWidgetId,
item.getTargetComponent());
}
if (widgetInfo != null) {

View File

@@ -539,8 +539,8 @@ public class GridSizeMigrationUtil {
verifyPackage(cn.getPackageName());
int widgetId = c.getInt(indexAppWidgetId);
LauncherAppWidgetProviderInfo pInfo =
widgetManagerHelper.getLauncherAppWidgetInfo(widgetId);
LauncherAppWidgetProviderInfo pInfo = widgetManagerHelper
.getLauncherAppWidgetInfo(widgetId, cn);
Point spans = null;
if (pInfo != null) {
spans = pInfo.getMinSpans();

View File

@@ -95,6 +95,7 @@ import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.WidgetManagerHelper;
import com.android.launcher3.widget.custom.CustomWidgetManager;
import java.util.ArrayList;
import java.util.Collections;
@@ -740,8 +741,13 @@ public class LoaderTask implements Runnable {
ComponentKey providerKey = new ComponentKey(component, c.user);
if (!mWidgetProvidersMap.containsKey(providerKey)) {
mWidgetProvidersMap.put(providerKey,
widgetHelper.findProvider(component, c.user));
if (customWidget) {
mWidgetProvidersMap.put(providerKey, CustomWidgetManager.INSTANCE
.get(mApp.getContext()).getWidgetProvider(component));
} else {
mWidgetProvidersMap.put(providerKey,
widgetHelper.findProvider(component, c.user));
}
}
final AppWidgetProviderInfo provider = mWidgetProvidersMap.get(providerKey);
@@ -814,7 +820,8 @@ public class LoaderTask implements Runnable {
return;
}
LauncherAppWidgetProviderInfo widgetProviderInfo =
widgetHelper.getLauncherAppWidgetInfo(appWidgetId);
widgetHelper.getLauncherAppWidgetInfo(appWidgetId,
appWidgetInfo.getTargetComponent());
if (widgetProviderInfo != null
&& (appWidgetInfo.spanX < widgetProviderInfo.minSpanX
|| appWidgetInfo.spanY < widgetProviderInfo.minSpanY)) {

View File

@@ -23,9 +23,11 @@ import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Build;
import android.os.Handler;
import android.os.Parcelable;
import android.os.SystemClock;
import android.os.Trace;
import android.util.Log;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.view.MotionEvent;
@@ -498,4 +500,13 @@ public class LauncherAppWidgetHostView extends BaseLauncherAppWidgetHostView
*/
void notifyBoundChangeOnPreLayout(View v, int left, int top, int right, int bottom);
}
@Override
protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
try {
super.dispatchRestoreInstanceState(container);
} catch (Exception e) {
Log.i(TAG, "Exception: " + e);
}
}
}

View File

@@ -67,7 +67,7 @@ public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo
*/
public int maxSpanY;
private boolean mIsMinSizeFulfilled;
protected boolean mIsMinSizeFulfilled;
public static LauncherAppWidgetProviderInfo fromProviderInfo(Context context,
AppWidgetProviderInfo info) {

View File

@@ -57,10 +57,15 @@ public class WidgetManagerHelper {
/**
* @see AppWidgetManager#getAppWidgetInfo(int)
*/
public LauncherAppWidgetProviderInfo getLauncherAppWidgetInfo(int appWidgetId) {
if (appWidgetId <= LauncherAppWidgetInfo.CUSTOM_WIDGET_ID) {
return CustomWidgetManager.INSTANCE.get(mContext).getWidgetProvider(appWidgetId);
public LauncherAppWidgetProviderInfo getLauncherAppWidgetInfo(
int appWidgetId, ComponentName componentName) {
// For custom widgets.
if (appWidgetId <= LauncherAppWidgetInfo.CUSTOM_WIDGET_ID && !CustomWidgetManager
.INSTANCE.get(mContext).getWidgetIdForCustomProvider(componentName).equals("")) {
return CustomWidgetManager.INSTANCE.get(mContext).getWidgetProvider(componentName);
}
AppWidgetProviderInfo info = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
return info == null ? null : LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, info);
}

View File

@@ -33,14 +33,15 @@ import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
public class CustomAppWidgetProviderInfo extends LauncherAppWidgetProviderInfo
implements Parcelable {
public final int providerId;
public final String providerId;
protected CustomAppWidgetProviderInfo(Parcel parcel, boolean readSelf, int providerId) {
protected CustomAppWidgetProviderInfo(Parcel parcel, boolean readSelf, String providerId) {
super(parcel);
if (readSelf) {
this.providerId = parcel.readInt();
this.providerId = parcel.readString();
provider = new ComponentName(parcel.readString(), CLS_CUSTOM_WIDGET_PREFIX + providerId);
provider = new ComponentName(parcel.readString(),
CLS_CUSTOM_WIDGET_PREFIX + parcel.readString());
label = parcel.readString();
initialLayout = parcel.readInt();
@@ -58,7 +59,10 @@ public class CustomAppWidgetProviderInfo extends LauncherAppWidgetProviderInfo
}
@Override
public void initSpans(Context context, InvariantDeviceProfile idp) { }
public void initSpans(Context context, InvariantDeviceProfile idp) {
mIsMinSizeFulfilled = Math.min(spanX, minSpanX) <= idp.numColumns
&& Math.min(spanY, minSpanY) <= idp.numRows;
}
@Override
public String getLabel(PackageManager packageManager) {
@@ -73,8 +77,9 @@ public class CustomAppWidgetProviderInfo extends LauncherAppWidgetProviderInfo
@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeInt(providerId);
out.writeString(providerId);
out.writeString(provider.getPackageName());
out.writeString(provider.getClassName());
out.writeString(label);
out.writeInt(initialLayout);
@@ -93,7 +98,7 @@ public class CustomAppWidgetProviderInfo extends LauncherAppWidgetProviderInfo
@Override
public CustomAppWidgetProviderInfo createFromParcel(Parcel parcel) {
return new CustomAppWidgetProviderInfo(parcel, true, 0);
return new CustomAppWidgetProviderInfo(parcel, true, "");
}
@Override

View File

@@ -16,7 +16,8 @@
package com.android.launcher3.widget.custom;
import static com.android.launcher3.widget.LauncherAppWidgetProviderInfo.CLS_CUSTOM_WIDGET_PREFIX;
import static com.android.launcher3.config.FeatureFlags.SMARTSPACE_AS_A_WIDGET;
import static com.android.launcher3.model.data.LauncherAppWidgetInfo.CUSTOM_WIDGET_ID;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
@@ -24,12 +25,12 @@ import android.content.ComponentName;
import android.content.Context;
import android.os.Parcel;
import android.os.Process;
import android.util.SparseArray;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.R;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.PackageUserKey;
@@ -39,8 +40,11 @@ import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.systemui.plugins.CustomWidgetPlugin;
import com.android.systemui.plugins.PluginListener;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.stream.Stream;
@@ -52,24 +56,37 @@ public class CustomWidgetManager implements PluginListener<CustomWidgetPlugin>,
public static final MainThreadInitializedObject<CustomWidgetManager> INSTANCE =
new MainThreadInitializedObject<>(CustomWidgetManager::new);
private static final String TAG = "CustomWidgetManager";
private final Context mContext;
/**
* auto provider Id is an ever-increasing number that serves as the providerId whenever a new
* custom widget has been connected.
*/
private int mAutoProviderId = 0;
private final SparseArray<CustomWidgetPlugin> mPlugins;
private final HashMap<String, CustomWidgetPlugin> mPlugins;
private final List<CustomAppWidgetProviderInfo> mCustomWidgets;
private final SparseArray<ComponentName> mWidgetsIdMap;
private final HashMap<ComponentName, String> mWidgetsIdMap;
private Consumer<PackageUserKey> mWidgetRefreshCallback;
private CustomWidgetManager(Context context) {
mContext = context;
mPlugins = new SparseArray<>();
mPlugins = new HashMap<>();
mCustomWidgets = new ArrayList<>();
mWidgetsIdMap = new SparseArray<>();
mWidgetsIdMap = new HashMap<>();
PluginManagerWrapper.INSTANCE.get(context)
.addPluginListener(this, CustomWidgetPlugin.class, true);
if (SMARTSPACE_AS_A_WIDGET.get()) {
for (String s: context.getResources()
.getStringArray(R.array.custom_widget_providers)) {
try {
Class<?> cls = Class.forName(s);
CustomWidgetPlugin plugin = (CustomWidgetPlugin)
cls.getDeclaredConstructor(Context.class).newInstance(context);
mPlugins.put(plugin.getId(), plugin);
onPluginConnected(mPlugins.get(plugin.getId()), context);
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException
| ClassCastException | NoSuchMethodException
| InvocationTargetException e) {
Log.e(TAG, "Exception found when trying to add custom widgets: " + e);
}
}
}
}
@Override
@@ -79,28 +96,41 @@ public class CustomWidgetManager implements PluginListener<CustomWidgetPlugin>,
@Override
public void onPluginConnected(CustomWidgetPlugin plugin, Context context) {
mPlugins.put(mAutoProviderId, plugin);
List<AppWidgetProviderInfo> providers = AppWidgetManager.getInstance(context)
.getInstalledProvidersForProfile(Process.myUserHandle());
if (providers.isEmpty()) return;
Parcel parcel = Parcel.obtain();
providers.get(0).writeToParcel(parcel, 0);
parcel.setDataPosition(0);
CustomAppWidgetProviderInfo info = newInfo(mAutoProviderId, plugin, parcel, context);
CustomAppWidgetProviderInfo info = newInfo(plugin.getId(), plugin, parcel, context);
parcel.recycle();
mCustomWidgets.add(info);
mWidgetsIdMap.put(mAutoProviderId, info.provider);
mWidgetRefreshCallback.accept(null);
mAutoProviderId++;
mWidgetsIdMap.put(info.provider, plugin.getId());
}
@Override
public void onPluginDisconnected(CustomWidgetPlugin plugin) {
int providerId = findProviderId(plugin);
if (providerId == -1) return;
mPlugins.remove(providerId);
mCustomWidgets.remove(getWidgetProvider(providerId));
mWidgetsIdMap.remove(providerId);
String providerId = plugin.getId();
if (mPlugins.containsKey(providerId)) {
mPlugins.remove(providerId);
}
ComponentName cn = null;
for (Map.Entry entry: mWidgetsIdMap.entrySet()) {
if (entry.getValue().equals(providerId)) {
cn = (ComponentName) entry.getKey();
}
}
if (cn != null) {
mWidgetsIdMap.remove(cn);
for (int i = 0; i < mCustomWidgets.size(); i++) {
if (mCustomWidgets.get(i).getComponent().equals(cn)) {
mCustomWidgets.remove(i);
return;
}
}
}
}
/**
@@ -131,12 +161,11 @@ public class CustomWidgetManager implements PluginListener<CustomWidgetPlugin>,
/**
* Returns the widget id for a specific provider.
*/
public int getWidgetIdForCustomProvider(@NonNull ComponentName provider) {
int index = mWidgetsIdMap.indexOfValue(provider);
if (index >= 0) {
return LauncherAppWidgetInfo.CUSTOM_WIDGET_ID - mWidgetsIdMap.keyAt(index);
public String getWidgetIdForCustomProvider(@NonNull ComponentName provider) {
if (mWidgetsIdMap.containsKey(provider)) {
return mWidgetsIdMap.get(provider);
} else {
return AppWidgetManager.INVALID_APPWIDGET_ID;
return "";
}
}
@@ -144,38 +173,26 @@ public class CustomWidgetManager implements PluginListener<CustomWidgetPlugin>,
* Returns the widget provider in respect to given widget id.
*/
@Nullable
public LauncherAppWidgetProviderInfo getWidgetProvider(int widgetId) {
ComponentName cn = mWidgetsIdMap.get(LauncherAppWidgetInfo.CUSTOM_WIDGET_ID - widgetId);
public LauncherAppWidgetProviderInfo getWidgetProvider(ComponentName componentName) {
for (LauncherAppWidgetProviderInfo info : mCustomWidgets) {
if (info.provider.equals(cn)) return info;
if (info.provider.equals(componentName)) return info;
}
return null;
}
private static CustomAppWidgetProviderInfo newInfo(int providerId, CustomWidgetPlugin plugin,
private static CustomAppWidgetProviderInfo newInfo(String providerId, CustomWidgetPlugin plugin,
Parcel parcel, Context context) {
CustomAppWidgetProviderInfo info = new CustomAppWidgetProviderInfo(
parcel, false, providerId);
info.provider = new ComponentName(
context.getPackageName(), CLS_CUSTOM_WIDGET_PREFIX + providerId);
info.label = plugin.getLabel();
info.resizeMode = plugin.getResizeMode();
info.spanX = plugin.getSpanX();
info.spanY = plugin.getSpanY();
info.minSpanX = plugin.getMinSpanX();
info.minSpanY = plugin.getMinSpanY();
plugin.updateWidgetInfo(info, context);
return info;
}
private int findProviderId(CustomWidgetPlugin plugin) {
for (int i = 0; i < mPlugins.size(); i++) {
int providerId = mPlugins.keyAt(i);
if (mPlugins.get(providerId) == plugin) {
return providerId;
}
}
return -1;
/**
* Returns an id to set as the appWidgetId for a custom widget.
*/
public int allocateCustomAppWidgetId(ComponentName componentName) {
return CUSTOM_WIDGET_ID - mCustomWidgets.indexOf(getWidgetProvider(componentName));
}
}

View File

@@ -17,6 +17,8 @@
package com.android.systemui.plugins;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
import com.android.systemui.plugins.annotations.ProvidesInterface;
@@ -29,43 +31,21 @@ public interface CustomWidgetPlugin extends Plugin {
String ACTION = "com.android.systemui.action.PLUGIN_CUSTOM_WIDGET";
int VERSION = 1;
/**
* The label to display to the user in the AppWidget picker.
*/
String getLabel();
/**
* The default width of the widget when added to a host, in dp. The widget will get
* at least this width, and will often be given more, depending on the host.
*/
int getSpanX();
/**
* The default height of the widget when added to a host, in dp. The widget will get
* at least this height, and will often be given more, depending on the host.
*/
int getSpanY();
/**
* Minimum width (in dp) which the widget can be resized to. This field has no effect if it
* is greater than minWidth or if horizontal resizing isn't enabled.
*/
int getMinSpanX();
/**
* Minimum height (in dp) which the widget can be resized to. This field has no effect if it
* is greater than minHeight or if vertical resizing isn't enabled.
*/
int getMinSpanY();
/**
* The rules by which a widget can be resized.
*/
int getResizeMode();
/**
* Notify the plugin that container of the widget has been rendered, where the custom widget
* can be attached to.
*/
void onViewCreated(AppWidgetHostView parent);
/**
* Get the UUID for the custom widget.
*/
String getId();
/**
* Used to modify a widgets' info.
*/
default void updateWidgetInfo(AppWidgetProviderInfo info, Context context) {
}
}