diff options
Diffstat (limited to 'core/java/android/gadget/GadgetHostView.java')
-rw-r--r-- | core/java/android/gadget/GadgetHostView.java | 206 |
1 files changed, 149 insertions, 57 deletions
diff --git a/core/java/android/gadget/GadgetHostView.java b/core/java/android/gadget/GadgetHostView.java index d92c123..a985bd4 100644 --- a/core/java/android/gadget/GadgetHostView.java +++ b/core/java/android/gadget/GadgetHostView.java @@ -18,7 +18,6 @@ package android.gadget; import android.content.Context; import android.content.pm.PackageManager; -import android.gadget.GadgetInfo; import android.graphics.Color; import android.util.Config; import android.util.Log; @@ -26,12 +25,18 @@ import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.view.animation.Animation; import android.widget.FrameLayout; import android.widget.RemoteViews; import android.widget.TextView; import android.widget.ViewAnimator; -public class GadgetHostView extends ViewAnimator { +/** + * Provides the glue to show gadget views. This class offers automatic animation + * between updates, and will try recycling old views for each incoming + * {@link RemoteViews}. + */ +public class GadgetHostView extends ViewAnimator implements Animation.AnimationListener { static final String TAG = "GadgetHostView"; static final boolean LOGD = Config.LOGD || true; @@ -42,57 +47,93 @@ public class GadgetHostView extends ViewAnimator { return clazz.isAnnotationPresent(RemoteViews.RemoteView.class); } }; - + + Context mLocalContext; + int mGadgetId; - GadgetInfo mInfo; - View mActiveView; - View mStaleView; + GadgetProviderInfo mInfo; - protected int mDefaultGravity = Gravity.CENTER; - + View mActiveView = null; + View mStaleView = null; + + int mActiveLayoutId = -1; + int mStaleLayoutId = -1; + + /** + * Last set of {@link RemoteViews} applied to {@link #mActiveView} + */ + RemoteViews mActiveActions = null; + + /** + * Flag indicating that {@link #mActiveActions} has been applied to + * {@link #mStaleView}, meaning it's readyto recycle. + */ + boolean mStalePrepared = false; + + /** + * Create a host view. Uses default fade animations. + */ public GadgetHostView(Context context) { - super(context); + this(context, android.R.anim.fade_in, android.R.anim.fade_out); } - public void setGadget(int gadgetId, GadgetInfo info) { - if (LOGD) Log.d(TAG, "setGadget is incoming with info=" + info); + /** + * Create a host view. Uses specified animations when pushing + * {@link #updateGadget(RemoteViews)}. + * + * @param animationIn Resource ID of in animation to use + * @param animationOut Resource ID of out animation to use + */ + public GadgetHostView(Context context, int animationIn, int animationOut) { + super(context); + mLocalContext = context; + + // Prepare our default transition animations + setAnimateFirstView(true); + setInAnimation(context, animationIn); + setOutAnimation(context, animationOut); + + // Watch for animation events to prepare recycling + Animation inAnimation = getInAnimation(); + if (inAnimation != null) { + inAnimation.setAnimationListener(this); + } + } + + /** + * Set the gadget that will be displayed by this view. + */ + public void setGadget(int gadgetId, GadgetProviderInfo info) { if (mInfo != null) { // TODO: remove the old view, or whatever } mGadgetId = gadgetId; mInfo = info; - - View defaultView = getDefaultView(); - flipUpdate(defaultView); } - /** - * Trigger actual animation between current and new content in the - * {@link ViewAnimator}. - */ - protected void flipUpdate(View newContent) { - if (LOGD) Log.d(TAG, "pushing an update to surface"); - - // Take requested dimensions from parent, but apply default gravity. - ViewGroup.LayoutParams requested = newContent.getLayoutParams(); - if (requested == null) { - requested = new FrameLayout.LayoutParams(LayoutParams.FILL_PARENT, - LayoutParams.FILL_PARENT); + public int getGadgetId() { + return mGadgetId; + } + + public GadgetProviderInfo getGadgetInfo() { + return mInfo; + } + + public void onAnimationEnd(Animation animation) { + // When our transition animation finishes, we should try bringing our + // newly-stale view up to the current view. + if (mActiveActions != null && + mStaleLayoutId == mActiveActions.getLayoutId()) { + if (LOGD) Log.d(TAG, "after animation, layoutId matched so we're recycling old view"); + mActiveActions.reapply(mLocalContext, mStaleView); + mStalePrepared = true; } - - FrameLayout.LayoutParams params = - new FrameLayout.LayoutParams(requested.width, requested.height); - params.gravity = mDefaultGravity; - newContent.setLayoutParams(params); - - // Add new content and animate to it - addView(newContent); - showNext(); - - // Dispose old stale view - removeView(mStaleView); - mStaleView = mActiveView; - mActiveView = newContent; + } + + public void onAnimationRepeat(Animation animation) { + } + + public void onAnimationStart(Animation animation) { } /** @@ -100,26 +141,42 @@ public class GadgetHostView extends ViewAnimator { * gadget provider. Will animate into these new views as needed. */ public void updateGadget(RemoteViews remoteViews) { - if (LOGD) Log.d(TAG, "updateGadget() with remoteViews = " + remoteViews); + if (LOGD) Log.d(TAG, "updateGadget called"); + boolean recycled = false; View newContent = null; Exception exception = null; - try { - if (remoteViews == null) { - // there is no remoteViews (yet), so use the initial layout - newContent = getDefaultView(); - } else { - // use the RemoteViews - // TODO: try applying RemoteViews to existing staleView if available - newContent = remoteViews.apply(mContext, this); + if (remoteViews == null) { + newContent = getDefaultView(); + } + + // If our stale view has been prepared to match active, and the new + // layout matches, try recycling it + if (newContent == null && mStalePrepared && + remoteViews.getLayoutId() == mStaleLayoutId) { + try { + remoteViews.reapply(mLocalContext, mStaleView); + newContent = mStaleView; + recycled = true; + if (LOGD) Log.d(TAG, "was able to recycled existing layout"); + } catch (RuntimeException e) { + exception = e; + } + } + + // Try normal RemoteView inflation + if (newContent == null) { + try { + newContent = remoteViews.apply(mLocalContext, this); + if (LOGD) Log.d(TAG, "had to inflate new layout"); + } catch (RuntimeException e) { + exception = e; } - } catch (RuntimeException e) { - exception = e; } if (exception != null && LOGD) { - Log.w(TAG, "Error inflating gadget " + mInfo, exception); + Log.w(TAG, "Error inflating gadget " + getGadgetInfo(), exception); } if (newContent == null) { @@ -128,8 +185,44 @@ public class GadgetHostView extends ViewAnimator { if (LOGD) Log.d(TAG, "updateGadget couldn't find any view, so inflating error"); newContent = getErrorView(); } - - flipUpdate(newContent); + + if (!recycled) { + prepareView(newContent); + addView(newContent); + } + + showNext(); + + if (!recycled) { + removeView(mStaleView); + } + + mStalePrepared = false; + mActiveActions = remoteViews; + + mStaleView = mActiveView; + mActiveView = newContent; + + mStaleLayoutId = mActiveLayoutId; + mActiveLayoutId = (remoteViews == null) ? -1 : remoteViews.getLayoutId(); + } + + /** + * Prepare the given view to be shown. This might include adjusting + * {@link FrameLayout.LayoutParams} before inserting. + */ + protected void prepareView(View view) { + // Take requested dimensions from parent, but apply default gravity. + ViewGroup.LayoutParams requested = view.getLayoutParams(); + if (requested == null) { + requested = new FrameLayout.LayoutParams(LayoutParams.FILL_PARENT, + LayoutParams.FILL_PARENT); + } + + FrameLayout.LayoutParams params = + new FrameLayout.LayoutParams(requested.width, requested.height); + params.gravity = Gravity.CENTER; + view.setLayoutParams(params); } /** @@ -141,7 +234,7 @@ public class GadgetHostView extends ViewAnimator { try { if (mInfo != null) { - Context theirContext = mContext.createPackageContext( + Context theirContext = mLocalContext.createPackageContext( mInfo.provider.getPackageName(), 0 /* no flags */); LayoutInflater inflater = (LayoutInflater) theirContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); @@ -173,11 +266,10 @@ public class GadgetHostView extends ViewAnimator { * Inflate and return a view that represents an error state. */ protected View getErrorView() { - TextView tv = new TextView(mContext); + TextView tv = new TextView(mLocalContext); // TODO: move this error string and background color into resources tv.setText("Error inflating gadget"); tv.setBackgroundColor(Color.argb(127, 0, 0, 0)); return tv; } } - |