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

Commit 1162fd77 authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

Let RemoteViews nest children, allow ViewFlipper.

This change allows applications to nest children RemoteViews
inside an existing set of RemoteViews.  These nested views
are inflated and treated as addView() calls.

This change also allows ViewFlipper through RemoteViews, and
adds logic surpress flipping when the parent window is
detached or behind the lockscreen.  Also fixes ViewAnimator
to observe the measureAllChildren flag when set.

Fixes http://b/2239905
parent ec8178eb
Loading
Loading
Loading
Loading
+63 −0
Original line number Diff line number Diff line
@@ -1967,6 +1967,17 @@
 visibility="public"
>
</field>
<field name="autoStart"
 type="int"
 transient="false"
 volatile="false"
 value="16843446"
 static="true"
 final="true"
 deprecated="not deprecated"
 visibility="public"
>
</field>
<field name="autoText"
 type="int"
 transient="false"
@@ -189616,6 +189627,21 @@
<parameter name="parcel" type="android.os.Parcel">
</parameter>
</constructor>
<method name="addView"
 return="void"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="viewId" type="int">
</parameter>
<parameter name="nestedView" type="android.widget.RemoteViews">
</parameter>
</method>
<method name="apply"
 return="android.view.View"
 abstract="false"
@@ -189692,6 +189718,19 @@
<parameter name="v" type="android.view.View">
</parameter>
</method>
<method name="removeAllViews"
 return="void"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="viewId" type="int">
</parameter>
</method>
<method name="setBitmap"
 return="void"
 abstract="false"
@@ -196277,6 +196316,17 @@
<parameter name="attrs" type="android.util.AttributeSet">
</parameter>
</constructor>
<method name="isAutoStart"
 return="boolean"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</method>
<method name="isFlipping"
 return="boolean"
 abstract="false"
@@ -196288,6 +196338,19 @@
 visibility="public"
>
</method>
<method name="setAutoStart"
 return="void"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="autoStart" type="boolean">
</parameter>
</method>
<method name="setFlipInterval"
 return="void"
 abstract="false"
