diff options
author | Hyunyoung Song <hyunyoungs@google.com> | 2015-04-23 15:17:50 -0700 |
---|---|---|
committer | Hyunyoung Song <hyunyoungs@google.com> | 2015-04-23 15:17:50 -0700 |
commit | b99ff3e83270e113f6182e337c4f7b0223bad92b (patch) | |
tree | 357fb2fee1efa1ea7248a420f59c0436860545c0 /src/com/android/launcher3/widget/WidgetHostViewLoader.java | |
parent | 685f98b2e23465c7f12839590663b53a40e36527 (diff) | |
download | android_packages_apps_Trebuchet-b99ff3e83270e113f6182e337c4f7b0223bad92b.tar.gz android_packages_apps_Trebuchet-b99ff3e83270e113f6182e337c4f7b0223bad92b.tar.bz2 android_packages_apps_Trebuchet-b99ff3e83270e113f6182e337c4f7b0223bad92b.zip |
Add drop animation / Toast to widgettray
- show instruction toast on clicking the widget
- Add animation when widget drops on the workspace.
Added WidgetHostViewLoader to handle short press and assign widget
host view to enable animation
b/19897708
Change-Id: Iec36d72cb21bf09343d0beeb31a09bf8b0cb5e0d
Diffstat (limited to 'src/com/android/launcher3/widget/WidgetHostViewLoader.java')
-rw-r--r-- | src/com/android/launcher3/widget/WidgetHostViewLoader.java | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/src/com/android/launcher3/widget/WidgetHostViewLoader.java b/src/com/android/launcher3/widget/WidgetHostViewLoader.java new file mode 100644 index 000000000..d65455053 --- /dev/null +++ b/src/com/android/launcher3/widget/WidgetHostViewLoader.java @@ -0,0 +1,197 @@ +package com.android.launcher3.widget; + +import android.appwidget.AppWidgetHostView; +import android.appwidget.AppWidgetManager; +import android.content.Context; +import android.graphics.Rect; +import android.os.Build; +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.DragLayer; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherAppWidgetProviderInfo; +import com.android.launcher3.compat.AppWidgetManagerCompat; + +public class WidgetHostViewLoader { + + private static final boolean DEBUG = false; + private static final String TAG = "WidgetHostViewLoader"; + + /* constants used for widget loading state. */ + private static final int WIDGET_NO_CLEANUP_REQUIRED = -1; + private static final int WIDGET_PRELOAD_PENDING = 0; + private static final int WIDGET_BOUND = 1; + private static final int WIDGET_INFLATED = 2; + + int mState = WIDGET_NO_CLEANUP_REQUIRED; + + /* Runnables to handle inflation and binding. */ + private Runnable mInflateWidgetRunnable = null; + private Runnable mBindWidgetRunnable = null; + + /* Id of the widget being handled. */ + int mWidgetLoadingId = -1; + PendingAddWidgetInfo mCreateWidgetInfo = null; + + // TODO: technically, this class should not have to know the existence of the launcher. + private Launcher mLauncher; + private Handler mHandler; + + public WidgetHostViewLoader(Launcher launcher) { + mLauncher = launcher; + mHandler = new Handler(); + } + + /** + * Start loading the widget. + */ + public void load(View v) { + if (mCreateWidgetInfo != null) { + // Just in case the cleanup process wasn't properly executed. + finish(false); + } + boolean status = false; + if (v.getTag() instanceof PendingAddWidgetInfo) { + mCreateWidgetInfo = new PendingAddWidgetInfo((PendingAddWidgetInfo) v.getTag()); + status = preloadWidget(v, mCreateWidgetInfo); + } + if (DEBUG) { + Log.d(TAG, String.format("load started on [state=%d, status=%s]", mState, status)); + } + } + + + /** + * Clean up according to what the last known state was. + * @param widgetIdUsed {@code true} if the widgetId was consumed which can happen only + * when view is fully inflated + */ + public void finish(boolean widgetIdUsed) { + if (DEBUG) { + Log.d(TAG, String.format("cancel on state [%d] widgetId=[%d]", + mState, mWidgetLoadingId)); + } + + // If the widget was not added, we may need to do further cleanup. + PendingAddWidgetInfo info = mCreateWidgetInfo; + mCreateWidgetInfo = null; + + if (mState == WIDGET_PRELOAD_PENDING) { + // We never did any preloading, so just remove pending callbacks to do so + mHandler.removeCallbacks(mBindWidgetRunnable); + mHandler.removeCallbacks(mInflateWidgetRunnable); + } else if (mState == WIDGET_BOUND) { + // Delete the widget id which was allocated + if (mWidgetLoadingId != -1 && !info.isCustomWidget()) { + mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId); + } + + // We never got around to inflating the widget, so remove the callback to do so. + mHandler.removeCallbacks(mInflateWidgetRunnable); + } else if (mState == WIDGET_INFLATED && !widgetIdUsed) { + // Delete the widget id which was allocated + if (mWidgetLoadingId != -1 && !info.isCustomWidget()) { + mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId); + } + + // The widget was inflated and added to the DragLayer -- remove it. + AppWidgetHostView widget = info.boundWidget; + mLauncher.getDragLayer().removeView(widget); + } + setState(WIDGET_NO_CLEANUP_REQUIRED); + mWidgetLoadingId = -1; + } + + private boolean preloadWidget(final View v, final PendingAddWidgetInfo info) { + final LauncherAppWidgetProviderInfo pInfo = info.info; + + final Bundle options = pInfo.isCustomWidget ? null : + getDefaultOptionsForWidget(mLauncher, info); + + // If there is a configuration activity, do not follow thru bound and inflate. + if (pInfo.configure != null) { + info.bindOptions = options; + return false; + } + setState(WIDGET_PRELOAD_PENDING); + mBindWidgetRunnable = new Runnable() { + @Override + public void run() { + if (pInfo.isCustomWidget) { + setState(WIDGET_BOUND); + return; + } + + mWidgetLoadingId = mLauncher.getAppWidgetHost().allocateAppWidgetId(); + if(AppWidgetManagerCompat.getInstance(mLauncher).bindAppWidgetIdIfAllowed( + mWidgetLoadingId, pInfo, options)) { + setState(WIDGET_BOUND); + } + } + }; + mHandler.post(mBindWidgetRunnable); + + mInflateWidgetRunnable = new Runnable() { + @Override + public void run() { + if (mState != WIDGET_BOUND) { + return; + } + AppWidgetHostView hostView = mLauncher.getAppWidgetHost().createView( + (Context) mLauncher, mWidgetLoadingId, pInfo); + info.boundWidget = hostView; + setState(WIDGET_INFLATED); + hostView.setVisibility(View.INVISIBLE); + int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(info, false); + + // 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); + mLauncher.getDragLayer().addView(hostView); + v.setTag(info); + } + }; + mHandler.post(mInflateWidgetRunnable); + return true; + } + + public static Bundle getDefaultOptionsForWidget(Launcher launcher, PendingAddWidgetInfo info) { + Bundle options = null; + Rect rect = new Rect(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + AppWidgetResizeFrame.getWidgetSizeRanges(launcher, info.spanX, info.spanY, rect); + Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(launcher, + info.componentName, null); + + float density = launcher.getResources().getDisplayMetrics().density; + int xPaddingDips = (int) ((padding.left + padding.right) / density); + int yPaddingDips = (int) ((padding.top + padding.bottom) / density); + + 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; + } + + private void setState(int state) { + if (DEBUG) { + Log.d(TAG, String.format(" state [%d -> %d]", mState, state)); + } + mState = state; + } +} |