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