Loading core/java/android/appwidget/AppWidgetHostView.java +97 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import android.graphics.Paint; import android.graphics.Rect; import android.os.Build; import android.os.Bundle; import android.os.CancellationSignal; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemClock; Loading @@ -49,6 +50,8 @@ import android.widget.RemoteViews.OnClickHandler; import android.widget.RemoteViewsAdapter.RemoteAdapterConnectionCallback; import android.widget.TextView; import java.util.concurrent.Executor; /** * Provides the glue to show AppWidget views. This class offers automatic animation * between updates, and will try recycling old views for each incoming Loading Loading @@ -87,6 +90,9 @@ public class AppWidgetHostView extends FrameLayout { Paint mOldPaint = new Paint(); private OnClickHandler mOnClickHandler; private Executor mAsyncExecutor; private CancellationSignal mLastExecutionSignal; /** * Create a host view. Uses default fade animations. */ Loading Loading @@ -339,6 +345,22 @@ public class AppWidgetHostView extends FrameLayout { return new FrameLayout.LayoutParams(context, attrs); } /** * Sets an executor which can be used for asynchronously inflating and applying the remoteviews. * @see {@link RemoteViews#applyAsync(Context, ViewGroup, RemoteViews.OnViewAppliedListener, Executor)} * * @param executor the executor to use or null. * @hide */ public void setAsyncExecutor(Executor executor) { if (mLastExecutionSignal != null) { mLastExecutionSignal.cancel(); mLastExecutionSignal = null; } mAsyncExecutor = executor; } /** * Update the AppWidgetProviderInfo for this view, and reset it to the * initial layout. Loading Loading @@ -380,6 +402,11 @@ public class AppWidgetHostView extends FrameLayout { } } if (mLastExecutionSignal != null) { mLastExecutionSignal.cancel(); mLastExecutionSignal = null; } if (remoteViews == null) { if (mViewMode == VIEW_MODE_DEFAULT) { // We've already done this -- nothing to do. Loading @@ -389,6 +416,10 @@ public class AppWidgetHostView extends FrameLayout { mLayoutId = -1; mViewMode = VIEW_MODE_DEFAULT; } else { if (mAsyncExecutor != null) { inflateAsync(remoteViews); return; } // Prepare a local reference to the remote Context so we're ready to // inflate any requested LayoutParams. mRemoteContext = getRemoteContext(); Loading Loading @@ -421,6 +452,10 @@ public class AppWidgetHostView extends FrameLayout { mViewMode = VIEW_MODE_CONTENT; } applyContent(content, recycled, exception); } private void applyContent(View content, boolean recycled, Exception exception) { if (content == null) { if (mViewMode == VIEW_MODE_ERROR) { // We've already done this -- nothing to do. Loading Loading @@ -452,6 +487,68 @@ public class AppWidgetHostView extends FrameLayout { } } private void inflateAsync(RemoteViews remoteViews) { // Prepare a local reference to the remote Context so we're ready to // inflate any requested LayoutParams. mRemoteContext = getRemoteContext(); int layoutId = remoteViews.getLayoutId(); // If our stale view has been prepared to match active, and the new // layout matches, try recycling it if (layoutId == mLayoutId && mView != null) { try { mLastExecutionSignal = remoteViews.reapplyAsync(mContext, mView, mAsyncExecutor, new ViewApplyListener(remoteViews, layoutId, true), mOnClickHandler); } catch (Exception e) { // Reapply failed. Try apply } } if (mLastExecutionSignal == null) { mLastExecutionSignal = remoteViews.applyAsync(mContext, this, mAsyncExecutor, new ViewApplyListener(remoteViews, layoutId, false), mOnClickHandler); } } private class ViewApplyListener implements RemoteViews.OnViewAppliedListener { private final RemoteViews mViews; private final boolean mIsReapply; private final int mLayoutId; public ViewApplyListener(RemoteViews views, int layoutId, boolean isReapply) { mViews = views; mLayoutId = layoutId; mIsReapply = isReapply; } @Override public void onViewApplied(View v) { AppWidgetHostView.this.mLayoutId = mLayoutId; mViewMode = VIEW_MODE_CONTENT; applyContent(v, mIsReapply, null); } @Override public void onError(Exception e) { if (mIsReapply) { // Try a fresh replay mLastExecutionSignal = mViews.applyAsync(mContext, AppWidgetHostView.this, mAsyncExecutor, new ViewApplyListener(mViews, mLayoutId, false), mOnClickHandler); } else { applyContent(null, false, e); } } } /** * Process data-changed notifications for the specified view in the specified * set of {@link RemoteViews} views. Loading core/java/android/view/RemotableViewMethod.java +6 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,12 @@ import java.lang.annotation.Target; @Target({ ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) public @interface RemotableViewMethod { /** * @return Method name which can be called on a background thread. It should have the * same arguments as the original method and should return a {@link Runnable} (or null) * which will be called on the UI thread. */ String asyncImpl() default ""; } Loading core/java/android/widget/ImageView.java +83 −36 Original line number Diff line number Diff line Loading @@ -392,6 +392,26 @@ public class ImageView extends View { return mDrawable; } private class ImageDrawableCallback implements Runnable { private final Drawable drawable; private final Uri uri; private final int resource; ImageDrawableCallback(Drawable drawable, Uri uri, int resource) { this.drawable = drawable; this.uri = uri; this.resource = resource; } @Override public void run() { setImageDrawable(drawable); mUri = uri; mResource = resource; } } /** * Sets a drawable as the content of this ImageView. * Loading @@ -405,7 +425,7 @@ public class ImageView extends View { * * @attr ref android.R.styleable#ImageView_src */ @android.view.RemotableViewMethod @android.view.RemotableViewMethod(asyncImpl="setImageResourceAsync") public void setImageResource(@DrawableRes int resId) { // The resource configuration may have changed, so we should always // try to load the resource even if the resId hasn't changed. Loading @@ -424,6 +444,11 @@ public class ImageView extends View { invalidate(); } /** @hide **/ public Runnable setImageResourceAsync(@DrawableRes int resId) { return new ImageDrawableCallback(getContext().getDrawable(resId), null, resId); } /** * Sets the content of this ImageView to the specified Uri. * Loading @@ -435,7 +460,7 @@ public class ImageView extends View { * * @param uri the Uri of an image, or {@code null} to clear the content */ @android.view.RemotableViewMethod @android.view.RemotableViewMethod(asyncImpl="setImageURIAsync") public void setImageURI(@Nullable Uri uri) { if (mResource != 0 || (mUri != uri && (uri == null || mUri == null || !uri.equals(mUri)))) { updateDrawable(null); Loading @@ -454,6 +479,19 @@ public class ImageView extends View { } } /** @hide **/ public Runnable setImageURIAsync(@Nullable Uri uri) { if (mResource != 0 || (mUri != uri && (uri == null || mUri == null || !uri.equals(mUri)))) { Drawable d = uri == null ? null : getDrawableFromUri(uri); if (d == null) { // Do not set the URI if the drawable couldn't be loaded. uri = null; } return new ImageDrawableCallback(d, uri, 0); } return null; } /** * Sets a drawable as the content of this ImageView. * Loading Loading @@ -490,11 +528,16 @@ public class ImageView extends View { * @param icon an Icon holding the desired image, or {@code null} to clear * the content */ @android.view.RemotableViewMethod @android.view.RemotableViewMethod(asyncImpl="setImageIconAsync") public void setImageIcon(@Nullable Icon icon) { setImageDrawable(icon == null ? null : icon.loadDrawable(mContext)); } /** @hide **/ public Runnable setImageIconAsync(@Nullable Icon icon) { return new ImageDrawableCallback(icon == null ? null : icon.loadDrawable(mContext), null, 0); } /** * Applies a tint to the image drawable. Does not modify the current tint * mode, which is {@link PorterDuff.Mode#SRC_IN} by default. Loading Loading @@ -786,8 +829,7 @@ public class ImageView extends View { return; } final Resources res = getResources(); if (res == null) { if (getResources() == null) { return; } Loading @@ -802,48 +844,53 @@ public class ImageView extends View { mUri = null; } } else if (mUri != null) { final String scheme = mUri.getScheme(); d = getDrawableFromUri(mUri); if (d == null) { Log.w(LOG_TAG, "resolveUri failed on bad bitmap uri: " + mUri); // Don't try again. mUri = null; } } else { return; } updateDrawable(d); } private Drawable getDrawableFromUri(Uri uri) { final String scheme = uri.getScheme(); if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)) { try { // Load drawable through Resources, to get the source density information ContentResolver.OpenResourceIdResult r = mContext.getContentResolver().getResourceId(mUri); d = r.r.getDrawable(r.id, mContext.getTheme()); mContext.getContentResolver().getResourceId(uri); return r.r.getDrawable(r.id, mContext.getTheme()); } catch (Exception e) { Log.w(LOG_TAG, "Unable to open content: " + mUri, e); Log.w(LOG_TAG, "Unable to open content: " + uri, e); } } else if (ContentResolver.SCHEME_CONTENT.equals(scheme) || ContentResolver.SCHEME_FILE.equals(scheme)) { InputStream stream = null; try { stream = mContext.getContentResolver().openInputStream(mUri); d = Drawable.createFromResourceStream( mUseCorrectStreamDensity ? res : null, null, stream, null); stream = mContext.getContentResolver().openInputStream(uri); return Drawable.createFromResourceStream( mUseCorrectStreamDensity ? getResources() : null, null, stream, null); } catch (Exception e) { Log.w(LOG_TAG, "Unable to open content: " + mUri, e); Log.w(LOG_TAG, "Unable to open content: " + uri, e); } finally { if (stream != null) { try { stream.close(); } catch (IOException e) { Log.w(LOG_TAG, "Unable to close content: " + mUri, e); Log.w(LOG_TAG, "Unable to close content: " + uri, e); } } } } else { d = Drawable.createFromPath(mUri.toString()); return Drawable.createFromPath(uri.toString()); } if (d == null) { Log.w(LOG_TAG, "resolveUri failed on bad bitmap uri: " + mUri); // Don't try again. mUri = null; } } else { return; } updateDrawable(d); return null; } @Override Loading core/java/android/widget/RemoteViews.java +456 −9 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
core/java/android/appwidget/AppWidgetHostView.java +97 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import android.graphics.Paint; import android.graphics.Rect; import android.os.Build; import android.os.Bundle; import android.os.CancellationSignal; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemClock; Loading @@ -49,6 +50,8 @@ import android.widget.RemoteViews.OnClickHandler; import android.widget.RemoteViewsAdapter.RemoteAdapterConnectionCallback; import android.widget.TextView; import java.util.concurrent.Executor; /** * Provides the glue to show AppWidget views. This class offers automatic animation * between updates, and will try recycling old views for each incoming Loading Loading @@ -87,6 +90,9 @@ public class AppWidgetHostView extends FrameLayout { Paint mOldPaint = new Paint(); private OnClickHandler mOnClickHandler; private Executor mAsyncExecutor; private CancellationSignal mLastExecutionSignal; /** * Create a host view. Uses default fade animations. */ Loading Loading @@ -339,6 +345,22 @@ public class AppWidgetHostView extends FrameLayout { return new FrameLayout.LayoutParams(context, attrs); } /** * Sets an executor which can be used for asynchronously inflating and applying the remoteviews. * @see {@link RemoteViews#applyAsync(Context, ViewGroup, RemoteViews.OnViewAppliedListener, Executor)} * * @param executor the executor to use or null. * @hide */ public void setAsyncExecutor(Executor executor) { if (mLastExecutionSignal != null) { mLastExecutionSignal.cancel(); mLastExecutionSignal = null; } mAsyncExecutor = executor; } /** * Update the AppWidgetProviderInfo for this view, and reset it to the * initial layout. Loading Loading @@ -380,6 +402,11 @@ public class AppWidgetHostView extends FrameLayout { } } if (mLastExecutionSignal != null) { mLastExecutionSignal.cancel(); mLastExecutionSignal = null; } if (remoteViews == null) { if (mViewMode == VIEW_MODE_DEFAULT) { // We've already done this -- nothing to do. Loading @@ -389,6 +416,10 @@ public class AppWidgetHostView extends FrameLayout { mLayoutId = -1; mViewMode = VIEW_MODE_DEFAULT; } else { if (mAsyncExecutor != null) { inflateAsync(remoteViews); return; } // Prepare a local reference to the remote Context so we're ready to // inflate any requested LayoutParams. mRemoteContext = getRemoteContext(); Loading Loading @@ -421,6 +452,10 @@ public class AppWidgetHostView extends FrameLayout { mViewMode = VIEW_MODE_CONTENT; } applyContent(content, recycled, exception); } private void applyContent(View content, boolean recycled, Exception exception) { if (content == null) { if (mViewMode == VIEW_MODE_ERROR) { // We've already done this -- nothing to do. Loading Loading @@ -452,6 +487,68 @@ public class AppWidgetHostView extends FrameLayout { } } private void inflateAsync(RemoteViews remoteViews) { // Prepare a local reference to the remote Context so we're ready to // inflate any requested LayoutParams. mRemoteContext = getRemoteContext(); int layoutId = remoteViews.getLayoutId(); // If our stale view has been prepared to match active, and the new // layout matches, try recycling it if (layoutId == mLayoutId && mView != null) { try { mLastExecutionSignal = remoteViews.reapplyAsync(mContext, mView, mAsyncExecutor, new ViewApplyListener(remoteViews, layoutId, true), mOnClickHandler); } catch (Exception e) { // Reapply failed. Try apply } } if (mLastExecutionSignal == null) { mLastExecutionSignal = remoteViews.applyAsync(mContext, this, mAsyncExecutor, new ViewApplyListener(remoteViews, layoutId, false), mOnClickHandler); } } private class ViewApplyListener implements RemoteViews.OnViewAppliedListener { private final RemoteViews mViews; private final boolean mIsReapply; private final int mLayoutId; public ViewApplyListener(RemoteViews views, int layoutId, boolean isReapply) { mViews = views; mLayoutId = layoutId; mIsReapply = isReapply; } @Override public void onViewApplied(View v) { AppWidgetHostView.this.mLayoutId = mLayoutId; mViewMode = VIEW_MODE_CONTENT; applyContent(v, mIsReapply, null); } @Override public void onError(Exception e) { if (mIsReapply) { // Try a fresh replay mLastExecutionSignal = mViews.applyAsync(mContext, AppWidgetHostView.this, mAsyncExecutor, new ViewApplyListener(mViews, mLayoutId, false), mOnClickHandler); } else { applyContent(null, false, e); } } } /** * Process data-changed notifications for the specified view in the specified * set of {@link RemoteViews} views. Loading
core/java/android/view/RemotableViewMethod.java +6 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,12 @@ import java.lang.annotation.Target; @Target({ ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) public @interface RemotableViewMethod { /** * @return Method name which can be called on a background thread. It should have the * same arguments as the original method and should return a {@link Runnable} (or null) * which will be called on the UI thread. */ String asyncImpl() default ""; } Loading
core/java/android/widget/ImageView.java +83 −36 Original line number Diff line number Diff line Loading @@ -392,6 +392,26 @@ public class ImageView extends View { return mDrawable; } private class ImageDrawableCallback implements Runnable { private final Drawable drawable; private final Uri uri; private final int resource; ImageDrawableCallback(Drawable drawable, Uri uri, int resource) { this.drawable = drawable; this.uri = uri; this.resource = resource; } @Override public void run() { setImageDrawable(drawable); mUri = uri; mResource = resource; } } /** * Sets a drawable as the content of this ImageView. * Loading @@ -405,7 +425,7 @@ public class ImageView extends View { * * @attr ref android.R.styleable#ImageView_src */ @android.view.RemotableViewMethod @android.view.RemotableViewMethod(asyncImpl="setImageResourceAsync") public void setImageResource(@DrawableRes int resId) { // The resource configuration may have changed, so we should always // try to load the resource even if the resId hasn't changed. Loading @@ -424,6 +444,11 @@ public class ImageView extends View { invalidate(); } /** @hide **/ public Runnable setImageResourceAsync(@DrawableRes int resId) { return new ImageDrawableCallback(getContext().getDrawable(resId), null, resId); } /** * Sets the content of this ImageView to the specified Uri. * Loading @@ -435,7 +460,7 @@ public class ImageView extends View { * * @param uri the Uri of an image, or {@code null} to clear the content */ @android.view.RemotableViewMethod @android.view.RemotableViewMethod(asyncImpl="setImageURIAsync") public void setImageURI(@Nullable Uri uri) { if (mResource != 0 || (mUri != uri && (uri == null || mUri == null || !uri.equals(mUri)))) { updateDrawable(null); Loading @@ -454,6 +479,19 @@ public class ImageView extends View { } } /** @hide **/ public Runnable setImageURIAsync(@Nullable Uri uri) { if (mResource != 0 || (mUri != uri && (uri == null || mUri == null || !uri.equals(mUri)))) { Drawable d = uri == null ? null : getDrawableFromUri(uri); if (d == null) { // Do not set the URI if the drawable couldn't be loaded. uri = null; } return new ImageDrawableCallback(d, uri, 0); } return null; } /** * Sets a drawable as the content of this ImageView. * Loading Loading @@ -490,11 +528,16 @@ public class ImageView extends View { * @param icon an Icon holding the desired image, or {@code null} to clear * the content */ @android.view.RemotableViewMethod @android.view.RemotableViewMethod(asyncImpl="setImageIconAsync") public void setImageIcon(@Nullable Icon icon) { setImageDrawable(icon == null ? null : icon.loadDrawable(mContext)); } /** @hide **/ public Runnable setImageIconAsync(@Nullable Icon icon) { return new ImageDrawableCallback(icon == null ? null : icon.loadDrawable(mContext), null, 0); } /** * Applies a tint to the image drawable. Does not modify the current tint * mode, which is {@link PorterDuff.Mode#SRC_IN} by default. Loading Loading @@ -786,8 +829,7 @@ public class ImageView extends View { return; } final Resources res = getResources(); if (res == null) { if (getResources() == null) { return; } Loading @@ -802,48 +844,53 @@ public class ImageView extends View { mUri = null; } } else if (mUri != null) { final String scheme = mUri.getScheme(); d = getDrawableFromUri(mUri); if (d == null) { Log.w(LOG_TAG, "resolveUri failed on bad bitmap uri: " + mUri); // Don't try again. mUri = null; } } else { return; } updateDrawable(d); } private Drawable getDrawableFromUri(Uri uri) { final String scheme = uri.getScheme(); if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)) { try { // Load drawable through Resources, to get the source density information ContentResolver.OpenResourceIdResult r = mContext.getContentResolver().getResourceId(mUri); d = r.r.getDrawable(r.id, mContext.getTheme()); mContext.getContentResolver().getResourceId(uri); return r.r.getDrawable(r.id, mContext.getTheme()); } catch (Exception e) { Log.w(LOG_TAG, "Unable to open content: " + mUri, e); Log.w(LOG_TAG, "Unable to open content: " + uri, e); } } else if (ContentResolver.SCHEME_CONTENT.equals(scheme) || ContentResolver.SCHEME_FILE.equals(scheme)) { InputStream stream = null; try { stream = mContext.getContentResolver().openInputStream(mUri); d = Drawable.createFromResourceStream( mUseCorrectStreamDensity ? res : null, null, stream, null); stream = mContext.getContentResolver().openInputStream(uri); return Drawable.createFromResourceStream( mUseCorrectStreamDensity ? getResources() : null, null, stream, null); } catch (Exception e) { Log.w(LOG_TAG, "Unable to open content: " + mUri, e); Log.w(LOG_TAG, "Unable to open content: " + uri, e); } finally { if (stream != null) { try { stream.close(); } catch (IOException e) { Log.w(LOG_TAG, "Unable to close content: " + mUri, e); Log.w(LOG_TAG, "Unable to close content: " + uri, e); } } } } else { d = Drawable.createFromPath(mUri.toString()); return Drawable.createFromPath(uri.toString()); } if (d == null) { Log.w(LOG_TAG, "resolveUri failed on bad bitmap uri: " + mUri); // Don't try again. mUri = null; } } else { return; } updateDrawable(d); return null; } @Override Loading
core/java/android/widget/RemoteViews.java +456 −9 File changed.Preview size limit exceeded, changes collapsed. Show changes