blob: 7936f6535d9f0ee3bb1cab8ce369dbcfe61984b3 [file] [log] [blame]
/*
* 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.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
import android.content.Context;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.PorterDuff;
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.RemotableViewMethod;
import android.view.View;
import android.view.ViewGroup;
import android.view.LayoutInflater.Filter;
import android.view.View.OnClickListener;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import java.lang.Class;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
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<Action> 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(Exception ex) {
super(ex);
}
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
* {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
* to launch the provided {@link PendingIntent}.
*/
private class SetOnClickPendingIntent extends Action {
public SetOnClickPendingIntent(int id, PendingIntent pendingIntent) {
this.viewId = id;
this.pendingIntent = pendingIntent;
}
public SetOnClickPendingIntent(Parcel parcel) {
viewId = parcel.readInt();
pendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(TAG);
dest.writeInt(viewId);
pendingIntent.writeToParcel(dest, 0 /* no flags */);
}
@Override
public void apply(View root) {
final View target = root.findViewById(viewId);
if (target != null && pendingIntent != null) {
OnClickListener listener = new OnClickListener() {
public void onClick(View v) {
try {
// TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
pendingIntent.send();
} catch (CanceledException e) {
throw new ActionException(e.toString());
}
}
};
target.setOnClickListener(listener);
}
}
int viewId;
PendingIntent pendingIntent;
public final static int TAG = 1;
}
/**
* Equivalent to calling a combination of {@link Drawable#setAlpha(int)},
* {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
* and/or {@link Drawable#setLevel(int)} on the {@link Drawable} of a given view.
* <p>
* These operations will be performed on the {@link Drawable} returned by the
* target {@link View#getBackground()} by default. If targetBackground is false,
* we assume the target is an {@link ImageView} and try applying the operations
* to {@link ImageView#getDrawable()}.
* <p>
* You can omit specific calls by marking their values with null or -1.
*/
private class SetDrawableParameters extends Action {
public SetDrawableParameters(int id, boolean targetBackground, int alpha,
int colorFilter, PorterDuff.Mode mode, int level) {
this.viewId = id;
this.targetBackground = targetBackground;
this.alpha = alpha;
this.colorFilter = colorFilter;
this.filterMode = mode;
this.level = level;
}
public SetDrawableParameters(Parcel parcel) {
viewId = parcel.readInt();
targetBackground = parcel.readInt() != 0;
alpha = parcel.readInt();
colorFilter = parcel.readInt();
boolean hasMode = parcel.readInt() != 0;
if (hasMode) {
filterMode = PorterDuff.Mode.valueOf(parcel.readString());
} else {
filterMode = null;
}
level = parcel.readInt();
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(TAG);
dest.writeInt(viewId);
dest.writeInt(targetBackground ? 1 : 0);
dest.writeInt(alpha);
dest.writeInt(colorFilter);
if (filterMode != null) {
dest.writeInt(1);
dest.writeString(filterMode.toString());
} else {
dest.writeInt(0);
}
dest.writeInt(level);
}
@Override
public void apply(View root) {
final View target = root.findViewById(viewId);
if (target == null) {
return;
}
// Pick the correct drawable to modify for this view
Drawable targetDrawable = null;
if (targetBackground) {
targetDrawable = target.getBackground();
} else if (target instanceof ImageView) {
ImageView imageView = (ImageView) target;
targetDrawable = imageView.getDrawable();
}
// Perform modifications only if values are set correctly
if (alpha != -1) {
targetDrawable.setAlpha(alpha);
}
if (colorFilter != -1 && filterMode != null) {
targetDrawable.setColorFilter(colorFilter, filterMode);
}
if (level != -1) {
targetDrawable.setLevel(level);
}
}
int viewId;
boolean targetBackground;
int alpha;
int colorFilter;
PorterDuff.Mode filterMode;
int level;
public final static int TAG = 3;
}
/**
* Base class for the reflection actions.
*/
private class ReflectionAction extends Action {
static final int TAG = 2;
static final int BOOLEAN = 1;
static final int BYTE = 2;
static final int SHORT = 3;
static final int INT = 4;
static final int LONG = 5;
static final int FLOAT = 6;
static final int DOUBLE = 7;
static final int CHAR = 8;
static final int STRING = 9;
static final int CHAR_SEQUENCE = 10;
static final int URI = 11;
static final int BITMAP = 12;
int viewId;
String methodName;
int type;
Object value;
ReflectionAction(int viewId, String methodName, int type, Object value) {
this.viewId = viewId;
this.methodName = methodName;
this.type = type;
this.value = value;
}
ReflectionAction(Parcel in) {
this.viewId = in.readInt();
this.methodName = in.readString();
this.type = in.readInt();
if (false) {
Log.d("RemoteViews", "read viewId=0x" + Integer.toHexString(this.viewId)
+ " methodName=" + this.methodName + " type=" + this.type);
}
switch (this.type) {
case BOOLEAN:
this.value = in.readInt() != 0;
break;
case BYTE:
this.value = in.readByte();
break;
case SHORT:
this.value = (short)in.readInt();
break;
case INT:
this.value = in.readInt();
break;
case LONG:
this.value = in.readLong();
break;
case FLOAT:
this.value = in.readFloat();
break;
case DOUBLE:
this.value = in.readDouble();
break;
case CHAR:
this.value = (char)in.readInt();
break;
case STRING:
this.value = in.readString();
break;
case CHAR_SEQUENCE:
this.value = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
break;
case URI:
this.value = Uri.CREATOR.createFromParcel(in);
break;
case BITMAP:
this.value = Bitmap.CREATOR.createFromParcel(in);
break;
default:
break;
}
}
public void writeToParcel(Parcel out, int flags) {
out.writeInt(TAG);
out.writeInt(this.viewId);
out.writeString(this.methodName);
out.writeInt(this.type);
if (false) {
Log.d("RemoteViews", "write viewId=0x" + Integer.toHexString(this.viewId)
+ " methodName=" + this.methodName + " type=" + this.type);
}
switch (this.type) {
case BOOLEAN:
out.writeInt(((Boolean)this.value).booleanValue() ? 1 : 0);
break;
case BYTE:
out.writeByte(((Byte)this.value).byteValue());
break;
case SHORT:
out.writeInt(((Short)this.value).shortValue());
break;
case INT:
out.writeInt(((Integer)this.value).intValue());
break;
case LONG:
out.writeLong(((Long)this.value).longValue());
break;
case FLOAT:
out.writeFloat(((Float)this.value).floatValue());
break;
case DOUBLE:
out.writeDouble(((Double)this.value).doubleValue());
break;
case CHAR:
out.writeInt((int)((Character)this.value).charValue());
break;
case STRING:
out.writeString((String)this.value);
break;
case CHAR_SEQUENCE:
TextUtils.writeToParcel((CharSequence)this.value, out, flags);
break;
case URI:
((Uri)this.value).writeToParcel(out, flags);
break;
case BITMAP:
((Bitmap)this.value).writeToParcel(out, flags);
break;
default:
break;
}
}
private Class getParameterType() {
switch (this.type) {
case BOOLEAN:
return boolean.class;
case BYTE:
return byte.class;
case SHORT:
return short.class;
case INT:
return int.class;
case LONG:
return long.class;
case FLOAT:
return float.class;
case DOUBLE:
return double.class;
case CHAR:
return char.class;
case STRING:
return String.class;
case CHAR_SEQUENCE:
return CharSequence.class;
case URI:
return Uri.class;
case BITMAP:
return Bitmap.class;
default:
return null;
}
}
@Override
public void apply(View root) {
final View view = root.findViewById(viewId);
if (view == null) {
throw new ActionException("can't find view: 0x" + Integer.toHexString(viewId));
}
Class param = getParameterType();
if (param == null) {
throw new ActionException("bad type: " + this.type);
}
Class klass = view.getClass();
Method method = null;
try {
method = klass.getMethod(this.methodName, getParameterType());
}
catch (NoSuchMethodException ex) {
throw new ActionException("view: " + klass.getName() + " doesn't have method: "
+ this.methodName + "(" + param.getName() + ")");
}
if (!method.isAnnotationPresent(RemotableViewMethod.class)) {
throw new ActionException("view: " + klass.getName()
+ " can't use method with RemoteViews: "
+ this.methodName + "(" + param.getName() + ")");
}
try {
if (false) {
Log.d("RemoteViews", "view: " + klass.getName() + " calling method: "
+ this.methodName + "(" + param.getName() + ") with "
+ (this.value == null ? "null" : this.value.getClass().getName()));
}
method.invoke(view, this.value);
}
catch (Exception ex) {
throw new ActionException(ex);
}
}
}
/**
* 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<Action>(count);
for (int i=0; i<count; i++) {
int tag = parcel.readInt();
switch (tag) {
case SetOnClickPendingIntent.TAG:
mActions.add(new SetOnClickPendingIntent(parcel));
break;
case SetDrawableParameters.TAG:
mActions.add(new SetDrawableParameters(parcel));
break;
case ReflectionAction.TAG:
mActions.add(new ReflectionAction(parcel));
break;
default:
throw new ActionException("Tag " + tag + " not found");
}
}
}
}
public String getPackage() {
return mPackage;
}
public int getLayoutId() {
return mLayoutId;
}
/**
* Add an action to be executed on the remote side when apply is called.
*
* @param a The action to add
*/
private void addAction(Action a) {
if (mActions == null) {
mActions = new ArrayList<Action>();
}
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) {
setInt(viewId, "setVisibility", 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) {
setCharSequence(viewId, "setText", 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) {
setInt(viewId, "setImageResource", 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) {
setUri(viewId, "setImageURI", 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
*/
public void setImageViewBitmap(int viewId, Bitmap bitmap) {
setBitmap(viewId, "setImageBitmap", 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 started True if you want the clock to be started, false if not.
*/
public void setChronometer(int viewId, long base, String format, boolean started) {
setLong(viewId, "setBase", base);
setString(viewId, "setFormat", format);
setBoolean(viewId, "setStarted", started);
}
/**
* Equivalent to calling {@link ProgressBar#setMax ProgressBar.setMax},
* {@link ProgressBar#setProgress ProgressBar.setProgress}, and
* {@link ProgressBar#setIndeterminate ProgressBar.setIndeterminate}
*
* If indeterminate is true, then the values for max and progress are ignored.
*
* @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) {
setBoolean(viewId, "setIndeterminate", indeterminate);
if (!indeterminate) {
setInt(viewId, "setMax", max);
setInt(viewId, "setProgress", progress);
}
}
/**
* Equivalent to calling
* {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
* to launch the provided {@link PendingIntent}.
*
* @param viewId The id of the view that will trigger the {@link PendingIntent} when clicked
* @param pendingIntent The {@link PendingIntent} to send when user clicks
*/
public void setOnClickPendingIntent(int viewId, PendingIntent pendingIntent) {
addAction(new SetOnClickPendingIntent(viewId, pendingIntent));
}
/**
* @hide
* Equivalent to calling a combination of {@link Drawable#setAlpha(int)},
* {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
* and/or {@link Drawable#setLevel(int)} on the {@link Drawable} of a given
* view.
* <p>
* You can omit specific calls by marking their values with null or -1.
*
* @param viewId The id of the view that contains the target
* {@link Drawable}
* @param targetBackground If true, apply these parameters to the
* {@link Drawable} returned by
* {@link android.view.View#getBackground()}. Otherwise, assume
* the target view is an {@link ImageView} and apply them to
* {@link ImageView#getDrawable()}.
* @param alpha Specify an alpha value for the drawable, or -1 to leave
* unchanged.
* @param colorFilter Specify a color for a
* {@link android.graphics.ColorFilter} for this drawable, or -1
* to leave unchanged.
* @param mode Specify a PorterDuff mode for this drawable, or null to leave
* unchanged.
* @param level Specify the level for the drawable, or -1 to leave
* unchanged.
*/
public void setDrawableParameters(int viewId, boolean targetBackground, int alpha,
int colorFilter, PorterDuff.Mode mode, int level) {
addAction(new SetDrawableParameters(viewId, targetBackground, alpha,
colorFilter, mode, level));
}
/**
* Equivalent to calling {@link android.widget.TextView#setTextColor(int)}.
*
* @param viewId The id of the view whose text should change
* @param color Sets the text color for all the states (normal, selected,
* focused) to be this color.
*/
public void setTextColor(int viewId, int color) {
setInt(viewId, "setTextColor", color);
}
/**
* Call a method taking one boolean on a view in the layout for this RemoteViews.
*
* @param viewId The id of the view whose text should change
* @param methodName The name of the method to call.
* @param value The value to pass to the method.
*/
public void setBoolean(int viewId, String methodName, boolean value) {
addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BOOLEAN, value));
}
/**
* Call a method taking one byte on a view in the layout for this RemoteViews.
*
* @param viewId The id of the view whose text should change
* @param methodName The name of the method to call.
* @param value The value to pass to the method.
*/
public void setByte(int viewId, String methodName, byte value) {
addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BYTE, value));
}
/**
* Call a method taking one short on a view in the layout for this RemoteViews.
*
* @param viewId The id of the view whose text should change
* @param methodName The name of the method to call.
* @param value The value to pass to the method.
*/
public void setShort(int viewId, String methodName, short value) {
addAction(new ReflectionAction(viewId, methodName, ReflectionAction.SHORT, value));
}
/**
* Call a method taking one int on a view in the layout for this RemoteViews.
*
* @param viewId The id of the view whose text should change
* @param methodName The name of the method to call.
* @param value The value to pass to the method.
*/
public void setInt(int viewId, String methodName, int value) {
addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INT, value));
}
/**
* Call a method taking one long on a view in the layout for this RemoteViews.
*
* @param viewId The id of the view whose text should change
* @param methodName The name of the method to call.
* @param value The value to pass to the method.
*/
public void setLong(int viewId, String methodName, long value) {
addAction(new ReflectionAction(viewId, methodName, ReflectionAction.LONG, value));
}
/**
* Call a method taking one float on a view in the layout for this RemoteViews.
*
* @param viewId The id of the view whose text should change
* @param methodName The name of the method to call.
* @param value The value to pass to the method.
*/
public void setFloat(int viewId, String methodName, float value) {
addAction(new ReflectionAction(viewId, methodName, ReflectionAction.FLOAT, value));
}
/**
* Call a method taking one double on a view in the layout for this RemoteViews.
*
* @param viewId The id of the view whose text should change
* @param methodName The name of the method to call.
* @param value The value to pass to the method.
*/
public void setDouble(int viewId, String methodName, double value) {
addAction(new ReflectionAction(viewId, methodName, ReflectionAction.DOUBLE, value));
}
/**
* Call a method taking one char on a view in the layout for this RemoteViews.
*
* @param viewId The id of the view whose text should change
* @param methodName The name of the method to call.
* @param value The value to pass to the method.
*/
public void setChar(int viewId, String methodName, char value) {
addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR, value));
}
/**
* Call a method taking one String on a view in the layout for this RemoteViews.
*
* @param viewId The id of the view whose text should change
* @param methodName The name of the method to call.
* @param value The value to pass to the method.
*/
public void setString(int viewId, String methodName, String value) {
addAction(new ReflectionAction(viewId, methodName, ReflectionAction.STRING, value));
}
/**
* Call a method taking one CharSequence on a view in the layout for this RemoteViews.
*
* @param viewId The id of the view whose text should change
* @param methodName The name of the method to call.
* @param value The value to pass to the method.
*/
public void setCharSequence(int viewId, String methodName, CharSequence value) {
addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR_SEQUENCE, value));
}
/**
* Call a method taking one Uri on a view in the layout for this RemoteViews.
*
* @param viewId The id of the view whose text should change
* @param methodName The name of the method to call.
* @param value The value to pass to the method.
*/
public void setUri(int viewId, String methodName, Uri value) {
addAction(new ReflectionAction(viewId, methodName, ReflectionAction.URI, value));
}
/**
* Call a method taking one Bitmap on a view in the layout for this RemoteViews.
* @more
* <p class="note">The bitmap will be flattened into the parcel if this object is
* sent across processes, so it may end up using a lot of memory, and may be fairly slow.</p>
*
* @param viewId The id of the view whose text should change
* @param methodName The name of the method to call.
* @param value The value to pass to the method.
*/
public void setBitmap(int viewId, String methodName, Bitmap value) {
addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BITMAP, value));
}
/**
* Inflates the view hierarchy represented by this object and applies
* all of the actions.
*
* <p><strong>Caller beware: this may throw</strong>
*
* @param context Default context to use
* @param parent Parent that the resulting view hierarchy will be attached to. This method
* does <strong>not</strong> 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.
*
* <p><strong>Caller beware: this may throw</strong>
*
* @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<count; i++) {
Action a = mActions.get(i);
a.writeToParcel(dest, 0);
}
}
/**
* Parcelable.Creator that instantiates RemoteViews objects
*/
public static final Parcelable.Creator<RemoteViews> CREATOR = new Parcelable.Creator<RemoteViews>() {
public RemoteViews createFromParcel(Parcel parcel) {
return new RemoteViews(parcel);
}
public RemoteViews[] newArray(int size) {
return new RemoteViews[size];
}
};
}