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

Commit a32edd4b authored by Adam Cohen's avatar Adam Cohen
Browse files

Adding click feedback to widget collections

Change-Id: I97fceb6c68ca6eb1b703eafacf201e1aed7c38e7
parent 2444ddb3
Loading
Loading
Loading
Loading
+1 −16
Original line number Diff line number Diff line
@@ -237817,21 +237817,6 @@
<parameter name="value" type="long">
</parameter>
</method>
<method name="setOnClickExtras"
 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="extras" type="android.os.Bundle">
</parameter>
</method>
<method name="setOnClickFillInIntent"
 return="void"
 abstract="false"
@@ -245613,7 +245598,7 @@
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="arg0" type="T">
<parameter name="t" type="T">
</parameter>
</method>
</interface>
+2 −1
Original line number Diff line number Diff line
@@ -1298,8 +1298,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
     * Returns true if a child view contains the specified point when transformed
     * into its coordinate space.
     * Child must not be null.
     * @hide
     */
    private boolean isTransformedTouchPointInView(float x, float y, View child,
    protected boolean isTransformedTouchPointInView(float x, float y, View child,
            PointF outLocalPoint) {
        float localX = x + mScrollX - child.mLeft;
        float localY = y + mScrollY - child.mTop;
+100 −4
Original line number Diff line number Diff line
@@ -24,16 +24,16 @@ import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;

/**
 * Base class for a {@link AdapterView} that will perform animations
@@ -141,6 +141,20 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
    ObjectAnimator mInAnimation;
    ObjectAnimator mOutAnimation;

    /**
     * Current touch state.
     */
    private int mTouchMode = TOUCH_MODE_NONE;

    /**
     * Private touch states.
     */
    static final int TOUCH_MODE_NONE = 0;
    static final int TOUCH_MODE_DOWN_IN_CURRENT_VIEW = 1;
    static final int TOUCH_MODE_HANDLED = 2;

    private Runnable mPendingCheckForTap;

    private static final int DEFAULT_ANIMATION_DURATION = 200;

    public AdapterViewAnimator(Context context) {
@@ -528,7 +542,6 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
            // above the layout will end up being ignored since we are currently laying out, so
            // we post a delayed requestLayout and invalidate
            mMainQueue.post(new Runnable() {
                @Override
                public void run() {
                    requestLayout();
                    invalidate();
@@ -551,6 +564,89 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
        }
    }

    void showTapFeedback(View v) {
        v.setPressed(true);
    }

    void hideTapFeedback(View v) {
        v.setPressed(false);
    }

    void cancelHandleClick() {
        View v = getCurrentView();
        if (v != null) {
            hideTapFeedback(v);
        }
        mTouchMode = TOUCH_MODE_NONE;
    }

    final class CheckForTap implements Runnable {
        public void run() {
            if (mTouchMode == TOUCH_MODE_DOWN_IN_CURRENT_VIEW) {
                View v = getCurrentView();
                showTapFeedback(v);
            }
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        boolean handled = false;
        switch (action) {
            case MotionEvent.ACTION_DOWN: {
                View v = getCurrentView();
                if (v != null) {
                    if (isTransformedTouchPointInView(ev.getX(), ev.getY(), v, null)) {
                        if (mPendingCheckForTap == null) {
                            mPendingCheckForTap = new CheckForTap();
                        }
                        mTouchMode = TOUCH_MODE_DOWN_IN_CURRENT_VIEW;
                        postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
                    }
                }
                break;
            }
            case MotionEvent.ACTION_MOVE: break;
            case MotionEvent.ACTION_POINTER_UP: break;
            case MotionEvent.ACTION_UP: {
                if (mTouchMode == TOUCH_MODE_DOWN_IN_CURRENT_VIEW) {
                    final View v = getCurrentView();
                    if (v != null) {
                        if (isTransformedTouchPointInView(ev.getX(), ev.getY(), v, null)) {
                            final Handler handler = getHandler();
                            if (handler != null) {
                                handler.removeCallbacks(mPendingCheckForTap);
                            }
                            showTapFeedback(v);
                            postDelayed(new Runnable() {
                                public void run() {
                                    hideTapFeedback(v);
                                    post(new Runnable() {
                                        public void run() {
                                            performItemClick(v, 0, 0);
                                        }
                                    });
                                }
                            }, ViewConfiguration.getPressedStateDuration());
                            handled = true;
                        }
                    }
                }
                mTouchMode = TOUCH_MODE_NONE;
                break;
            }
            case MotionEvent.ACTION_CANCEL: {
                View v = getCurrentView();
                if (v != null) {
                    hideTapFeedback(v);
                }
                mTouchMode = TOUCH_MODE_NONE;
            }
        }
        return handled;
    }

    private void measureChildren() {
        final int count = getChildCount();
        final int childWidth = mMeasuredWidth - mPaddingLeft - mPaddingRight;
+52 −101
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.LayoutInflater.Filter;
import android.view.View.OnClickListener;
import android.widget.AdapterView.OnItemClickListener;


/**
@@ -216,8 +217,9 @@ public class RemoteViews implements Parcelable, Filter {
                        "only from RemoteViewsFactory (ie. on collection items).");
                return;
            }

            if (target != null && fillInIntent != null) {
            if (target == root) {
                target.setTagInternal(com.android.internal.R.id.fillInIntent, fillInIntent);
            } else if (target != null && fillInIntent != null) {
                OnClickListener listener = new OnClickListener() {
                    public void onClick(View v) {
                        // Insure that this view is a child of an AdapterView
@@ -269,21 +271,21 @@ public class RemoteViews implements Parcelable, Filter {
        public final static int TAG = 9;
    }

    private class SetOnClickExtras extends Action {
        public SetOnClickExtras(int id, Bundle extras) {
    private class SetPendingIntentTemplate extends Action {
        public SetPendingIntentTemplate(int id, PendingIntent pendingIntentTemplate) {
            this.viewId = id;
            this.extras = extras;
            this.pendingIntentTemplate = pendingIntentTemplate;
        }

        public SetOnClickExtras(Parcel parcel) {
        public SetPendingIntentTemplate(Parcel parcel) {
            viewId = parcel.readInt();
            extras = Bundle.CREATOR.createFromParcel(parcel);
            pendingIntentTemplate = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
        }

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

        @Override
@@ -291,92 +293,54 @@ public class RemoteViews implements Parcelable, Filter {
            final View target = root.findViewById(viewId);
            if (target == null) return;

            if (!mIsWidgetCollectionChild) {
                Log.e("RemoteViews", "The method setOnClickExtras is available " +
                        "only from RemoteViewsFactory (ie. on collection items).");
                return;
            }

            if (target != null && extras != null) {
                OnClickListener listener = new OnClickListener() {
                    public void onClick(View v) {
                        // Insure that this view is a child of an AdapterView
                        View parent = (View) v.getParent();
                        while (!(parent instanceof AdapterView<?>)
                                && !(parent instanceof AppWidgetHostView)) {
                            parent = (View) parent.getParent();
                        }

                        if (parent instanceof AppWidgetHostView) {
                            // Somehow they've managed to get this far without having
                            // and AdapterView as a parent.
                            Log.e("RemoteViews", "Collection item doesn't have AdapterView parent");
                            return;
            // If the view isn't an AdapterView, setting a PendingIntent template doesn't make sense
            if (target instanceof AdapterView<?>) {
                AdapterView<?> av = (AdapterView<?>) target;
                // The PendingIntent template is stored in the view's tag.
                OnItemClickListener listener = new OnItemClickListener() {
                    public void onItemClick(AdapterView<?> parent, View view,
                            int position, long id) {
                        // The view should be a frame layout
                        if (view instanceof ViewGroup) {
                            ViewGroup vg = (ViewGroup) view;

                            // AdapterViews contain their children in a frame
                            // so we need to go one layer deeper here.
                            if (parent instanceof AdapterViewAnimator) {
                                vg = (ViewGroup) vg.getChildAt(0);
                            }
                            if (vg == null) return;

                            Intent fillInIntent = null;
                            int childCount = vg.getChildCount();
                            for (int i = 0; i < childCount; i++) {
                                Object tag = vg.getChildAt(i).getTag(com.android.internal.R.id.fillInIntent);
                                if (tag instanceof Intent) {
                                    fillInIntent = (Intent) tag;
                                    break;
                                }

                        // Insure that a template pending intent has been set on an ancestor
                        if (!(parent.getTag() instanceof PendingIntent)) {
                            Log.e("RemoteViews", "Attempting setOnClickExtras without calling " +
                                "setPendingIntentTemplate on parent.");
                            return;
                            }
                            if (fillInIntent == null) return;

                        PendingIntent pendingIntent = (PendingIntent) parent.getTag();

                        final float appScale = v.getContext().getResources()
                            final float appScale = view.getContext().getResources()
                                    .getCompatibilityInfo().applicationScale;
                            final int[] pos = new int[2];
                        v.getLocationOnScreen(pos);
                            view.getLocationOnScreen(pos);

                            final Rect rect = new Rect();
                            rect.left = (int) (pos[0] * appScale + 0.5f);
                            rect.top = (int) (pos[1] * appScale + 0.5f);
                        rect.right = (int) ((pos[0] + v.getWidth()) * appScale + 0.5f);
                        rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f);
                            rect.right = (int) ((pos[0] + view.getWidth()) * appScale + 0.5f);
                            rect.bottom = (int) ((pos[1] + view.getHeight()) * appScale + 0.5f);

                            final Intent intent = new Intent();
                            intent.setSourceBounds(rect);
                        intent.putExtras(extras);
                        startIntentSafely(v.getContext(), pendingIntent, intent);
                    }

                };
                target.setOnClickListener(listener);
                            startIntentSafely(view.getContext(), pendingIntentTemplate, fillInIntent);
                        }
                    }

        int viewId;
        Bundle extras;

        public final static int TAG = 7;
    }

    private class SetPendingIntentTemplate extends Action {
        public SetPendingIntentTemplate(int id, PendingIntent pendingIntentTemplate) {
            this.viewId = id;
            this.pendingIntentTemplate = pendingIntentTemplate;
        }

        public SetPendingIntentTemplate(Parcel parcel) {
            viewId = parcel.readInt();
            pendingIntentTemplate = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
        }

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

        @Override
        public void apply(View root) {
            final View target = root.findViewById(viewId);
            if (target == null) return;

            // If the view isn't an AdapterView, setting a PendingIntent template doesn't make sense
            if (target instanceof AdapterView<?>) {
                // The PendingIntent template is stored in the view's tag.
                target.setTag(pendingIntentTemplate);
                };
                av.setOnItemClickListener(listener);
                av.setTag(pendingIntentTemplate);
            } else {
                Log.e("RemoteViews", "Cannot setPendingIntentTemplate on a view which is not" +
                        "an AdapterView (id: " + viewId + ")");
@@ -973,9 +937,6 @@ public class RemoteViews implements Parcelable, Filter {
                case SetEmptyView.TAG:
                    mActions.add(new SetEmptyView(parcel));
                    break;
                case SetOnClickExtras.TAG:
                    mActions.add(new SetOnClickExtras(parcel));
                    break;
                case SetPendingIntentTemplate.TAG:
                    mActions.add(new SetPendingIntentTemplate(parcel));
                    break;
@@ -1238,16 +1199,6 @@ public class RemoteViews implements Parcelable, Filter {
        addAction(new SetPendingIntentTemplate(viewId, pendingIntentTemplate));
    }

    /**
     * Being deprecated. See {@link RemoteViews#setOnClickFillInIntent(int, Intent)}.
     *
     * @param viewId
     * @param extras
     */
    public void setOnClickExtras(int viewId, Bundle extras) {
        addAction(new SetOnClickExtras(viewId, extras));
    }

    /**
     * When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is very
     * costly to set PendingIntents on the individual items, and is hence not permitted. Instead
+7 −4
Original line number Diff line number Diff line
@@ -319,7 +319,7 @@ public class RemoteViewsAdapter extends BaseAdapter {
                if (mUserLoadingView != null) {
                    // A user-specified loading view
                    View loadingView = mUserLoadingView.apply(parent.getContext(), parent);
                    loadingView.setTag(new Integer(0));
                    loadingView.setTagInternal(com.android.internal.R.id.rowTypeId, new Integer(0));
                    layout.addView(loadingView);
                } else {
                    // A default loading view
@@ -741,8 +741,11 @@ public class RemoteViewsAdapter extends BaseAdapter {
     */
    private int getConvertViewTypeId(View convertView) {
        int typeId = -1;
        if (convertView != null && convertView.getTag() != null) {
            typeId = (Integer) convertView.getTag();
        if (convertView != null) {
            Object tag = convertView.getTag(com.android.internal.R.id.rowTypeId);
            if (tag != null) {
                typeId = (Integer) tag;
            }
        }
        return typeId;
    }
@@ -781,7 +784,7 @@ public class RemoteViewsAdapter extends BaseAdapter {

                    // Otherwise, create a new view to be returned
                    View newView = rv.apply(context, parent);
                    newView.setTag(new Integer(typeId));
                    newView.setTagInternal(com.android.internal.R.id.rowTypeId, new Integer(typeId));
                    if (convertView != null) {
                        layout.removeAllViews();
                    } else {
Loading