• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.android.launcher3.widget;
2 
3 import static com.android.launcher3.Utilities.ATLEAST_S;
4 
5 import android.appwidget.AppWidgetProviderInfo;
6 import android.content.ComponentName;
7 import android.content.Context;
8 import android.content.pm.PackageManager;
9 import android.graphics.Point;
10 import android.graphics.Rect;
11 import android.graphics.drawable.Drawable;
12 import android.os.Parcel;
13 import android.os.UserHandle;
14 
15 import com.android.launcher3.DeviceProfile;
16 import com.android.launcher3.InvariantDeviceProfile;
17 import com.android.launcher3.LauncherAppState;
18 import com.android.launcher3.Utilities;
19 import com.android.launcher3.icons.ComponentWithLabelAndIcon;
20 import com.android.launcher3.icons.IconCache;
21 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
22 
23 /**
24  * This class is a thin wrapper around the framework AppWidgetProviderInfo class. This class affords
25  * a common object for describing both framework provided AppWidgets as well as custom widgets
26  * (who's implementation is owned by the launcher). This object represents a widget type / class,
27  * as opposed to a widget instance, and so should not be confused with {@link LauncherAppWidgetInfo}
28  */
29 public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo
30         implements ComponentWithLabelAndIcon {
31 
32     public static final String CLS_CUSTOM_WIDGET_PREFIX = "#custom-widget-";
33 
34     /**
35      * The desired number of cells that this widget occupies horizontally in
36      * {@link com.android.launcher3.CellLayout}.
37      */
38     public int spanX;
39 
40     /**
41      * The desired number of cells that this widget occupies vertically in
42      * {@link com.android.launcher3.CellLayout}.
43      */
44     public int spanY;
45 
46     /**
47      * The minimum number of cells that this widget can occupy horizontally in
48      * {@link com.android.launcher3.CellLayout}.
49      */
50     public int minSpanX;
51 
52     /**
53      * The minimum number of cells that this widget can occupy vertically in
54      * {@link com.android.launcher3.CellLayout}.
55      */
56     public int minSpanY;
57 
58     /**
59      * The maximum number of cells that this widget can occupy horizontally in
60      * {@link com.android.launcher3.CellLayout}.
61      */
62     public int maxSpanX;
63 
64     /**
65      * The maximum number of cells that this widget can occupy vertically in
66      * {@link com.android.launcher3.CellLayout}.
67      */
68     public int maxSpanY;
69 
70     private boolean mIsMinSizeFulfilled;
71 
fromProviderInfo(Context context, AppWidgetProviderInfo info)72     public static LauncherAppWidgetProviderInfo fromProviderInfo(Context context,
73             AppWidgetProviderInfo info) {
74         final LauncherAppWidgetProviderInfo launcherInfo;
75         if (info instanceof LauncherAppWidgetProviderInfo) {
76             launcherInfo = (LauncherAppWidgetProviderInfo) info;
77         } else {
78 
79             // In lieu of a public super copy constructor, we first write the AppWidgetProviderInfo
80             // into a parcel, and then construct a new LauncherAppWidgetProvider info from the
81             // associated super parcel constructor. This allows us to copy non-public members without
82             // using reflection.
83             Parcel p = Parcel.obtain();
84             info.writeToParcel(p, 0);
85             p.setDataPosition(0);
86             launcherInfo = new LauncherAppWidgetProviderInfo(p);
87             p.recycle();
88         }
89         launcherInfo.initSpans(context, LauncherAppState.getIDP(context));
90         return launcherInfo;
91     }
92 
LauncherAppWidgetProviderInfo()93     protected LauncherAppWidgetProviderInfo() {}
94 
LauncherAppWidgetProviderInfo(Parcel in)95     protected LauncherAppWidgetProviderInfo(Parcel in) {
96         super(in);
97     }
98 
initSpans(Context context, InvariantDeviceProfile idp)99     public void initSpans(Context context, InvariantDeviceProfile idp) {
100         int minSpanX = 0;
101         int minSpanY = 0;
102         int maxSpanX = idp.numColumns;
103         int maxSpanY = idp.numRows;
104         int spanX = 0;
105         int spanY = 0;
106 
107 
108         Point cellSize = new Point();
109         for (DeviceProfile dp : idp.supportedProfiles) {
110             dp.getCellSize(cellSize);
111             Rect widgetPadding = dp.widgetPadding;
112 
113             minSpanX = Math.max(minSpanX,
114                     getSpanX(widgetPadding, minResizeWidth, dp.cellLayoutBorderSpacePx.x,
115                             cellSize.x));
116             minSpanY = Math.max(minSpanY,
117                     getSpanY(widgetPadding, minResizeHeight, dp.cellLayoutBorderSpacePx.y,
118                             cellSize.y));
119 
120             if (ATLEAST_S) {
121                 if (maxResizeWidth > 0) {
122                     maxSpanX = Math.min(maxSpanX, getSpanX(widgetPadding, maxResizeWidth,
123                             dp.cellLayoutBorderSpacePx.x, cellSize.x));
124                 }
125                 if (maxResizeHeight > 0) {
126                     maxSpanY = Math.min(maxSpanY, getSpanY(widgetPadding, maxResizeHeight,
127                             dp.cellLayoutBorderSpacePx.y, cellSize.y));
128                 }
129             }
130 
131             spanX = Math.max(spanX,
132                     getSpanX(widgetPadding, minWidth, dp.cellLayoutBorderSpacePx.x,
133                             cellSize.x));
134             spanY = Math.max(spanY,
135                     getSpanY(widgetPadding, minHeight, dp.cellLayoutBorderSpacePx.y,
136                             cellSize.y));
137         }
138 
139         if (ATLEAST_S) {
140             // Ensures maxSpan >= minSpan
141             maxSpanX = Math.max(maxSpanX, minSpanX);
142             maxSpanY = Math.max(maxSpanY, minSpanY);
143 
144             // Use targetCellWidth/Height if it is within the min/max ranges.
145             // Otherwise, use the span of minWidth/Height.
146             if (targetCellWidth >= minSpanX && targetCellWidth <= maxSpanX
147                     && targetCellHeight >= minSpanY && targetCellHeight <= maxSpanY) {
148                 spanX = targetCellWidth;
149                 spanY = targetCellHeight;
150             }
151         }
152 
153         // If minSpanX/Y > spanX/Y, ignore the minSpanX/Y to match the behavior described in
154         // minResizeWidth & minResizeHeight Android documentation. See
155         // https://developer.android.com/reference/android/appwidget/AppWidgetProviderInfo
156         this.minSpanX = Math.min(spanX, minSpanX);
157         this.minSpanY = Math.min(spanY, minSpanY);
158         this.maxSpanX = maxSpanX;
159         this.maxSpanY = maxSpanY;
160         this.mIsMinSizeFulfilled = Math.min(spanX, minSpanX) <= idp.numColumns
161             && Math.min(spanY, minSpanY) <= idp.numRows;
162         // Ensures the default span X and span Y will not exceed the current grid size.
163         this.spanX = Math.min(spanX, idp.numColumns);
164         this.spanY = Math.min(spanY, idp.numRows);
165     }
166 
167     /**
168      * Returns {@code true} if the widget's minimum size requirement can be fulfilled in the device
169      * grid setting, {@link InvariantDeviceProfile}, that was passed in
170      * {@link #initSpans(Context, InvariantDeviceProfile)}.
171      */
isMinSizeFulfilled()172     public boolean isMinSizeFulfilled() {
173         return mIsMinSizeFulfilled;
174     }
175 
getSpanX(Rect widgetPadding, int widgetWidth, int cellSpacing, float cellWidth)176     private int getSpanX(Rect widgetPadding, int widgetWidth, int cellSpacing, float cellWidth) {
177         return getSpan(widgetPadding.left + widgetPadding.right,
178                 widgetWidth, cellSpacing, cellWidth);
179     }
180 
getSpanY(Rect widgetPadding, int widgetHeight, int cellSpacing, float cellHeight)181     private int getSpanY(Rect widgetPadding, int widgetHeight, int cellSpacing, float cellHeight) {
182         return getSpan(widgetPadding.top + widgetPadding.bottom, widgetHeight,
183                 cellSpacing, cellHeight);
184     }
185 
186     /**
187      * Solving the equation:
188      *   n * cellSize + (n - 1) * cellSpacing - widgetPadding = widgetSize
189      */
getSpan(int widgetPadding, int widgetSize, int cellSpacing, float cellSize)190     private int getSpan(int widgetPadding, int widgetSize, int cellSpacing, float cellSize) {
191         return Math.max(1, (int) Math.ceil(
192                 (widgetSize + widgetPadding + cellSpacing) / (cellSize + cellSpacing)));
193     }
194 
getLabel(PackageManager packageManager)195     public String getLabel(PackageManager packageManager) {
196         return super.loadLabel(packageManager);
197     }
198 
getMinSpans()199     public Point getMinSpans() {
200         return new Point((resizeMode & RESIZE_HORIZONTAL) != 0 ? minSpanX : -1,
201                 (resizeMode & RESIZE_VERTICAL) != 0 ? minSpanY : -1);
202     }
203 
isCustomWidget()204     public boolean isCustomWidget() {
205         return provider.getClassName().startsWith(CLS_CUSTOM_WIDGET_PREFIX);
206     }
207 
getWidgetFeatures()208     public int getWidgetFeatures() {
209         if (Utilities.ATLEAST_P) {
210             return widgetFeatures;
211         } else {
212             return 0;
213         }
214     }
215 
isReconfigurable()216     public boolean isReconfigurable() {
217         return configure != null && (getWidgetFeatures() & WIDGET_FEATURE_RECONFIGURABLE) != 0;
218     }
219 
isConfigurationOptional()220     public boolean isConfigurationOptional() {
221         return ATLEAST_S
222                 && isReconfigurable()
223                 && (getWidgetFeatures() & WIDGET_FEATURE_CONFIGURATION_OPTIONAL) != 0;
224     }
225 
226     @Override
getComponent()227     public final ComponentName getComponent() {
228         return provider;
229     }
230 
231     @Override
getUser()232     public final UserHandle getUser() {
233         return getProfile();
234     }
235 
236     @Override
getFullResIcon(IconCache cache)237     public Drawable getFullResIcon(IconCache cache) {
238         return cache.getFullResIcon(provider.getPackageName(), icon);
239     }
240 }