1 package com.android.launcher3.widget;
2 
3 import android.appwidget.AppWidgetHostView;
4 import android.appwidget.AppWidgetManager;
5 import android.content.Context;
6 import android.graphics.Rect;
7 import android.os.Bundle;
8 import android.os.Handler;
9 import android.util.Log;
10 import android.view.View;
11 
12 import com.android.launcher3.AppWidgetResizeFrame;
13 import com.android.launcher3.DragController;
14 import com.android.launcher3.DragLayer;
15 import com.android.launcher3.DragSource;
16 import com.android.launcher3.Launcher;
17 import com.android.launcher3.LauncherAppWidgetProviderInfo;
18 import com.android.launcher3.Utilities;
19 import com.android.launcher3.compat.AppWidgetManagerCompat;
20 import com.android.launcher3.util.Thunk;
21 
22 public class WidgetHostViewLoader implements DragController.DragListener {
23     private static final String TAG = "WidgetHostViewLoader";
24     private static final boolean LOGD = false;
25 
26     /* Runnables to handle inflation and binding. */
27     @Thunk Runnable mInflateWidgetRunnable = null;
28     private Runnable mBindWidgetRunnable = null;
29 
30     // TODO: technically, this class should not have to know the existence of the launcher.
31     @Thunk Launcher mLauncher;
32     @Thunk Handler mHandler;
33     @Thunk final View mView;
34     @Thunk final PendingAddWidgetInfo mInfo;
35 
36     // Widget id generated for binding a widget host view or -1 for invalid id. The id is
37     // not is use as long as it is stored here and can be deleted safely. Once its used, this value
38     // to be set back to -1.
39     @Thunk int mWidgetLoadingId = -1;
40 
WidgetHostViewLoader(Launcher launcher, View view)41     public WidgetHostViewLoader(Launcher launcher, View view) {
42         mLauncher = launcher;
43         mHandler = new Handler();
44         mView = view;
45         mInfo = (PendingAddWidgetInfo) view.getTag();
46     }
47 
48     @Override
onDragStart(DragSource source, Object info, int dragAction)49     public void onDragStart(DragSource source, Object info, int dragAction) { }
50 
51     @Override
onDragEnd()52     public void onDragEnd() {
53         if (LOGD) {
54             Log.d(TAG, "Cleaning up in onDragEnd()...");
55         }
56 
57         // Cleanup up preloading state.
58         mLauncher.getDragController().removeDragListener(this);
59 
60         mHandler.removeCallbacks(mBindWidgetRunnable);
61         mHandler.removeCallbacks(mInflateWidgetRunnable);
62 
63         // Cleanup widget id
64         if (mWidgetLoadingId != -1) {
65             mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId);
66             mWidgetLoadingId = -1;
67         }
68 
69         // The widget was inflated and added to the DragLayer -- remove it.
70         if (mInfo.boundWidget != null) {
71             if (LOGD) {
72                 Log.d(TAG, "...removing widget from drag layer");
73             }
74             mLauncher.getDragLayer().removeView(mInfo.boundWidget);
75             mLauncher.getAppWidgetHost().deleteAppWidgetId(mInfo.boundWidget.getAppWidgetId());
76             mInfo.boundWidget = null;
77         }
78     }
79 
80     /**
81      * Start preloading the widget.
82      */
preloadWidget()83     public boolean preloadWidget() {
84         final LauncherAppWidgetProviderInfo pInfo = mInfo.info;
85 
86         if (pInfo.isCustomWidget) {
87             return false;
88         }
89         final Bundle options = getDefaultOptionsForWidget(mLauncher, mInfo);
90 
91         // If there is a configuration activity, do not follow thru bound and inflate.
92         if (pInfo.configure != null) {
93             mInfo.bindOptions = options;
94             return false;
95         }
96 
97         mBindWidgetRunnable = new Runnable() {
98             @Override
99             public void run() {
100                 mWidgetLoadingId = mLauncher.getAppWidgetHost().allocateAppWidgetId();
101                 if (LOGD) {
102                     Log.d(TAG, "Binding widget, id: " + mWidgetLoadingId);
103                 }
104                 if(AppWidgetManagerCompat.getInstance(mLauncher).bindAppWidgetIdIfAllowed(
105                         mWidgetLoadingId, pInfo, options)) {
106 
107                     // Widget id bound. Inflate the widget.
108                     mHandler.post(mInflateWidgetRunnable);
109                 }
110             }
111         };
112 
113         mInflateWidgetRunnable = new Runnable() {
114             @Override
115             public void run() {
116                 if (LOGD) {
117                     Log.d(TAG, "Inflating widget, id: " + mWidgetLoadingId);
118                 }
119                 if (mWidgetLoadingId == -1) {
120                     return;
121                 }
122                 AppWidgetHostView hostView = mLauncher.getAppWidgetHost().createView(
123                         (Context) mLauncher, mWidgetLoadingId, pInfo);
124                 mInfo.boundWidget = hostView;
125 
126                 // We used up the widget Id in binding the above view.
127                 mWidgetLoadingId = -1;
128 
129                 hostView.setVisibility(View.INVISIBLE);
130                 int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(mInfo, false);
131                 // We want the first widget layout to be the correct size. This will be important
132                 // for width size reporting to the AppWidgetManager.
133                 DragLayer.LayoutParams lp = new DragLayer.LayoutParams(unScaledSize[0],
134                         unScaledSize[1]);
135                 lp.x = lp.y = 0;
136                 lp.customPosition = true;
137                 hostView.setLayoutParams(lp);
138                 if (LOGD) {
139                     Log.d(TAG, "Adding host view to drag layer");
140                 }
141                 mLauncher.getDragLayer().addView(hostView);
142                 mView.setTag(mInfo);
143             }
144         };
145 
146         if (LOGD) {
147             Log.d(TAG, "About to bind/inflate widget");
148         }
149         mHandler.post(mBindWidgetRunnable);
150         return true;
151     }
152 
getDefaultOptionsForWidget(Launcher launcher, PendingAddWidgetInfo info)153     public static Bundle getDefaultOptionsForWidget(Launcher launcher, PendingAddWidgetInfo info) {
154         Bundle options = null;
155         Rect rect = new Rect();
156         if (Utilities.ATLEAST_JB_MR1) {
157             AppWidgetResizeFrame.getWidgetSizeRanges(launcher, info.spanX, info.spanY, rect);
158             Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(launcher,
159                     info.componentName, null);
160 
161             float density = launcher.getResources().getDisplayMetrics().density;
162             int xPaddingDips = (int) ((padding.left + padding.right) / density);
163             int yPaddingDips = (int) ((padding.top + padding.bottom) / density);
164 
165             options = new Bundle();
166             options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH,
167                     rect.left - xPaddingDips);
168             options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT,
169                     rect.top - yPaddingDips);
170             options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH,
171                     rect.right - xPaddingDips);
172             options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT,
173                     rect.bottom - yPaddingDips);
174         }
175         return options;
176     }
177 }
178