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