diff options
22 files changed, 363 insertions, 205 deletions
diff --git a/res/values/attrs.xml b/res/values/attrs.xml index 68b628fb7..34385b053 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -143,4 +143,19 @@ <declare-styleable name="RecyclerViewFastScroller"> <attr name="canThumbDetach" format="boolean" /> </declare-styleable> + + <declare-styleable name="CustomAppWidgetProviderInfo"> + <attr name="providerId" format="integer" /> + + <attr name="android:label" /> + <attr name="android:initialLayout" /> + <attr name="android:icon" /> + <attr name="android:previewImage" /> + <attr name="android:resizeMode" /> + + <attr name="numRows" /> + <attr name="numColumns" /> + <attr name="numMinRows" format="integer" /> + <attr name="numMinColumns" format="integer" /> + </declare-styleable> </resources> diff --git a/res/values/dimens.xml b/res/values/dimens.xml index b1f9d6379..b403fbdc2 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -54,7 +54,6 @@ <dimen name="vert_drop_target_horizontal_gap">14dp</dimen> <!-- App Widget resize frame --> - <dimen name="default_widget_padding">8dp</dimen> <dimen name="widget_handle_margin">13dp</dimen> <dimen name="resize_frame_background_padding">24dp</dimen> diff --git a/res/layout/zzz_dummy_widget.xml b/res/xml/custom_widgets.xml index a0fa8fc3e..4b5438650 100644 --- a/res/layout/zzz_dummy_widget.xml +++ b/res/xml/custom_widgets.xml @@ -1,5 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2014 The Android Open Source Project +<!-- + Copyright (C) 2017 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. @@ -14,22 +15,17 @@ limitations under the License. --> -<LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="horizontal"> - - <FrameLayout - android:layout_width="0dp" - android:layout_height="match_parent" - android:layout_weight="1" - android:background="#ffff0000" /> - - <FrameLayout - android:layout_width="0dp" - android:layout_height="match_parent" - android:layout_weight="1" - android:background="#ff00ff00" /> - -</LinearLayout>
\ No newline at end of file +<widgets> + <!-- Sample widget definition + <widget + android:label="My custom widget" + android:initialLayout="@layout/sample_widget_layout" + android:icon="@drawable/ic_launcher_home" + android:resizeMode="horizontal|vertical" + launcher:numRows="2" + launcher:numColumns="3" + launcher:numMinRows="1" + launcher:numMinColumns="2" + launcher:providerId="1" /> + --> +</widgets>
\ No newline at end of file diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java index a486a3aa3..d0b1c3082 100644 --- a/src/com/android/launcher3/AppWidgetResizeFrame.java +++ b/src/com/android/launcher3/AppWidgetResizeFrame.java @@ -8,7 +8,6 @@ import android.animation.ValueAnimator.AnimatorUpdateListener; import android.appwidget.AppWidgetHostView; import android.appwidget.AppWidgetProviderInfo; import android.content.Context; -import android.content.res.Resources; import android.graphics.Point; import android.graphics.Rect; import android.util.AttributeSet; @@ -126,14 +125,8 @@ public class AppWidgetResizeFrame extends FrameLayout mMinHSpan = info.minSpanX; mMinVSpan = info.minSpanY; - if (!info.isCustomWidget) { - mWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(getContext(), - widgetView.getAppWidgetInfo().provider, null); - } else { - Resources r = getContext().getResources(); - int padding = r.getDimensionPixelSize(R.dimen.default_widget_padding); - mWidgetPadding = new Rect(padding, padding, padding, padding); - } + mWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(getContext(), + widgetView.getAppWidgetInfo().provider, null); if (mResizeMode == AppWidgetProviderInfo.RESIZE_HORIZONTAL) { mDragHandles[INDEX_TOP].setVisibility(GONE); diff --git a/src/com/android/launcher3/CustomAppWidget.java b/src/com/android/launcher3/CustomAppWidget.java deleted file mode 100644 index 1b4ed79c0..000000000 --- a/src/com/android/launcher3/CustomAppWidget.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.android.launcher3; - -public interface CustomAppWidget { - public String getLabel(); - public int getPreviewImage(); - public int getIcon(); - public int getWidgetLayout(); - - public int getSpanX(); - public int getSpanY(); - public int getMinSpanX(); - public int getMinSpanY(); - public int getResizeMode(); -} diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 8d9c29f98..76f5cda35 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -136,6 +136,7 @@ import com.android.launcher3.widget.PendingAddWidgetInfo; import com.android.launcher3.widget.WidgetAddFlowHandler; import com.android.launcher3.widget.WidgetHostViewLoader; import com.android.launcher3.widget.WidgetsContainerView; +import com.android.launcher3.widget.custom.CustomWidgetParser; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -296,15 +297,6 @@ public class Launcher extends BaseActivity // the press state and keep this reference to reset the press state when we return to launcher. private BubbleTextView mWaitingForResume; - protected static final HashMap<String, CustomAppWidget> sCustomAppWidgets = - new HashMap<>(); - - static { - if (TestingUtils.ENABLE_CUSTOM_WIDGET_TEST) { - TestingUtils.addDummyWidget(sCustomAppWidgets); - } - } - // Exiting spring loaded mode happens with a delay. This runnable object triggers the // state transition. If another state transition happened during this delay, // simply unregister this runnable. @@ -1408,17 +1400,13 @@ public class Launcher extends BaseActivity appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(appWidgetId); } - if (appWidgetInfo.isCustomWidget) { - appWidgetId = LauncherAppWidgetInfo.CUSTOM_WIDGET_ID; - } - LauncherAppWidgetInfo launcherInfo; launcherInfo = new LauncherAppWidgetInfo(appWidgetId, appWidgetInfo.provider); launcherInfo.spanX = itemInfo.spanX; launcherInfo.spanY = itemInfo.spanY; launcherInfo.minSpanX = itemInfo.minSpanX; launcherInfo.minSpanY = itemInfo.minSpanY; - launcherInfo.user = appWidgetInfo.getUser(); + launcherInfo.user = appWidgetInfo.getProfile(); getModelWriter().addItemToDatabase(launcherInfo, itemInfo.container, itemInfo.screenId, itemInfo.cellX, itemInfo.cellY); @@ -1968,7 +1956,7 @@ public class Launcher extends BaseActivity */ private void addAppWidgetFromDrop(PendingAddWidgetInfo info) { AppWidgetHostView hostView = info.boundWidget; - int appWidgetId; + final int appWidgetId; WidgetAddFlowHandler addFlowHandler = info.getHandler(); if (hostView != null) { // In the case where we've prebound the widget, we remove it from the DragLayer @@ -1985,7 +1973,13 @@ public class Launcher extends BaseActivity } else { // 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. - appWidgetId = getAppWidgetHost().allocateAppWidgetId(); + if (FeatureFlags.ENABLE_CUSTOM_WIDGETS && + info.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET) { + appWidgetId = CustomWidgetParser.getWidgetIdForCustomProvider( + this, info.componentName); + } else { + appWidgetId = getAppWidgetHost().allocateAppWidgetId(); + } Bundle options = info.bindOptions; boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed( @@ -3192,7 +3186,8 @@ public class Launcher extends BaseActivity (FolderInfo) item); break; } - case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: { + case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: + case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET: { view = inflateAppWidget((LauncherAppWidgetInfo) item); if (view == null) { continue; @@ -3882,14 +3877,6 @@ public class Launcher extends BaseActivity return super.onKeyShortcut(keyCode, event); } - public static CustomAppWidget getCustomAppWidget(String name) { - return sCustomAppWidgets.get(name); - } - - public static HashMap<String, CustomAppWidget> getCustomAppWidgets() { - return sCustomAppWidgets; - } - public static Launcher getLauncher(Context context) { if (context instanceof Launcher) { return (Launcher) context; diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java index 5573c5c15..819f23fae 100644 --- a/src/com/android/launcher3/LauncherAppWidgetHost.java +++ b/src/com/android/launcher3/LauncherAppWidgetHost.java @@ -116,7 +116,7 @@ public class LauncherAppWidgetHost extends AppWidgetHost { public AppWidgetHostView createView(Context context, int appWidgetId, LauncherAppWidgetProviderInfo appWidget) { - if (appWidget.isCustomWidget) { + if (appWidget.isCustomWidget()) { LauncherAppWidgetHostView lahv = new LauncherAppWidgetHostView(context); LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); diff --git a/src/com/android/launcher3/LauncherAppWidgetInfo.java b/src/com/android/launcher3/LauncherAppWidgetInfo.java index 6f23e56b3..051846c87 100644 --- a/src/com/android/launcher3/LauncherAppWidgetInfo.java +++ b/src/com/android/launcher3/LauncherAppWidgetInfo.java @@ -71,7 +71,7 @@ public class LauncherAppWidgetInfo extends ItemInfo { /** * Indicates that this is a locally defined widget and hence has no system allocated id. */ - static final int CUSTOM_WIDGET_ID = -100; + public static final int CUSTOM_WIDGET_ID = -100; /** * Identifier for this widget when talking with @@ -104,15 +104,15 @@ public class LauncherAppWidgetInfo extends ItemInfo { private boolean mHasNotifiedInitialWidgetSizeChanged; public LauncherAppWidgetInfo(int appWidgetId, ComponentName providerName) { - if (appWidgetId == CUSTOM_WIDGET_ID) { + this.appWidgetId = appWidgetId; + this.providerName = providerName; + + if (isCustomWidget()) { itemType = LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET; } else { itemType = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET; } - this.appWidgetId = appWidgetId; - this.providerName = providerName; - // Since the widget isn't instantiated yet, we don't know these values. Set them to -1 // to indicate that they should be calculated based on the layout and minWidth/minHeight spanX = -1; @@ -128,7 +128,7 @@ public class LauncherAppWidgetInfo extends ItemInfo { } public boolean isCustomWidget() { - return appWidgetId == CUSTOM_WIDGET_ID; + return appWidgetId <= CUSTOM_WIDGET_ID; } @Override diff --git a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java index 6cb703b43..c7139925c 100644 --- a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java +++ b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java @@ -2,15 +2,11 @@ package com.android.launcher3; import android.appwidget.AppWidgetHostView; import android.appwidget.AppWidgetProviderInfo; -import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageManager; import android.graphics.Point; import android.graphics.Rect; -import android.graphics.drawable.Drawable; import android.os.Parcel; -import android.os.Process; -import android.os.UserHandle; /** * This class is a thin wrapper around the framework AppWidgetProviderInfo class. This class affords @@ -20,7 +16,7 @@ import android.os.UserHandle; */ public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo { - public boolean isCustomWidget = false; + public static final String CLS_CUSTOM_WIDGET_PREFIX = "#custom-widget-"; public int spanX; public int spanY; @@ -48,20 +44,10 @@ public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo { return launcherInfo; } - private LauncherAppWidgetProviderInfo(Parcel in) { - super(in); - } - - public LauncherAppWidgetProviderInfo(Context context, CustomAppWidget widget) { - isCustomWidget = true; + protected LauncherAppWidgetProviderInfo() {} - provider = new ComponentName(context, widget.getClass().getName()); - icon = widget.getIcon(); - label = widget.getLabel(); - previewImage = widget.getPreviewImage(); - initialLayout = widget.getWidgetLayout(); - resizeMode = widget.getResizeMode(); - initSpans(context); + protected LauncherAppWidgetProviderInfo(Parcel in) { + super(in); } public void initSpans(Context context) { @@ -97,34 +83,15 @@ public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo { } public String getLabel(PackageManager packageManager) { - if (isCustomWidget) { - return Utilities.trim(label); - } return super.loadLabel(packageManager); } - public Drawable getIcon(Context context, IconCache cache) { - if (isCustomWidget) { - return cache.getFullResIcon(provider.getPackageName(), icon); - } - return super.loadIcon(context, LauncherAppState.getIDP(context).fillResIconDpi); - } - - public String toString(PackageManager pm) { - if (isCustomWidget) { - return "WidgetProviderInfo(" + provider + ")"; - } - return String.format("WidgetProviderInfo provider:%s package:%s short:%s label:%s", - provider.toString(), provider.getPackageName(), provider.getShortClassName(), getLabel(pm)); - } - - public Point getMinSpans(InvariantDeviceProfile idp, Context context) { - return new Point( - (resizeMode & RESIZE_HORIZONTAL) != 0 ? minSpanX : -1, - (resizeMode & RESIZE_VERTICAL) != 0 ? minSpanY : -1); + public Point getMinSpans() { + return new Point((resizeMode & RESIZE_HORIZONTAL) != 0 ? minSpanX : -1, + (resizeMode & RESIZE_VERTICAL) != 0 ? minSpanY : -1); } - public UserHandle getUser() { - return isCustomWidget ? Process.myUserHandle() : getProfile(); + public boolean isCustomWidget() { + return provider.getClassName().startsWith(CLS_CUSTOM_WIDGET_PREFIX); } } diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java index a65ea9b10..f150c89c1 100644 --- a/src/com/android/launcher3/WidgetPreviewLoader.java +++ b/src/com/android/launcher3/WidgetPreviewLoader.java @@ -22,7 +22,6 @@ import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.os.AsyncTask; -import android.os.Build; import android.os.CancellationSignal; import android.os.Handler; import android.os.UserHandle; @@ -412,7 +411,8 @@ public class WidgetPreviewLoader { // Draw icon in the center. try { - Drawable icon = info.getIcon(launcher, mIconCache); + Drawable icon = + mIconCache.getFullResIcon(info.provider.getPackageName(), info.icon); if (icon != null) { int appIconSize = launcher.getDeviceProfile().iconSizePx; int iconSize = (int) Math.min(appIconSize * scale, diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index d7f709932..c432a5372 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -3641,7 +3641,7 @@ public class Workspace extends PagedView .getInstance(mLauncher).findProvider(item.providerName, item.user); } else { widgetInfo = AppWidgetManagerCompat.getInstance(mLauncher) - .getAppWidgetInfo(item.appWidgetId); + .getLauncherAppWidgetInfo(item.appWidgetId); } if (widgetInfo != null) { diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java index a77a87f2c..fd1f0cca2 100644 --- a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java +++ b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java @@ -24,10 +24,13 @@ import android.os.Bundle; import android.os.UserHandle; import android.support.annotation.Nullable; +import com.android.launcher3.LauncherAppWidgetInfo; import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.Utilities; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.PackageUserKey; +import com.android.launcher3.widget.custom.CustomWidgetParser; import java.util.HashMap; import java.util.List; @@ -58,12 +61,13 @@ public abstract class AppWidgetManagerCompat { mAppWidgetManager = AppWidgetManager.getInstance(context); } - public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) { - return mAppWidgetManager.getAppWidgetInfo(appWidgetId); - } - public LauncherAppWidgetProviderInfo getLauncherAppWidgetInfo(int appWidgetId) { - AppWidgetProviderInfo info = getAppWidgetInfo(appWidgetId); + if (FeatureFlags.ENABLE_CUSTOM_WIDGETS + && appWidgetId <= LauncherAppWidgetInfo.CUSTOM_WIDGET_ID) { + return CustomWidgetParser.getWidgetProvider(mContext, appWidgetId); + } + + AppWidgetProviderInfo info = mAppWidgetManager.getAppWidgetInfo(appWidgetId); return info == null ? null : LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, info); } diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java index cb3bd6c7d..843028503 100644 --- a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java +++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java @@ -20,14 +20,17 @@ import android.appwidget.AppWidgetProviderInfo; import android.content.ComponentName; import android.content.Context; import android.os.Bundle; +import android.os.Process; import android.os.UserHandle; import android.os.UserManager; import android.support.annotation.Nullable; +import com.android.launcher3.LauncherAppWidgetInfo; import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.PackageUserKey; +import com.android.launcher3.widget.custom.CustomWidgetParser; import java.util.ArrayList; import java.util.Collections; @@ -54,6 +57,10 @@ class AppWidgetManagerCompatVL extends AppWidgetManagerCompat { for (UserHandle user : mUserManager.getUserProfiles()) { providers.addAll(mAppWidgetManager.getInstalledProvidersForProfile(user)); } + + if (FeatureFlags.ENABLE_CUSTOM_WIDGETS) { + providers.addAll(CustomWidgetParser.getCustomWidgets(mContext)); + } return providers; } // Only get providers for the given package/user. @@ -65,6 +72,11 @@ class AppWidgetManagerCompatVL extends AppWidgetManagerCompat { iterator.remove(); } } + + if (FeatureFlags.ENABLE_CUSTOM_WIDGETS && Process.myUserHandle().equals(packageUser.mUser) + && mContext.getPackageName().equals(packageUser.mPackageName)) { + providers.addAll(CustomWidgetParser.getCustomWidgets(mContext)); + } return providers; } @@ -74,6 +86,11 @@ class AppWidgetManagerCompatVL extends AppWidgetManagerCompat { if (FeatureFlags.GO_DISABLE_WIDGETS) { return false; } + + if (FeatureFlags.ENABLE_CUSTOM_WIDGETS + && appWidgetId <= LauncherAppWidgetInfo.CUSTOM_WIDGET_ID) { + return true; + } return mAppWidgetManager.bindAppWidgetIdIfAllowed( appWidgetId, info.getProfile(), info.provider, options); } @@ -89,6 +106,15 @@ class AppWidgetManagerCompatVL extends AppWidgetManagerCompat { return LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, info); } } + + if (FeatureFlags.ENABLE_CUSTOM_WIDGETS && Process.myUserHandle().equals(user)) { + for (LauncherAppWidgetProviderInfo info : + CustomWidgetParser.getCustomWidgets(mContext)) { + if (info.provider.equals(provider)) { + return info; + } + } + } return null; } @@ -104,6 +130,13 @@ class AppWidgetManagerCompatVL extends AppWidgetManagerCompat { result.put(new ComponentKey(info.provider, user), info); } } + + if (FeatureFlags.ENABLE_CUSTOM_WIDGETS) { + for (LauncherAppWidgetProviderInfo info : + CustomWidgetParser.getCustomWidgets(mContext)) { + result.put(new ComponentKey(info.provider, info.getProfile()), info); + } + } return result; } } diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java index 5adeec1c6..5f6909c70 100644 --- a/src/com/android/launcher3/config/BaseFlags.java +++ b/src/com/android/launcher3/config/BaseFlags.java @@ -61,6 +61,9 @@ abstract class BaseFlags { // When enabled, the qsb will be moved to the hotseat. public static final boolean QSB_IN_HOTSEAT = true; + // When true, custom widgets are loaded using CustomWidgetParser. + public static final boolean ENABLE_CUSTOM_WIDGETS = false; + // Features to control Launcher3Go behavior public static final boolean GO_DISABLE_WIDGETS = false; } diff --git a/src/com/android/launcher3/model/GridSizeMigrationTask.java b/src/com/android/launcher3/model/GridSizeMigrationTask.java index 8de0de053..d9b1a3f9e 100644 --- a/src/com/android/launcher3/model/GridSizeMigrationTask.java +++ b/src/com/android/launcher3/model/GridSizeMigrationTask.java @@ -726,7 +726,7 @@ public class GridSizeMigrationTask { mContext).getLauncherAppWidgetInfo(widgetId); Point spans = null; if (pInfo != null) { - spans = pInfo.getMinSpans(mIdp, mContext); + spans = pInfo.getMinSpans(); } if (spans != null) { entry.minSpanX = spans.x > 0 ? spans.x : entry.spanX; diff --git a/src/com/android/launcher3/testing/DummyWidget.java b/src/com/android/launcher3/testing/DummyWidget.java deleted file mode 100644 index df887ac1f..000000000 --- a/src/com/android/launcher3/testing/DummyWidget.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.android.launcher3.testing; - -import android.appwidget.AppWidgetProviderInfo; - -import com.android.launcher3.CustomAppWidget; -import com.android.launcher3.R; - -public class DummyWidget implements CustomAppWidget { - @Override - public String getLabel() { - return "Dumb Launcher Widget"; - } - - @Override - public int getPreviewImage() { - return 0; - } - - @Override - public int getIcon() { - return 0; - } - - @Override - public int getWidgetLayout() { - return R.layout.zzz_dummy_widget; - } - - @Override - public int getSpanX() { - return 2; - } - - @Override - public int getSpanY() { - return 2; - } - - @Override - public int getMinSpanX() { - return 1; - } - - @Override - public int getMinSpanY() { - return 1; - } - - @Override - public int getResizeMode() { - return AppWidgetProviderInfo.RESIZE_BOTH; - } -} diff --git a/src/com/android/launcher3/util/TestingUtils.java b/src/com/android/launcher3/util/TestingUtils.java index a7cc42b5f..d927dc335 100644 --- a/src/com/android/launcher3/util/TestingUtils.java +++ b/src/com/android/launcher3/util/TestingUtils.java @@ -3,18 +3,14 @@ package com.android.launcher3.util; import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.util.Log; import android.view.Gravity; import android.view.View; import android.widget.FrameLayout; -import com.android.launcher3.CustomAppWidget; import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.Utilities; -import java.util.HashMap; - public class TestingUtils { public static final String MEMORY_TRACKER = "com.android.launcher3.testing.MemoryTracker"; @@ -23,9 +19,6 @@ public class TestingUtils { public static final boolean MEMORY_DUMP_ENABLED = false; public static final String SHOW_WEIGHT_WATCHER = "debug.show_mem"; - public static final boolean ENABLE_CUSTOM_WIDGET_TEST = false; - public static final String DUMMY_WIDGET = "com.android.launcher3.testing.DummyWidget"; - public static void startTrackingMemory(Context context) { if (MEMORY_DUMP_ENABLED) { context.startService(new Intent() @@ -55,16 +48,4 @@ public class TestingUtils { launcher.mWeightWatcher = watcher; } } - - public static void addDummyWidget(HashMap<String, CustomAppWidget> set) { - if (ENABLE_CUSTOM_WIDGET_TEST) { - try { - Class<?> clazz = Class.forName(DUMMY_WIDGET); - CustomAppWidget widget = (CustomAppWidget) clazz.newInstance(); - set.put(widget.getClass().getName(), widget); - } catch (Exception e) { - Log.e("TestingUtils", "Error adding dummy widget", e); - } - } - } } diff --git a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java index ad05ce917..bc404842e 100644 --- a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java +++ b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java @@ -35,13 +35,13 @@ public class PendingAddWidgetInfo extends PendingAddItemInfo { public Bundle bindOptions = null; public PendingAddWidgetInfo(LauncherAppWidgetProviderInfo i) { - if (i.isCustomWidget) { + if (i.isCustomWidget()) { itemType = LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET; } else { itemType = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET; } this.info = i; - user = i.getUser(); + user = i.getProfile(); componentName = i.provider; previewImage = i.previewImage; icon = i.icon; diff --git a/src/com/android/launcher3/widget/WidgetHostViewLoader.java b/src/com/android/launcher3/widget/WidgetHostViewLoader.java index 5eeea44b8..e6f8bb68f 100644 --- a/src/com/android/launcher3/widget/WidgetHostViewLoader.java +++ b/src/com/android/launcher3/widget/WidgetHostViewLoader.java @@ -85,7 +85,7 @@ public class WidgetHostViewLoader implements DragController.DragListener { private boolean preloadWidget() { final LauncherAppWidgetProviderInfo pInfo = mInfo.info; - if (pInfo.isCustomWidget) { + if (pInfo.isCustomWidget()) { return false; } final Bundle options = getDefaultOptionsForWidget(mLauncher, mInfo); diff --git a/src/com/android/launcher3/widget/custom/CustomAppWidgetProviderInfo.java b/src/com/android/launcher3/widget/custom/CustomAppWidgetProviderInfo.java new file mode 100644 index 000000000..1086987f8 --- /dev/null +++ b/src/com/android/launcher3/widget/custom/CustomAppWidgetProviderInfo.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2017 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.custom; + +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.launcher3.LauncherAppWidgetProviderInfo; +import com.android.launcher3.Utilities; + +/** + * Custom app widget provider info that can be used as a widget, but provide extra functionality + * by allowing custom code and views. + */ +public class CustomAppWidgetProviderInfo extends LauncherAppWidgetProviderInfo + implements Parcelable { + + public final int providerId; + + protected CustomAppWidgetProviderInfo(Parcel parcel, boolean readSelf, int providerId) { + super(parcel); + if (readSelf) { + this.providerId = parcel.readInt(); + + provider = new ComponentName(parcel.readString(), CLS_CUSTOM_WIDGET_PREFIX + providerId); + + label = parcel.readString(); + initialLayout = parcel.readInt(); + icon = parcel.readInt(); + previewImage = parcel.readInt(); + + resizeMode = parcel.readInt(); + spanX = parcel.readInt(); + spanY = parcel.readInt(); + minSpanX = parcel.readInt(); + minSpanY = parcel.readInt(); + } else { + this.providerId = providerId; + } + } + + @Override + public void initSpans(Context context) { } + + @Override + public String getLabel(PackageManager packageManager) { + return Utilities.trim(label); + } + + @Override + public String toString() { + return "WidgetProviderInfo(" + provider + ")"; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + super.writeToParcel(out, flags); + out.writeInt(providerId); + out.writeString(provider.getPackageName()); + + out.writeString(label); + out.writeInt(initialLayout); + out.writeInt(icon); + out.writeInt(previewImage); + + out.writeInt(resizeMode); + out.writeInt(spanX); + out.writeInt(spanY); + out.writeInt(minSpanX); + out.writeInt(minSpanY); + } + + public static final Parcelable.Creator<CustomAppWidgetProviderInfo> CREATOR + = new Parcelable.Creator<CustomAppWidgetProviderInfo>() { + + @Override + public CustomAppWidgetProviderInfo createFromParcel(Parcel parcel) { + return new CustomAppWidgetProviderInfo(parcel, true, 0); + } + + @Override + public CustomAppWidgetProviderInfo[] newArray(int size) { + return new CustomAppWidgetProviderInfo[size]; + } + }; +} diff --git a/src/com/android/launcher3/widget/custom/CustomWidgetParser.java b/src/com/android/launcher3/widget/custom/CustomWidgetParser.java new file mode 100644 index 000000000..00720c449 --- /dev/null +++ b/src/com/android/launcher3/widget/custom/CustomWidgetParser.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2017 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.custom; + +import android.appwidget.AppWidgetManager; +import android.appwidget.AppWidgetProviderInfo; +import android.content.ComponentName; +import android.content.Context; +import android.content.res.TypedArray; +import android.content.res.XmlResourceParser; +import android.os.Parcel; +import android.os.Process; +import android.util.SparseArray; +import android.util.Xml; + +import com.android.launcher3.LauncherAppWidgetInfo; +import com.android.launcher3.LauncherAppWidgetProviderInfo; +import com.android.launcher3.R; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import static com.android.launcher3.LauncherAppWidgetProviderInfo.CLS_CUSTOM_WIDGET_PREFIX; + +/** + * Utility class to parse {@ink CustomAppWidgetProviderInfo} definitions from xml + */ +public class CustomWidgetParser { + + private static List<LauncherAppWidgetProviderInfo> sCustomWidgets; + private static SparseArray<ComponentName> sWidgetsIdMap; + + public static List<LauncherAppWidgetProviderInfo> getCustomWidgets(Context context) { + if (sCustomWidgets == null) { + // Synchronization not needed as it it safe to load multiple times + parseCustomWidgets(context); + } + + return sCustomWidgets; + } + + public static int getWidgetIdForCustomProvider(Context context, ComponentName provider) { + if (sWidgetsIdMap == null) { + parseCustomWidgets(context); + } + int index = sWidgetsIdMap.indexOfValue(provider); + if (index >= 0) { + return LauncherAppWidgetInfo.CUSTOM_WIDGET_ID - sWidgetsIdMap.keyAt(index); + } else { + return AppWidgetManager.INVALID_APPWIDGET_ID; + } + } + + public static LauncherAppWidgetProviderInfo getWidgetProvider(Context context, int widgetId) { + if (sWidgetsIdMap == null || sCustomWidgets == null) { + parseCustomWidgets(context); + } + ComponentName cn = sWidgetsIdMap.get(LauncherAppWidgetInfo.CUSTOM_WIDGET_ID - widgetId); + for (LauncherAppWidgetProviderInfo info : sCustomWidgets) { + if (info.provider.equals(cn)) { + return info; + } + } + return null; + } + + private static void parseCustomWidgets(Context context) { + ArrayList<LauncherAppWidgetProviderInfo> widgets = new ArrayList<>(); + SparseArray<ComponentName> idMap = new SparseArray<>(); + + List<AppWidgetProviderInfo> providers = AppWidgetManager.getInstance(context) + .getInstalledProvidersForProfile(Process.myUserHandle()); + if (providers.isEmpty()) { + sCustomWidgets = widgets; + sWidgetsIdMap = idMap; + return; + } + + Parcel parcel = Parcel.obtain(); + providers.get(0).writeToParcel(parcel, 0); + + try (XmlResourceParser parser = context.getResources().getXml(R.xml.custom_widgets)) { + final int depth = parser.getDepth(); + int type; + + while (((type = parser.next()) != XmlPullParser.END_TAG || + parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) { + if ((type == XmlPullParser.START_TAG) && "widget".equals(parser.getName())) { + TypedArray a = context.obtainStyledAttributes( + Xml.asAttributeSet(parser), R.styleable.CustomAppWidgetProviderInfo); + + parcel.setDataPosition(0); + CustomAppWidgetProviderInfo info = newInfo(a, parcel, context); + widgets.add(info); + a.recycle(); + + idMap.put(info.providerId, info.provider); + } + } + } catch (IOException | XmlPullParserException e) { + throw new RuntimeException(e); + } + parcel.recycle(); + sCustomWidgets = widgets; + sWidgetsIdMap = idMap; + } + + private static CustomAppWidgetProviderInfo newInfo(TypedArray a, Parcel parcel, Context context) { + int providerId = a.getInt(R.styleable.CustomAppWidgetProviderInfo_providerId, 0); + CustomAppWidgetProviderInfo info = new CustomAppWidgetProviderInfo(parcel, false, providerId); + info.provider = new ComponentName(context.getPackageName(), CLS_CUSTOM_WIDGET_PREFIX + providerId); + + info.label = a.getString(R.styleable.CustomAppWidgetProviderInfo_android_label); + info.initialLayout = a.getResourceId(R.styleable.CustomAppWidgetProviderInfo_android_initialLayout, 0); + info.icon = a.getResourceId(R.styleable.CustomAppWidgetProviderInfo_android_icon, 0); + info.previewImage = a.getResourceId(R.styleable.CustomAppWidgetProviderInfo_android_previewImage, 0); + info.resizeMode = a.getInt(R.styleable.CustomAppWidgetProviderInfo_android_resizeMode, 0); + + info.spanX = a.getInt(R.styleable.CustomAppWidgetProviderInfo_numColumns, 1); + info.spanY = a.getInt(R.styleable.CustomAppWidgetProviderInfo_numRows, 1); + info.minSpanX = a.getInt(R.styleable.CustomAppWidgetProviderInfo_numMinColumns, 1); + info.minSpanY = a.getInt(R.styleable.CustomAppWidgetProviderInfo_numMinRows, 1); + return info; + } +} diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java index d4d517a22..87103d713 100644 --- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java @@ -16,6 +16,7 @@ package com.android.launcher3.ui.widget; import android.appwidget.AppWidgetHost; +import android.appwidget.AppWidgetManager; import android.content.ComponentName; import android.content.ContentResolver; import android.content.ContentValues; @@ -171,8 +172,9 @@ public class BindWidgetTest extends AbstractLauncherUiTest { // Widget has a valid Id now. assertEquals(0, mCursor.getInt(mCursor.getColumnIndex(LauncherSettings.Favorites.RESTORED)) & LauncherAppWidgetInfo.FLAG_ID_NOT_VALID); - assertNotNull(mWidgetManager.getAppWidgetInfo(mCursor.getInt(mCursor.getColumnIndex( - LauncherSettings.Favorites.APPWIDGET_ID)))); + assertNotNull(AppWidgetManager.getInstance(mTargetContext) + .getAppWidgetInfo(mCursor.getInt(mCursor.getColumnIndex( + LauncherSettings.Favorites.APPWIDGET_ID)))); } @Test @@ -297,7 +299,7 @@ public class BindWidgetTest extends AbstractLauncherUiTest { item.spanY = info.minSpanY; item.minSpanX = info.minSpanX; item.minSpanY = info.minSpanY; - item.user = info.getUser(); + item.user = info.getProfile(); item.cellX = 0; item.cellY = 1; item.container = LauncherSettings.Favorites.CONTAINER_DESKTOP; |