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