+68 −1
Original line number Diff line number Diff line
@@ -457,6 +457,46 @@ public class RemoteViews implements Parcelable, Filter {
        }
    }

    /**
     * Equivalent to calling {@link ViewGroup#addView(View)} after inflating the
     * given {@link RemoteViews}, or calling {@link ViewGroup#removeAllViews()}
     * when null. This allows users to build "nested" {@link RemoteViews}.
     */
    private class ViewGroupAction extends Action {
        public ViewGroupAction(int viewId, RemoteViews nestedViews) {
            this.viewId = viewId;
            this.nestedViews = nestedViews;
        }

        public ViewGroupAction(Parcel parcel) {
            viewId = parcel.readInt();
            nestedViews = parcel.readParcelable(null);
        }

        public void writeToParcel(Parcel dest, int flags) {
            dest.writeInt(TAG);
            dest.writeInt(viewId);
            dest.writeParcelable(nestedViews, 0 /* no flags */);
        }

        @Override
        public void apply(View root) {
            final Context context = root.getContext();
            final ViewGroup target = (ViewGroup) root.findViewById(viewId);
            if (nestedViews != null) {
                // Inflate nested views and add as children
                target.addView(nestedViews.apply(context, target));
            } else if (target != null) {
                // Clear all children when nested views omitted
                target.removeAllViews();
            }
        }

        int viewId;
        RemoteViews nestedViews;

        public final static int TAG = 4;
    }

    /**
     * Create a new RemoteViews object that will display the views contained
@@ -493,6 +533,9 @@ public class RemoteViews implements Parcelable, Filter {
                case ReflectionAction.TAG:
                    mActions.add(new ReflectionAction(parcel));
                    break;
                case ViewGroupAction.TAG:
                    mActions.add(new ViewGroupAction(parcel));
                    break;
                default:
                    throw new ActionException("Tag " + tag + " not found");
                }
@@ -520,6 +563,30 @@ public class RemoteViews implements Parcelable, Filter {
        mActions.add(a);
    }

    /**
     * Equivalent to calling {@link ViewGroup#addView(View)} after inflating the
     * given {@link RemoteViews}. This allows users to build "nested"
     * {@link RemoteViews}. In cases where consumers of {@link RemoteViews} may
     * recycle layouts, use {@link #removeAllViews(int)} to clear any existing
     * children.
     *
     * @param viewId The id of the parent {@link ViewGroup} to add child into.
     * @param nestedView {@link RemoteViews} that describes the child.
     */
    public void addView(int viewId, RemoteViews nestedView) {
        addAction(new ViewGroupAction(viewId, nestedView));
    }

    /**
     * Equivalent to calling {@link ViewGroup#removeAllViews()}.
     *
     * @param viewId The id of the parent {@link ViewGroup} to remove all
     *            children from.
     */
    public void removeAllViews(int viewId) {
        addAction(new ViewGroupAction(viewId, null));
    }

    /**
     * Equivalent to calling View.setVisibility
     * 
+21 −4
Original line number Diff line number Diff line
@@ -43,7 +43,7 @@ public class ViewAnimator extends FrameLayout {

    public ViewAnimator(Context context) {
        super(context);
        initViewAnimator();
        initViewAnimator(context, null);
    }

    public ViewAnimator(Context context, AttributeSet attrs) {
@@ -61,11 +61,28 @@ public class ViewAnimator extends FrameLayout {
        }
        a.recycle();

        initViewAnimator();
        initViewAnimator(context, attrs);
    }

    private void initViewAnimator() {
    /**
     * Initialize this {@link ViewAnimator}, possibly setting
     * {@link #setMeasureAllChildren(boolean)} based on {@link FrameLayout} flags.
     */
    private void initViewAnimator(Context context, AttributeSet attrs) {
        if (attrs == null) {
            // For compatibility, always measure children when undefined.
            mMeasureAllChildren = true;
            return;
        }

        // For compatibility, default to measure children, but allow XML
        // attribute to override.
        final TypedArray a = context.obtainStyledAttributes(attrs,
                com.android.internal.R.styleable.FrameLayout);
        final boolean measureAllChildren = a.getBoolean(
                com.android.internal.R.styleable.FrameLayout_measureAllChildren, true);
        setMeasureAllChildren(measureAllChildren);
        a.recycle();
    }
    
    /**
+113 −15
Original line number Diff line number Diff line
@@ -16,12 +16,15 @@

package android.widget;


import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.TypedArray;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.RemoteViews.RemoteView;

/**
@@ -30,10 +33,22 @@ import android.widget.RemoteViews.RemoteView;
 * requested, can automatically flip between each child at a regular interval.
 *
 * @attr ref android.R.styleable#ViewFlipper_flipInterval
 * @attr ref android.R.styleable#ViewFlipper_autoStart
 */
@RemoteView
public class ViewFlipper extends ViewAnimator {
    private int mFlipInterval = 3000;
    private boolean mKeepFlipping = false;
    private static final String TAG = "ViewFlipper";
    private static final boolean LOGD = true;

    private static final int DEFAULT_INTERVAL = 3000;

    private int mFlipInterval = DEFAULT_INTERVAL;
    private boolean mAutoStart = false;

    private boolean mRunning = false;
    private boolean mStarted = false;
    private boolean mVisible = false;
    private boolean mUserPresent = true;

