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.DropTarget; 14 import com.android.launcher3.Launcher; 15 import com.android.launcher3.LauncherAppWidgetProviderInfo; 16 import com.android.launcher3.compat.AppWidgetManagerCompat; 17 import com.android.launcher3.dragndrop.DragController; 18 import com.android.launcher3.dragndrop.DragLayer; 19 import com.android.launcher3.dragndrop.DragOptions; 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(DropTarget.DragObject dragObject, DragOptions options)49 public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) { 50 preloadWidget(); 51 } 52 53 @Override onDragEnd()54 public void onDragEnd() { 55 if (LOGD) { 56 Log.d(TAG, "Cleaning up in onDragEnd()..."); 57 } 58 59 // Cleanup up preloading state. 60 mLauncher.getDragController().removeDragListener(this); 61 62 mHandler.removeCallbacks(mBindWidgetRunnable); 63 mHandler.removeCallbacks(mInflateWidgetRunnable); 64 65 // Cleanup widget id 66 if (mWidgetLoadingId != -1) { 67 mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId); 68 mWidgetLoadingId = -1; 69 } 70 71 // The widget was inflated and added to the DragLayer -- remove it. 72 if (mInfo.boundWidget != null) { 73 if (LOGD) { 74 Log.d(TAG, "...removing widget from drag layer"); 75 } 76 mLauncher.getDragLayer().removeView(mInfo.boundWidget); 77 mLauncher.getAppWidgetHost().deleteAppWidgetId(mInfo.boundWidget.getAppWidgetId()); 78 mInfo.boundWidget = null; 79 } 80 } 81 82 /** 83 * Start preloading the widget. 84 */ preloadWidget()85 private boolean preloadWidget() { 86 final LauncherAppWidgetProviderInfo pInfo = mInfo.info; 87 88 if (pInfo.isCustomWidget()) { 89 return false; 90 } 91 final Bundle options = getDefaultOptionsForWidget(mLauncher, mInfo); 92 93 // If there is a configuration activity, do not follow thru bound and inflate. 94 if (mInfo.getHandler().needsConfigure()) { 95 mInfo.bindOptions = options; 96 return false; 97 } 98 99 mBindWidgetRunnable = new Runnable() { 100 @Override 101 public void run() { 102 mWidgetLoadingId = mLauncher.getAppWidgetHost().allocateAppWidgetId(); 103 if (LOGD) { 104 Log.d(TAG, "Binding widget, id: " + mWidgetLoadingId); 105 } 106 if(AppWidgetManagerCompat.getInstance(mLauncher).bindAppWidgetIdIfAllowed( 107 mWidgetLoadingId, pInfo, options)) { 108 109 // Widget id bound. Inflate the widget. 110 mHandler.post(mInflateWidgetRunnable); 111 } 112 } 113 }; 114 115 mInflateWidgetRunnable = new Runnable() { 116 @Override 117 public void run() { 118 if (LOGD) { 119 Log.d(TAG, "Inflating widget, id: " + mWidgetLoadingId); 120 } 121 if (mWidgetLoadingId == -1) { 122 return; 123 } 124 AppWidgetHostView hostView = mLauncher.getAppWidgetHost().createView( 125 (Context) mLauncher, mWidgetLoadingId, pInfo); 126 mInfo.boundWidget = hostView; 127 128 // We used up the widget Id in binding the above view. 129 mWidgetLoadingId = -1; 130 131 hostView.setVisibility(View.INVISIBLE); 132 int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(mInfo); 133 // We want the first widget layout to be the correct size. This will be important 134 // for width size reporting to the AppWidgetManager. 135 DragLayer.LayoutParams lp = new DragLayer.LayoutParams(unScaledSize[0], 136 unScaledSize[1]); 137 lp.x = lp.y = 0; 138 lp.customPosition = true; 139 hostView.setLayoutParams(lp); 140 if (LOGD) { 141 Log.d(TAG, "Adding host view to drag layer"); 142 } 143 mLauncher.getDragLayer().addView(hostView); 144 mView.setTag(mInfo); 145 } 146 }; 147 148 if (LOGD) { 149 Log.d(TAG, "About to bind/inflate widget"); 150 } 151 mHandler.post(mBindWidgetRunnable); 152 return true; 153 } 154 getDefaultOptionsForWidget(Context context, PendingAddWidgetInfo info)155 public static Bundle getDefaultOptionsForWidget(Context context, PendingAddWidgetInfo info) { 156 Rect rect = new Rect(); 157 AppWidgetResizeFrame.getWidgetSizeRanges(context, info.spanX, info.spanY, rect); 158 Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(context, 159 info.componentName, null); 160 161 float density = context.getResources().getDisplayMetrics().density; 162 int xPaddingDips = (int) ((padding.left + padding.right) / density); 163 int yPaddingDips = (int) ((padding.top + padding.bottom) / density); 164 165 Bundle 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 return options; 175 } 176 } 177