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