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