/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.widget; import android.content.Context; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.LayoutInflater.Filter; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.ArrayList; /** * A class that describes a view hierarchy that can be displayed in * another process. The hierarchy is inflated from a layout resource * file, and this class provides some basic operations for modifying * the content of the inflated hierarchy. */ public class RemoteViews implements Parcelable, Filter { private static final String LOG_TAG = "RemoteViews"; /** * The package name of the package containing the layout * resource. (Added to the parcel) */ private String mPackage; /** * The resource ID of the layout file. (Added to the parcel) */ private int mLayoutId; /** * The Context object used to inflate the layout file. Also may * be used by actions if they need access to the senders resources. */ private Context mContext; /** * An array of actions to perform on the view tree once it has been * inflated */ private ArrayList mActions; /** * This annotation indicates that a subclass of View is alllowed to be used with the * {@link android.widget.RemoteViews} mechanism. */ @Target({ ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) public @interface RemoteView { } /** * Exception to send when something goes wrong executing an action * */ public static class ActionException extends RuntimeException { public ActionException(String message) { super(message); } } /** * Base class for all actions that can be performed on an * inflated view. * */ private abstract static class Action implements Parcelable { public abstract void apply(View root) throws ActionException; public int describeContents() { return 0; } }; /** * Equivalent to calling View.setVisibility */ private class SetViewVisibility extends Action { public SetViewVisibility(int id, int vis) { viewId = id; visibility = vis; } public SetViewVisibility(Parcel parcel) { viewId = parcel.readInt(); visibility = parcel.readInt(); } public void writeToParcel(Parcel dest, int flags) { dest.writeInt(TAG); dest.writeInt(viewId); dest.writeInt(visibility); } @Override public void apply(View root) { View target = root.findViewById(viewId); if (target != null) { target.setVisibility(visibility); } } private int viewId; private int visibility; public final static int TAG = 0; } /** * Equivalent to calling TextView.setText */ private class SetTextViewText extends Action { public SetTextViewText(int id, CharSequence t) { viewId = id; text = t; } public SetTextViewText(Parcel parcel) { viewId = parcel.readInt(); text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); } public void writeToParcel(Parcel dest, int flags) { dest.writeInt(TAG); dest.writeInt(viewId); TextUtils.writeToParcel(text, dest, flags); } @Override public void apply(View root) { TextView target = (TextView) root.findViewById(viewId); if (target != null) { target.setText(text); } } int viewId; CharSequence text; public final static int TAG = 1; } /** * Equivalent to calling ImageView.setResource */ private class SetImageViewResource extends Action { public SetImageViewResource(int id, int src) { viewId = id; srcId = src; } public SetImageViewResource(Parcel parcel) { viewId = parcel.readInt(); srcId = parcel.readInt(); } public void writeToParcel(Parcel dest, int flags) { dest.writeInt(TAG); dest.writeInt(viewId); dest.writeInt(srcId); } @Override public void apply(View root) { ImageView target = (ImageView) root.findViewById(viewId); Drawable d = mContext.getResources().getDrawable(srcId); if (target != null) { target.setImageDrawable(d); } } int viewId; int srcId; public final static int TAG = 2; } /** * Equivalent to calling ImageView.setImageURI */ private class SetImageViewUri extends Action { public SetImageViewUri(int id, Uri u) { viewId = id; uri = u; } public SetImageViewUri(Parcel parcel) { viewId = parcel.readInt(); uri = Uri.CREATOR.createFromParcel(parcel); } public void writeToParcel(Parcel dest, int flags) { dest.writeInt(TAG); dest.writeInt(viewId); Uri.writeToParcel(dest, uri); } @Override public void apply(View root) { ImageView target = (ImageView) root.findViewById(viewId); if (target != null) { target.setImageURI(uri); } } int viewId; Uri uri; public final static int TAG = 3; } /** * Equivalent to calling ImageView.setImageBitmap */ private class SetImageViewBitmap extends Action { public SetImageViewBitmap(int id, Bitmap src) { viewId = id; bitmap = src; } public SetImageViewBitmap(Parcel parcel) { viewId = parcel.readInt(); bitmap = Bitmap.CREATOR.createFromParcel(parcel); } public void writeToParcel(Parcel dest, int flags) { dest.writeInt(TAG); dest.writeInt(viewId); if (bitmap != null) { bitmap.writeToParcel(dest, flags); } } @Override public void apply(View root) { if (bitmap != null) { ImageView target = (ImageView) root.findViewById(viewId); Drawable d = new BitmapDrawable(bitmap); if (target != null) { target.setImageDrawable(d); } } } int viewId; Bitmap bitmap; public final static int TAG = 4; } /** * Equivalent to calling Chronometer.setBase, Chronometer.setFormat, * and Chronometer.start/stop. */ private class SetChronometer extends Action { public SetChronometer(int id, long base, String format, boolean running) { this.viewId = id; this.base = base; this.format = format; this.running = running; } public SetChronometer(Parcel parcel) { viewId = parcel.readInt(); base = parcel.readLong(); format = parcel.readString(); running = parcel.readInt() != 0; } public void writeToParcel(Parcel dest, int flags) { dest.writeInt(TAG); dest.writeInt(viewId); dest.writeLong(base); dest.writeString(format); dest.writeInt(running ? 1 : 0); } @Override public void apply(View root) { Chronometer target = (Chronometer) root.findViewById(viewId); if (target != null) { target.setBase(base); target.setFormat(format); if (running) { target.start(); } else { target.stop(); } } } int viewId; boolean running; long base; String format; public final static int TAG = 5; } /** * Equivalent to calling ProgressBar.setMax, ProgressBar.setProgress and * ProgressBar.setIndeterminate */ private class SetProgressBar extends Action { public SetProgressBar(int id, int max, int progress, boolean indeterminate) { this.viewId = id; this.progress = progress; this.max = max; this.indeterminate = indeterminate; } public SetProgressBar(Parcel parcel) { viewId = parcel.readInt(); progress = parcel.readInt(); max = parcel.readInt(); indeterminate = parcel.readInt() != 0; } public void writeToParcel(Parcel dest, int flags) { dest.writeInt(TAG); dest.writeInt(viewId); dest.writeInt(progress); dest.writeInt(max); dest.writeInt(indeterminate ? 1 : 0); } @Override public void apply(View root) { ProgressBar target = (ProgressBar) root.findViewById(viewId); if (target != null) { target.setIndeterminate(indeterminate); if (!indeterminate) { target.setMax(max); target.setProgress(progress); } } } int viewId; boolean indeterminate; int progress; int max; public final static int TAG = 6; } /** * Create a new RemoteViews object that will display the views contained * in the specified layout file. * * @param packageName Name of the package that contains the layout resource * @param layoutId The id of the layout resource */ public RemoteViews(String packageName, int layoutId) { mPackage = packageName; mLayoutId = layoutId; } /** * Reads a RemoteViews object from a parcel. * * @param parcel */ public RemoteViews(Parcel parcel) { mPackage = parcel.readString(); mLayoutId = parcel.readInt(); int count = parcel.readInt(); if (count > 0) { mActions = new ArrayList(count); for (int i=0; i(); } mActions.add(a); } /** * Equivalent to calling View.setVisibility * * @param viewId The id of the view whose visibility should change * @param visibility The new visibility for the view */ public void setViewVisibility(int viewId, int visibility) { addAction(new SetViewVisibility(viewId, visibility)); } /** * Equivalent to calling TextView.setText * * @param viewId The id of the view whose text should change * @param text The new text for the view */ public void setTextViewText(int viewId, CharSequence text) { addAction(new SetTextViewText(viewId, text)); } /** * Equivalent to calling ImageView.setImageResource * * @param viewId The id of the view whose drawable should change * @param srcId The new resource id for the drawable */ public void setImageViewResource(int viewId, int srcId) { addAction(new SetImageViewResource(viewId, srcId)); } /** * Equivalent to calling ImageView.setImageURI * * @param viewId The id of the view whose drawable should change * @param uri The Uri for the image */ public void setImageViewUri(int viewId, Uri uri) { addAction(new SetImageViewUri(viewId, uri)); } /** * Equivalent to calling ImageView.setImageBitmap * * @param viewId The id of the view whose drawable should change * @param bitmap The new Bitmap for the drawable * * @hide pending API Council approval to extend the public API */ public void setImageViewBitmap(int viewId, Bitmap bitmap) { addAction(new SetImageViewBitmap(viewId, bitmap)); } /** * Equivalent to calling {@link Chronometer#setBase Chronometer.setBase}, * {@link Chronometer#setFormat Chronometer.setFormat}, * and {@link Chronometer#start Chronometer.start()} or * {@link Chronometer#stop Chronometer.stop()}. * * @param viewId The id of the view whose text should change * @param base The time at which the timer would have read 0:00. This * time should be based off of * {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()}. * @param format The Chronometer format string, or null to * simply display the timer value. * @param running True if you want the clock to be running, false if not. */ public void setChronometer(int viewId, long base, String format, boolean running) { addAction(new SetChronometer(viewId, base, format, running)); } /** * Equivalent to calling {@link ProgressBar#setMax ProgressBar.setMax}, * {@link ProgressBar#setProgress ProgressBar.setProgress}, and * {@link ProgressBar#setIndeterminate ProgressBar.setIndeterminate} * * @param viewId The id of the view whose text should change * @param max The 100% value for the progress bar * @param progress The current value of the progress bar. * @param indeterminate True if the progress bar is indeterminate, * false if not. */ public void setProgressBar(int viewId, int max, int progress, boolean indeterminate) { addAction(new SetProgressBar(viewId, max, progress, indeterminate)); } /** * Inflates the view hierarchy represented by this object and applies * all of the actions. * *

Caller beware: this may throw * * @param context Default context to use * @param parent Parent that the resulting view hierarchy will be attached to. This method * does not attach the hierarchy. The caller should do so when appropriate. * @return The inflated view hierarchy */ public View apply(Context context, ViewGroup parent) { View result = null; Context c = prepareContext(context); Resources r = c.getResources(); LayoutInflater inflater = (LayoutInflater) c .getSystemService(Context.LAYOUT_INFLATER_SERVICE); inflater = inflater.cloneInContext(c); inflater.setFilter(this); result = inflater.inflate(mLayoutId, parent, false); performApply(result); return result; } /** * Applies all of the actions to the provided view. * *

Caller beware: this may throw * * @param v The view to apply the actions to. This should be the result of * the {@link #apply(Context,ViewGroup)} call. */ public void reapply(Context context, View v) { prepareContext(context); performApply(v); } private void performApply(View v) { if (mActions != null) { final int count = mActions.size(); for (int i = 0; i < count; i++) { Action a = mActions.get(i); a.apply(v); } } } private Context prepareContext(Context context) { Context c = null; String packageName = mPackage; if (packageName != null) { try { c = context.createPackageContext(packageName, 0); } catch (NameNotFoundException e) { Log.e(LOG_TAG, "Package name " + packageName + " not found"); c = context; } } else { c = context; } mContext = c; return c; } /* (non-Javadoc) * Used to restrict the views which can be inflated * * @see android.view.LayoutInflater.Filter#onLoadClass(java.lang.Class) */ public boolean onLoadClass(Class clazz) { return clazz.isAnnotationPresent(RemoteView.class); } public int describeContents() { return 0; } public void writeToParcel(Parcel dest, int flags) { dest.writeString(mPackage); dest.writeInt(mLayoutId); int count; if (mActions != null) { count = mActions.size(); } else { count = 0; } dest.writeInt(count); for (int i=0; i CREATOR = new Parcelable.Creator() { public RemoteViews createFromParcel(Parcel parcel) { return new RemoteViews(parcel); } public RemoteViews[] newArray(int size) { return new RemoteViews[size]; } }; }