package com.android.launcher3.widget; import android.appwidget.AppWidgetHostView; import android.appwidget.AppWidgetManager; import android.content.Context; import android.graphics.Rect; import android.os.Bundle; import android.os.Handler; import android.util.Log; import android.view.View; import com.android.launcher3.AppWidgetResizeFrame; import com.android.launcher3.DropTarget; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.compat.AppWidgetManagerCompat; import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.util.Thunk; public class WidgetHostViewLoader implements DragController.DragListener { private static final String TAG = "WidgetHostViewLoader"; private static final boolean LOGD = false; /* Runnables to handle inflation and binding. */ @Thunk Runnable mInflateWidgetRunnable = null; private Runnable mBindWidgetRunnable = null; // TODO: technically, this class should not have to know the existence of the launcher. @Thunk Launcher mLauncher; @Thunk Handler mHandler; @Thunk final View mView; @Thunk final PendingAddWidgetInfo mInfo; // Widget id generated for binding a widget host view or -1 for invalid id. The id is // not is use as long as it is stored here and can be deleted safely. Once its used, this value // to be set back to -1. @Thunk int mWidgetLoadingId = -1; public WidgetHostViewLoader(Launcher launcher, View view) { mLauncher = launcher; mHandler = new Handler(); mView = view; mInfo = (PendingAddWidgetInfo) view.getTag(); } @Override public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) { preloadWidget(); } @Override public void onDragEnd() { if (LOGD) { Log.d(TAG, "Cleaning up in onDragEnd()..."); } // Cleanup up preloading state. mLauncher.getDragController().removeDragListener(this); mHandler.removeCallbacks(mBindWidgetRunnable); mHandler.removeCallbacks(mInflateWidgetRunnable); // Cleanup widget id if (mWidgetLoadingId != -1) { mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId); mWidgetLoadingId = -1; } // The widget was inflated and added to the DragLayer -- remove it. if (mInfo.boundWidget != null) { if (LOGD) { Log.d(TAG, "...removing widget from drag layer"); } mLauncher.getDragLayer().removeView(mInfo.boundWidget); mLauncher.getAppWidgetHost().deleteAppWidgetId(mInfo.boundWidget.getAppWidgetId()); mInfo.boundWidget = null; } } /** * Start preloading the widget. */ private boolean preloadWidget() { final LauncherAppWidgetProviderInfo pInfo = mInfo.info; if (pInfo.isCustomWidget()) { return false; } final Bundle options = getDefaultOptionsForWidget(mLauncher, mInfo); // If there is a configuration activity, do not follow thru bound and inflate. if (mInfo.getHandler().needsConfigure()) { mInfo.bindOptions = options; return false; } mBindWidgetRunnable = new Runnable() { @Override public void run() { mWidgetLoadingId = mLauncher.getAppWidgetHost().allocateAppWidgetId(); if (LOGD) { Log.d(TAG, "Binding widget, id: " + mWidgetLoadingId); } if(AppWidgetManagerCompat.getInstance(mLauncher).bindAppWidgetIdIfAllowed( mWidgetLoadingId, pInfo, options)) { // Widget id bound. Inflate the widget. mHandler.post(mInflateWidgetRunnable); } } }; mInflateWidgetRunnable = new Runnable() { @Override public void run() { if (LOGD) { Log.d(TAG, "Inflating widget, id: " + mWidgetLoadingId); } if (mWidgetLoadingId == -1) { return; } AppWidgetHostView hostView = mLauncher.getAppWidgetHost().createView( (Context) mLauncher, mWidgetLoadingId, pInfo); mInfo.boundWidget = hostView; // We used up the widget Id in binding the above view. mWidgetLoadingId = -1; hostView.setVisibility(View.INVISIBLE); int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(mInfo); // We want the first widget layout to be the correct size. This will be important // for width size reporting to the AppWidgetManager. DragLayer.LayoutParams lp = new DragLayer.LayoutParams(unScaledSize[0], unScaledSize[1]); lp.x = lp.y = 0; lp.customPosition = true; hostView.setLayoutParams(lp); if (LOGD) { Log.d(TAG, "Adding host view to drag layer"); } mLauncher.getDragLayer().addView(hostView); mView.setTag(mInfo); } }; if (LOGD) { Log.d(TAG, "About to bind/inflate widget"); } mHandler.post(mBindWidgetRunnable); return true; } public static Bundle getDefaultOptionsForWidget(Context context, PendingAddWidgetInfo info) { Rect rect = new Rect(); AppWidgetResizeFrame.getWidgetSizeRanges(context, info.spanX, info.spanY, rect); Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(context, info.componentName, null); float density = context.getResources().getDisplayMetrics().density; int xPaddingDips = (int) ((padding.left + padding.right) / density); int yPaddingDips = (int) ((padding.top + padding.bottom) / density); Bundle options = new Bundle(); options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, rect.left - xPaddingDips); options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, rect.top - yPaddingDips); options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, rect.right - xPaddingDips); options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, rect.bottom - yPaddingDips); return options; } }