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