Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit db4a8fc6 authored by Adam Cohen's avatar Adam Cohen Committed by Android Git Automerger
Browse files

am 2938ac3f: Merge "Enabling RemoteViews to have a landscape and portrait layout" into jb-dev

* commit '2938ac3f':
  Enabling RemoteViews to have a landscape and portrait layout
parents a5dd37cd 2938ac3f
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -27842,6 +27842,7 @@ package android.widget {
  public class RemoteViews implements android.view.LayoutInflater.Filter android.os.Parcelable {
    ctor public RemoteViews(java.lang.String, int);
    ctor public RemoteViews(android.widget.RemoteViews, android.widget.RemoteViews);
    ctor public RemoteViews(android.os.Parcel);
    method public void addView(int, android.widget.RemoteViews);
    method public android.view.View apply(android.content.Context, android.view.ViewGroup);
+417 −99
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.PorterDuff;
import android.graphics.Rect;
@@ -87,6 +88,31 @@ public class RemoteViews implements Parcelable, Filter {
     */
    private MemoryUsageCounter mMemoryUsageCounter;

    /**
     * Maps bitmaps to unique indicies to avoid Bitmap duplication.
     */
    private BitmapCache mBitmapCache;

    /**
     * Indicates whether or not this RemoteViews object is contained as a child of any other
     * RemoteViews.
     */
    private boolean mIsRoot = true;

    /**
     * Constants to whether or not this RemoteViews is composed of a landscape and portrait
     * RemoteViews.
     */
    private static final int MODE_NORMAL = 0;
    private static final int MODE_HAS_LANDSCAPE_AND_PORTRAIT = 1;

    /**
     * Used in conjunction with the special constructor
     * {@link #RemoteViews(RemoteViews, RemoteViews)} to keep track of the landscape and portrait
     * RemoteViews.
     */
    private RemoteViews mLandscape = null;
    private RemoteViews mPortrait = null;

    /**
     * This flag indicates whether this RemoteViews object is being created from a
@@ -163,6 +189,10 @@ public class RemoteViews implements Parcelable, Filter {
            }
            return true;
        }

        public void setBitmapCache(BitmapCache bitmapCache) {
            // Do nothing
        }
    }

    private class SetEmptyView extends Action {
@@ -643,6 +673,112 @@ public class RemoteViews implements Parcelable, Filter {
        }
    }

    private static class BitmapCache {
        ArrayList<Bitmap> mBitmaps;

        public BitmapCache() {
            mBitmaps = new ArrayList<Bitmap>();
        }

        public BitmapCache(Parcel source) {
            int count = source.readInt();
            mBitmaps = new ArrayList<Bitmap>();
            for (int i = 0; i < count; i++) {
                Bitmap b = Bitmap.CREATOR.createFromParcel(source);
                mBitmaps.add(b);
            }
        }

        public int getBitmapId(Bitmap b) {
            if (b == null) {
                return -1;
            } else {
                if (mBitmaps.contains(b)) {
                    return mBitmaps.indexOf(b);
                } else {
                    mBitmaps.add(b);
                    return (mBitmaps.size() - 1);
                }
            }
        }

        public Bitmap getBitmapForId(int id) {
            if (id == -1 || id >= mBitmaps.size()) {
                return null;
            } else {
                return mBitmaps.get(id);
            }
        }

        public void writeBitmapsToParcel(Parcel dest, int flags) {
            int count = mBitmaps.size();
            dest.writeInt(count);
            for (int i = 0; i < count; i++) {
                mBitmaps.get(i).writeToParcel(dest, flags);
            }
        }

        public void assimilate(BitmapCache bitmapCache) {
            ArrayList<Bitmap> bitmapsToBeAdded = bitmapCache.mBitmaps;
            int count = bitmapsToBeAdded.size();
            for (int i = 0; i < count; i++) {
                Bitmap b = bitmapsToBeAdded.get(i);
                if (!mBitmaps.contains(b)) {
                    mBitmaps.add(b);
                }
            }
        }

        public void addBitmapMemory(MemoryUsageCounter memoryCounter) {
            for (int i = 0; i < mBitmaps.size(); i++) {
                memoryCounter.addBitmapMemory(mBitmaps.get(i));
            }
        }
    }

    private class BitmapReflectionAction extends Action {
        int bitmapId;
        int viewId;
        Bitmap bitmap;
        String methodName;

        BitmapReflectionAction(int viewId, String methodName, Bitmap bitmap) {
            this.bitmap = bitmap;
            this.viewId = viewId;
            this.methodName = methodName;
            bitmapId = mBitmapCache.getBitmapId(bitmap);
        }

        BitmapReflectionAction(Parcel in) {
            viewId = in.readInt();
            methodName = in.readString();
            bitmapId = in.readInt();
            bitmap = mBitmapCache.getBitmapForId(bitmapId);
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeInt(TAG);
            dest.writeInt(viewId);
            dest.writeString(methodName);
            dest.writeInt(bitmapId);
        }

        @Override
        public void apply(View root, ViewGroup rootParent) throws ActionException {
            ReflectionAction ra = new ReflectionAction(viewId, methodName, ReflectionAction.BITMAP,
                    bitmap);
            ra.apply(root, rootParent);
        }

        @Override
        public void setBitmapCache(BitmapCache bitmapCache) {
            bitmapId = bitmapCache.getBitmapId(bitmap);
        }

        public final static int TAG = 12;
    }

    /**
     * Base class for the reflection actions.
     */
@@ -894,24 +1030,7 @@ public class RemoteViews implements Parcelable, Filter {
                case BITMAP:
                    if (this.value != null) {
                        final Bitmap b = (Bitmap) this.value;
                        final Bitmap.Config c = b.getConfig();
                        // If we don't know, be pessimistic and assume 4
                        int bpp = 4;
                        if (c != null) {
                            switch (c) {
                            case ALPHA_8:
                                bpp = 1;
                                break;
                            case RGB_565:
                            case ARGB_4444:
                                bpp = 2;
                                break;
                            case ARGB_8888:
                                bpp = 4;
                                break;
                            }
                        }
                        counter.bitmapIncrement(b.getWidth() * b.getHeight() * bpp);
                        counter.addBitmapMemory(b);
                    }
                    break;
                default:
@@ -920,6 +1039,16 @@ public class RemoteViews implements Parcelable, Filter {
        }
    }

    private void configureRemoteViewsAsChild(RemoteViews rv) {
        mBitmapCache.assimilate(rv.mBitmapCache);
        rv.setBitmapCache(mBitmapCache);
        rv.setNotRoot();
    }

    void setNotRoot() {
        mIsRoot = false;
    }

    /**
     * Equivalent to calling {@link ViewGroup#addView(View)} after inflating the
     * given {@link RemoteViews}, or calling {@link ViewGroup#removeAllViews()}
@@ -929,17 +1058,29 @@ public class RemoteViews implements Parcelable, Filter {
        public ViewGroupAction(int viewId, RemoteViews nestedViews) {
            this.viewId = viewId;
            this.nestedViews = nestedViews;
            configureRemoteViewsAsChild(nestedViews);
        }

        public ViewGroupAction(Parcel parcel) {
        public ViewGroupAction(Parcel parcel, BitmapCache bitmapCache) {
            viewId = parcel.readInt();
            nestedViews = parcel.readParcelable(null);
            boolean nestedViewsNull = parcel.readInt() == 0;
            if (!nestedViewsNull) {
                nestedViews = new RemoteViews(parcel, bitmapCache);
            } else {
                nestedViews = null;
            }
        }

        public void writeToParcel(Parcel dest, int flags) {
            dest.writeInt(TAG);
            dest.writeInt(viewId);
            dest.writeParcelable(nestedViews, 0 /* no flags */);
            if (nestedViews != null) {
                dest.writeInt(1);
                nestedViews.writeToParcel(dest, flags);
            } else {
                // signifies null
                dest.writeInt(0);
            }
        }

        @Override
@@ -959,7 +1100,14 @@ public class RemoteViews implements Parcelable, Filter {
        @Override
        public void updateMemoryUsageEstimate(MemoryUsageCounter counter) {
            if (nestedViews != null) {
                counter.bitmapIncrement(nestedViews.estimateBitmapMemoryUsage());
                counter.increment(nestedViews.estimateMemoryUsage());
            }
        }

        @Override
        public void setBitmapCache(BitmapCache bitmapCache) {
            if (nestedViews != null) {
                nestedViews.setBitmapCache(bitmapCache);
            }
        }

@@ -1027,18 +1175,39 @@ public class RemoteViews implements Parcelable, Filter {
     */
    private class MemoryUsageCounter {
        public void clear() {
            mBitmapHeapMemoryUsage = 0;
            mMemoryUsage = 0;
        }

        public void bitmapIncrement(int numBytes) {
            mBitmapHeapMemoryUsage += numBytes;
        public void increment(int numBytes) {
            mMemoryUsage += numBytes;
        }

        public int getBitmapHeapMemoryUsage() {
            return mBitmapHeapMemoryUsage;
        public int getMemoryUsage() {
            return mMemoryUsage;
        }

        int mBitmapHeapMemoryUsage;
        public void addBitmapMemory(Bitmap b) {
            final Bitmap.Config c = b.getConfig();
            // If we don't know, be pessimistic and assume 4
            int bpp = 4;
            if (c != null) {
                switch (c) {
                case ALPHA_8:
                    bpp = 1;
                    break;
                case RGB_565:
                case ARGB_4444:
                    bpp = 2;
                    break;
                case ARGB_8888:
                    bpp = 4;
                    break;
                }
            }
            increment(b.getWidth() * b.getHeight() * bpp);
        }

        int mMemoryUsage;
    }

    /**
@@ -1051,9 +1220,44 @@ public class RemoteViews implements Parcelable, Filter {
    public RemoteViews(String packageName, int layoutId) {
        mPackage = packageName;
        mLayoutId = layoutId;
        mBitmapCache = new BitmapCache();

        // setup the memory usage statistics
        mMemoryUsageCounter = new MemoryUsageCounter();
        recalculateMemoryUsage();
    }

    private boolean hasLandscapeAndPortraitLayouts() {
        return (mLandscape != null) && (mPortrait != null);
    }

     /**
     * Create a new RemoteViews object that will inflate as the specified
     * landspace or portrait RemoteViews, depending on the current configuration.
     *
     * @param landscape The RemoteViews to inflate in landscape configuration
     * @param portrait The RemoteViews to inflate in portrait configuration
     */
    public RemoteViews(RemoteViews landscape, RemoteViews portrait) {
        if (landscape == null || portrait == null) {
            throw new RuntimeException("Both RemoteViews must be non-null");
        }
        if (landscape.getPackage().compareTo(portrait.getPackage()) != 0) {
            throw new RuntimeException("Both RemoteViews must share the same package");
        }
        mPackage = portrait.getPackage();
        mLayoutId = portrait.getLayoutId();

        mLandscape = landscape;
        mPortrait = portrait;

        // setup the memory usage statistics
        mMemoryUsageCounter = new MemoryUsageCounter();

        mBitmapCache = new BitmapCache();
        configureRemoteViewsAsChild(landscape);
        configureRemoteViewsAsChild(portrait);

        recalculateMemoryUsage();
    }

@@ -1063,6 +1267,21 @@ public class RemoteViews implements Parcelable, Filter {
     * @param parcel
     */
    public RemoteViews(Parcel parcel) {
        this(parcel, null);
    }

    private RemoteViews(Parcel parcel, BitmapCache bitmapCache) {
        int mode = parcel.readInt();

        // We only store a bitmap cache in the root of the RemoteViews.
        if (bitmapCache == null) {
            mBitmapCache = new BitmapCache(parcel);
        } else {
            setBitmapCache(bitmapCache);
            setNotRoot();
        }

        if (mode == MODE_NORMAL) {
            mPackage = parcel.readString();
            mLayoutId = parcel.readInt();
            mIsWidgetCollectionChild = parcel.readInt() == 1 ? true : false;
@@ -1083,7 +1302,7 @@ public class RemoteViews implements Parcelable, Filter {
                        mActions.add(new ReflectionAction(parcel));
                        break;
                    case ViewGroupAction.TAG:
                    mActions.add(new ViewGroupAction(parcel));
                        mActions.add(new ViewGroupAction(parcel, mBitmapCache));
                        break;
                    case ReflectionActionWithoutParams.TAG:
                        mActions.add(new ReflectionActionWithoutParams(parcel));
@@ -1103,11 +1322,21 @@ public class RemoteViews implements Parcelable, Filter {
                    case TextViewDrawableAction.TAG:
                        mActions.add(new TextViewDrawableAction(parcel));
                        break;
                    case BitmapReflectionAction.TAG:
                        mActions.add(new BitmapReflectionAction(parcel));
                        break;
                    default:
                        throw new ActionException("Tag " + tag + " not found");
                    }
                }
            }
        } else {
            // MODE_HAS_LANDSCAPE_AND_PORTRAIT
            mLandscape = new RemoteViews(parcel, mBitmapCache);
            mPortrait = new RemoteViews(parcel, mBitmapCache);
            mPackage = mPortrait.getPackage();
            mLayoutId = mPortrait.getLayoutId();
        }

        // setup the memory usage statistics
        mMemoryUsageCounter = new MemoryUsageCounter();
@@ -1116,11 +1345,18 @@ public class RemoteViews implements Parcelable, Filter {

    @Override
    public RemoteViews clone() {
        final RemoteViews that = new RemoteViews(mPackage, mLayoutId);
        RemoteViews that;
        if (!hasLandscapeAndPortraitLayouts()) {
            that = new RemoteViews(mPackage, mLayoutId);

            if (mActions != null) {
                that.mActions = (ArrayList<Action>)mActions.clone();
            }

        } else {
            RemoteViews land = mLandscape.clone();
            RemoteViews port = mPortrait.clone();
            that = new RemoteViews(land, port);
        }
        // update the memory usage stats of the cloned RemoteViews
        that.recalculateMemoryUsage();
        return that;
@@ -1130,6 +1366,13 @@ public class RemoteViews implements Parcelable, Filter {
        return mPackage;
    }

    /**
     * Reutrns the layout id of the root layout associated with this RemoteViews. In the case
     * that the RemoteViews has both a landscape and portrait root, this will return the layout
     * id associated with the portrait layout.
     *
     * @return the layout id.
     */
    public int getLayoutId() {
        return mLayoutId;
    }
@@ -1151,6 +1394,7 @@ public class RemoteViews implements Parcelable, Filter {
    private void recalculateMemoryUsage() {
        mMemoryUsageCounter.clear();

        if (!hasLandscapeAndPortraitLayouts()) {
            // Accumulate the memory usage for each action
            if (mActions != null) {
                final int count = mActions.size();
@@ -1158,13 +1402,39 @@ public class RemoteViews implements Parcelable, Filter {
                    mActions.get(i).updateMemoryUsageEstimate(mMemoryUsageCounter);
                }
            }
            if (mIsRoot) {
                mBitmapCache.addBitmapMemory(mMemoryUsageCounter);
            }
        } else {
            mMemoryUsageCounter.increment(mLandscape.estimateMemoryUsage());
            mMemoryUsageCounter.increment(mPortrait.estimateMemoryUsage());
            mBitmapCache.addBitmapMemory(mMemoryUsageCounter);
        }
    }

    /**
     * Recursively sets BitmapCache in the hierarchy and update the bitmap ids.
     */
    private void setBitmapCache(BitmapCache bitmapCache) {
        mBitmapCache = bitmapCache;
        if (!hasLandscapeAndPortraitLayouts()) {
            if (mActions != null) {
                final int count = mActions.size();
                for (int i= 0; i < count; ++i) {
                    mActions.get(i).setBitmapCache(bitmapCache);
                }
            }
        } else {
            mLandscape.setBitmapCache(bitmapCache);
            mPortrait.setBitmapCache(bitmapCache);
        }
    }

    /**
     * Returns an estimate of the bitmap heap memory usage for this RemoteViews.
     */
    int estimateBitmapMemoryUsage() {
        return mMemoryUsageCounter.getBitmapHeapMemoryUsage();
    int estimateMemoryUsage() {
        return mMemoryUsageCounter.getMemoryUsage();
    }

    /**
@@ -1173,6 +1443,11 @@ public class RemoteViews implements Parcelable, Filter {
     * @param a The action to add
     */
    private void addAction(Action a) {
        if (hasLandscapeAndPortraitLayouts()) {
            throw new RuntimeException("RemoteViews specifying separate landscape and portrait" +
                    " layouts cannot be modified. Instead, fully configure the landscape and" +
                    " portrait layouts individually before constructing the combined layout.");
        }
        if (mActions == null) {
            mActions = new ArrayList<Action>();
        }
@@ -1644,7 +1919,7 @@ public class RemoteViews implements Parcelable, Filter {
     * @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));
        addAction(new BitmapReflectionAction(viewId, methodName, value));
    }

    /**
@@ -1679,6 +1954,18 @@ public class RemoteViews implements Parcelable, Filter {
        setCharSequence(viewId, "setContentDescription", contentDescription);
    }

    private RemoteViews getRemoteViewsToApply(Context context) {
        if (hasLandscapeAndPortraitLayouts()) {
            int orientation = context.getResources().getConfiguration().orientation;
            if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
                return mLandscape;
            } else {
                return mPortrait;
            }
        }
        return this;
    }

    /**
     * Inflates the view hierarchy represented by this object and applies
     * all of the actions.
@@ -1691,6 +1978,8 @@ public class RemoteViews implements Parcelable, Filter {
     * @return The inflated view hierarchy
     */
    public View apply(Context context, ViewGroup parent) {
        RemoteViews rvToApply = getRemoteViewsToApply(context);

        View result;

        Context c = prepareContext(context);
@@ -1701,9 +1990,9 @@ public class RemoteViews implements Parcelable, Filter {
        inflater = inflater.cloneInContext(c);
        inflater.setFilter(this);

        result = inflater.inflate(mLayoutId, parent, false);
        result = inflater.inflate(rvToApply.getLayoutId(), parent, false);

        performApply(result, parent);
        rvToApply.performApply(result, parent);

        return result;
    }
@@ -1717,8 +2006,20 @@ public class RemoteViews implements Parcelable, Filter {
     * the {@link #apply(Context,ViewGroup)} call.
     */
    public void reapply(Context context, View v) {
        RemoteViews rvToApply = getRemoteViewsToApply(context);

        // In the case that a view has this RemoteViews applied in one orientation, is persisted
        // across orientation change, and has the RemoteViews re-applied in the new orientation,
        // we throw an exception, since the layouts may be completely unrelated.
        if (hasLandscapeAndPortraitLayouts()) {
            if (v.getId() != rvToApply.getLayoutId()) {
                throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" +
                        " that does not share the same root layout id.");
            }
        }

        prepareContext(context);
        performApply(v, (ViewGroup) v.getParent());
        rvToApply.performApply(v, (ViewGroup) v.getParent());
    }

    private void performApply(View v, ViewGroup parent) {
@@ -1763,6 +2064,13 @@ public class RemoteViews implements Parcelable, Filter {
    }

    public void writeToParcel(Parcel dest, int flags) {
        if (!hasLandscapeAndPortraitLayouts()) {
            dest.writeInt(MODE_NORMAL);
            // We only write the bitmap cache if we are the root RemoteViews, as this cache
            // is shared by all children.
            if (mIsRoot) {
                mBitmapCache.writeBitmapsToParcel(dest, flags);
            }
            dest.writeString(mPackage);
            dest.writeInt(mLayoutId);
            dest.writeInt(mIsWidgetCollectionChild ? 1 : 0);
@@ -1777,6 +2085,16 @@ public class RemoteViews implements Parcelable, Filter {
                Action a = mActions.get(i);
                a.writeToParcel(dest, 0);
            }
        } else {
            dest.writeInt(MODE_HAS_LANDSCAPE_AND_PORTRAIT);
            // We only write the bitmap cache if we are the root RemoteViews, as this cache
            // is shared by all children.
            if (mIsRoot) {
                mBitmapCache.writeBitmapsToParcel(dest, flags);
            }
            mLandscape.writeToParcel(dest, flags);
            mPortrait.writeToParcel(dest, flags);
        }
    }

    /**
+1 −5
Original line number Diff line number Diff line
@@ -595,7 +595,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
            for (Integer i : mIndexRemoteViews.keySet()) {
                final RemoteViews v = mIndexRemoteViews.get(i);
                if (v != null) {
                    mem += v.estimateBitmapMemoryUsage();
                    mem += v.estimateMemoryUsage();
                }
            }
            return mem;
@@ -942,10 +942,6 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
     * which  wouldn't otherwise be possible.
     */
    public void setVisibleRangeHint(int lowerBound, int upperBound) {
        if (lowerBound < 0 || upperBound < 0) {
            throw new RuntimeException("Attempted to set invalid range: lowerBound="+lowerBound +
                    "," + "upperBound="+upperBound);
        }
        mVisibleWindowLowerBound = lowerBound;
        mVisibleWindowUpperBound = upperBound;
    }