    public ViewFlipper(Context context) {
        super(context);
@@ -44,11 +59,59 @@ public class ViewFlipper extends ViewAnimator {

        TypedArray a = context.obtainStyledAttributes(attrs,
                com.android.internal.R.styleable.ViewFlipper);
        mFlipInterval = a.getInt(com.android.internal.R.styleable.ViewFlipper_flipInterval,
                3000);
        mFlipInterval = a.getInt(
                com.android.internal.R.styleable.ViewFlipper_flipInterval, DEFAULT_INTERVAL);
        mAutoStart = a.getBoolean(
                com.android.internal.R.styleable.ViewFlipper_autoStart, false);
        a.recycle();
    }

    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            final String action = intent.getAction();
            if (Intent.ACTION_SCREEN_OFF.equals(action)) {
                mUserPresent = false;
                updateRunning();
            } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
                mUserPresent = true;
                updateRunning();
            }
        }
    };

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();

        // Listen for broadcasts related to user-presence
        final IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_SCREEN_OFF);
        filter.addAction(Intent.ACTION_USER_PRESENT);
        getContext().registerReceiver(mReceiver, filter);

        if (mAutoStart) {
            // Automatically start when requested
            startFlipping();
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        mVisible = false;

        getContext().unregisterReceiver(mReceiver);
        updateRunning();
    }

    @Override
    protected void onWindowVisibilityChanged(int visibility) {
        super.onWindowVisibilityChanged(visibility);
        mVisible = visibility == VISIBLE;
        updateRunning();
    }

    /**
     * How long to wait before flipping to the next view
     *
@@ -64,26 +127,61 @@ public class ViewFlipper extends ViewAnimator {
     * Start a timer to cycle through child views
     */
    public void startFlipping() {
        if (!mKeepFlipping) {
            mKeepFlipping = true;
            showOnly(mWhichChild);
            Message msg = mHandler.obtainMessage(FLIP_MSG);
            mHandler.sendMessageDelayed(msg, mFlipInterval);
        }
        mStarted = true;
        updateRunning();
    }

    /**
     * No more flips
     */
    public void stopFlipping() {
        mKeepFlipping = false;
        mStarted = false;
        updateRunning();
    }

    /**
     * Internal method to start or stop dispatching flip {@link Message} based
     * on {@link #mRunning} and {@link #mVisible} state.
     */
    private void updateRunning() {
        boolean running = mVisible && mStarted && mUserPresent;
        if (running != mRunning) {
            if (running) {
                showOnly(mWhichChild);
                Message msg = mHandler.obtainMessage(FLIP_MSG);
                mHandler.sendMessageDelayed(msg, mFlipInterval);
            } else {
                mHandler.removeMessages(FLIP_MSG);
            }
            mRunning = running;
        }
        if (LOGD) {
            Log.d(TAG, "updateRunning() mVisible=" + mVisible + ", mStarted=" + mStarted
                    + ", mUserPresent=" + mUserPresent + ", mRunning=" + mRunning);
        }
    }

    /**
     * Returns true if the child views are flipping.
     */
    public boolean isFlipping() {
        return mKeepFlipping;
        return mStarted;
    }

    /**
     * Set if this view automatically calls {@link #startFlipping()} when it
     * becomes attached to a window.
     */
    public void setAutoStart(boolean autoStart) {
        mAutoStart = autoStart;
    }

    /**
     * Returns true if this view automatically calls {@link #startFlipping()}
     * when it becomes attached to a window.
     */
    public boolean isAutoStart() {
        return mAutoStart;
    }

    private final int FLIP_MSG = 1;
@@ -92,7 +190,7 @@ public class ViewFlipper extends ViewAnimator {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == FLIP_MSG) {
                if (mKeepFlipping) {
                if (mRunning) {
                    showNext();
                    msg = obtainMessage(FLIP_MSG);
                    sendMessageDelayed(msg, mFlipInterval);
+2 −0
Original line number Diff line number Diff line
@@ -2138,6 +2138,8 @@
    </declare-styleable>
    <declare-styleable name="ViewFlipper">
        <attr name="flipInterval" format="integer" min="0" />
        <!-- When true, automatically start animating -->
        <attr name="autoStart" format="boolean" />
    </declare-styleable>
    <declare-styleable name="ViewSwitcher">
    </declare-styleable>
Loading