RecentsHorizontalScrollView.java revision 3b1fc47d004f6b29af8f40d181baa3460b1e3b15
19f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller/*
29f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller * Copyright (C) 2011 The Android Open Source Project
39f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller *
49f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller * Licensed under the Apache License, Version 2.0 (the "License");
59f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller * you may not use this file except in compliance with the License.
69f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller * You may obtain a copy of the License at
79f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller *
89f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller *      http://www.apache.org/licenses/LICENSE-2.0
99f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller *
109f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller * Unless required by applicable law or agreed to in writing, software
119f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller * distributed under the License is distributed on an "AS IS" BASIS,
129f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller * See the License for the specific language governing permissions and
149f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller * limitations under the License.
159f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller */
169f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
179f0f0e0e3100caec459a5b5ef836317844c83b3fJim Millerpackage com.android.systemui.recent;
189f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
193b1fc47d004f6b29af8f40d181baa3460b1e3b15Michael Jurkaimport com.android.systemui.recent.RecentsPanelView.ActivityDescriptionAdapter;
209f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
219f0f0e0e3100caec459a5b5ef836317844c83b3fJim Millerimport android.animation.Animator;
229f0f0e0e3100caec459a5b5ef836317844c83b3fJim Millerimport android.animation.Animator.AnimatorListener;
239f0f0e0e3100caec459a5b5ef836317844c83b3fJim Millerimport android.animation.LayoutTransition;
249f0f0e0e3100caec459a5b5ef836317844c83b3fJim Millerimport android.animation.ObjectAnimator;
259f0f0e0e3100caec459a5b5ef836317844c83b3fJim Millerimport android.animation.ValueAnimator;
269f0f0e0e3100caec459a5b5ef836317844c83b3fJim Millerimport android.animation.ValueAnimator.AnimatorUpdateListener;
279f0f0e0e3100caec459a5b5ef836317844c83b3fJim Millerimport android.content.Context;
289f0f0e0e3100caec459a5b5ef836317844c83b3fJim Millerimport android.database.DataSetObserver;
299f0f0e0e3100caec459a5b5ef836317844c83b3fJim Millerimport android.graphics.RectF;
309f0f0e0e3100caec459a5b5ef836317844c83b3fJim Millerimport android.util.AttributeSet;
319f0f0e0e3100caec459a5b5ef836317844c83b3fJim Millerimport android.util.Log;
329f0f0e0e3100caec459a5b5ef836317844c83b3fJim Millerimport android.view.LayoutInflater;
339f0f0e0e3100caec459a5b5ef836317844c83b3fJim Millerimport android.view.MotionEvent;
349f0f0e0e3100caec459a5b5ef836317844c83b3fJim Millerimport android.view.VelocityTracker;
359f0f0e0e3100caec459a5b5ef836317844c83b3fJim Millerimport android.view.View;
369f0f0e0e3100caec459a5b5ef836317844c83b3fJim Millerimport android.view.animation.DecelerateInterpolator;
379f0f0e0e3100caec459a5b5ef836317844c83b3fJim Millerimport android.view.animation.LinearInterpolator;
389f0f0e0e3100caec459a5b5ef836317844c83b3fJim Millerimport android.widget.HorizontalScrollView;
399f0f0e0e3100caec459a5b5ef836317844c83b3fJim Millerimport android.widget.LinearLayout;
409f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
419f0f0e0e3100caec459a5b5ef836317844c83b3fJim Millerimport com.android.systemui.R;
429f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
439f0f0e0e3100caec459a5b5ef836317844c83b3fJim Millerpublic class RecentsHorizontalScrollView extends HorizontalScrollView
449f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        implements View.OnClickListener, View.OnTouchListener {
459f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    private static final float FADE_CONSTANT = 0.5f;
469f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    private static final int SNAP_BACK_DURATION = 250;
479f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    private static final int ESCAPE_VELOCITY = 100; // speed of item required to "curate" it
489f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    private static final String TAG = RecentsPanelView.TAG;
499f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    private static final float THRESHHOLD = 50;
509f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    private static final boolean DEBUG_INVALIDATE = false;
519f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    private LinearLayout mLinearLayout;
523b1fc47d004f6b29af8f40d181baa3460b1e3b15Michael Jurka    private ActivityDescriptionAdapter mAdapter;
539f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    private RecentsCallback mCallback;
549f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    protected int mLastScrollPosition;
559f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    private View mCurrentView;
569f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    private float mLastY;
579f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    private boolean mDragging;
589f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    private VelocityTracker mVelocityTracker;
599f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
609f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    public RecentsHorizontalScrollView(Context context) {
619f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        this(context, null);
629f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    }
639f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
649f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    public RecentsHorizontalScrollView(Context context, AttributeSet attrs) {
659f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        super(context, attrs, 0);
669f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    }
679f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
689f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    private int scrollPositionOfMostRecent() {
699f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        return mLinearLayout.getWidth() - getWidth();
709f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    }
719f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
729f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    public void update() {
739f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        mLinearLayout.removeAllViews();
749f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        for (int i = 0; i < mAdapter.getCount(); i++) {
759f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            View view = mAdapter.getView(i, null, mLinearLayout);
769f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            view.setClickable(true);
779f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            view.setOnClickListener(this);
789f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            view.setOnTouchListener(this);
799f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            mLinearLayout.addView(view);
809f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        }
819f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        // Scroll to end after layout.
829f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        post(new Runnable() {
839f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            public void run() {
849f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                mLastScrollPosition = scrollPositionOfMostRecent();
859f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                scrollTo(mLastScrollPosition, 0);
869f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            }
879f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        });
889f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    }
899f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
909f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    @Override
919f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    public boolean onInterceptTouchEvent(MotionEvent ev) {
929f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        if (mVelocityTracker == null) {
939f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            mVelocityTracker = VelocityTracker.obtain();
949f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        }
959f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        mVelocityTracker.addMovement(ev);
969f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        switch (ev.getAction()) {
979f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            case MotionEvent.ACTION_DOWN:
989f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                mDragging = false;
999f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                mLastY = ev.getY();
1009f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                break;
1019f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
1029f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            case MotionEvent.ACTION_MOVE:
1039f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                float delta = ev.getY() - mLastY;
1049f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                if (Math.abs(delta) > THRESHHOLD) {
1059f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                    mDragging = true;
1069f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                }
1079f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                break;
1089f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
1099f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            case MotionEvent.ACTION_UP:
1109f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                mDragging = false;
1119f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                break;
1129f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        }
1139f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        return mDragging ? true : super.onInterceptTouchEvent(ev);
1149f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    }
1159f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
1169f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    private float getAlphaForOffset(View view, float thumbHeight) {
1179f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        final float fadeHeight = FADE_CONSTANT * thumbHeight;
1189f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        float result = 1.0f;
1199f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        if (view.getY() >= thumbHeight) {
1209f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            result = 1.0f - (view.getY() - thumbHeight) / fadeHeight;
1219f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        } else if (view.getY() < 0.0f) {
1229f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            result = 1.0f + (thumbHeight + view.getY()) / fadeHeight;
1239f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        }
1249f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        return result;
1259f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    }
1269f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
1279f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    @Override
1289f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    public boolean onTouchEvent(MotionEvent ev) {
1299f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        if (!mDragging) {
1309f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            return super.onTouchEvent(ev);
1319f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        }
1329f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
1339f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        mVelocityTracker.addMovement(ev);
1349f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
1359f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        final View animView = mCurrentView;
1369f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        // TODO: Cache thumbnail
1379f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        final View thumb = animView.findViewById(R.id.app_thumbnail);
1389f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        switch (ev.getAction()) {
1399f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            case MotionEvent.ACTION_MOVE:
1409f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                if (animView != null) {
1419f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                    final float delta = ev.getY() - mLastY;
1429f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                    animView.setY(animView.getY() + delta);
1439f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                    animView.setAlpha(getAlphaForOffset(animView, thumb.getHeight()));
1449f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                    invalidateGlobalRegion(animView);
1459f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                }
1469f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                mLastY = ev.getY();
1479f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                break;
1489f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
1499f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            case MotionEvent.ACTION_UP:
1509f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                final ObjectAnimator anim;
1519f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                if (animView != null) {
1529f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                    final VelocityTracker velocityTracker = mVelocityTracker;
1539f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                    velocityTracker.computeCurrentVelocity(1000, 10000);
1549f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                    final float velocityX = velocityTracker.getXVelocity();
1559f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                    final float velocityY = velocityTracker.getYVelocity();
1569f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                    final float curY = animView.getY();
1579f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                    final float newY = (velocityY >= 0.0f ? 1 : -1) * animView.getHeight();
1589f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
1599f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                    if (Math.abs(velocityY) > Math.abs(velocityX)
1609f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                            && Math.abs(velocityY) > ESCAPE_VELOCITY
1619f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                            && (velocityY >= 0.0f) == (animView.getY() >= 0)) {
1629f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                        final long duration =
1639f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                            (long) (Math.abs(newY - curY) * 1000.0f / Math.abs(velocityY));
1649f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                        anim = ObjectAnimator.ofFloat(animView, "y", curY, newY);
1659f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                        anim.setInterpolator(new LinearInterpolator());
1669f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                        final int swipeDirection = animView.getY() >= 0.0f ?
1679f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                                RecentsCallback.SWIPE_RIGHT : RecentsCallback.SWIPE_LEFT;
1689f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                        anim.addListener(new AnimatorListener() {
1699f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                            public void onAnimationStart(Animator animation) {
1709f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                            }
1719f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                            public void onAnimationRepeat(Animator animation) {
1729f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                            }
1739f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                            public void onAnimationEnd(Animator animation) {
1749f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                                mLinearLayout.removeView(mCurrentView);
1759f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                                mCallback.handleSwipe(animView, swipeDirection);
1769f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                            }
1779f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                            public void onAnimationCancel(Animator animation) {
1789f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                                mLinearLayout.removeView(mCurrentView);
1799f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                                mCallback.handleSwipe(animView, swipeDirection);
1809f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                            }
1819f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                        });
1829f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                        anim.setDuration(duration);
1839f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                    } else { // Animate back to position
1849f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                        final long duration = Math.abs(velocityY) > 0.0f ?
1859f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                                (long) (Math.abs(newY - curY) * 1000.0f / Math.abs(velocityY))
1869f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                                : SNAP_BACK_DURATION;
1879f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                        anim = ObjectAnimator.ofFloat(animView, "y", animView.getY(), 0.0f);
1889f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                        anim.setInterpolator(new DecelerateInterpolator(2.0f));
1899f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                        anim.setDuration(duration);
1909f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                    }
1919f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
1929f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                    anim.addUpdateListener(new AnimatorUpdateListener() {
1939f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                        public void onAnimationUpdate(ValueAnimator animation) {
1949f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                            animView.setAlpha(getAlphaForOffset(animView, thumb.getHeight()));
1959f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                            invalidateGlobalRegion(animView);
1969f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                        }
1979f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                    });
1989f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                    anim.start();
1999f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                }
2009f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
2019f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                mVelocityTracker.recycle();
2029f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                mVelocityTracker = null;
2039f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                break;
2049f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        }
2059f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        return true;
2069f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    }
2079f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
2089f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    void invalidateGlobalRegion(View view) {
2099f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        RectF childBounds
2109f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                = new RectF(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
2119f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        childBounds.offset(view.getX(), view.getY());
2129f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        if (DEBUG_INVALIDATE) Log.v(TAG, "-------------");
2139f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        while (view.getParent() != null && view.getParent() instanceof View) {
2149f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            view = (View) view.getParent();
2159f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            view.getMatrix().mapRect(childBounds);
2169f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            view.invalidate((int) Math.floor(childBounds.left),
2179f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                    (int) Math.floor(childBounds.top),
2189f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                    (int) Math.ceil(childBounds.right),
2199f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                    (int) Math.ceil(childBounds.bottom));
2209f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            if (DEBUG_INVALIDATE) {
2219f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                Log.v(TAG, "INVALIDATE(" + (int) Math.floor(childBounds.left)
2229f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                        + "," + (int) Math.floor(childBounds.top)
2239f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                        + "," + (int) Math.ceil(childBounds.right)
2249f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                        + "," + (int) Math.ceil(childBounds.bottom));
2259f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            }
2269f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        }
2279f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    }
2289f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
2299f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    @Override
2309f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    protected void onFinishInflate() {
2319f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        super.onFinishInflate();
2329f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        LayoutInflater inflater = (LayoutInflater)
2339f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
2349f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
2359f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        setScrollbarFadingEnabled(true);
2369f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
2379f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        mLinearLayout = (LinearLayout) findViewById(R.id.recents_linear_layout);
2389f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
2399f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        final int leftPadding = mContext.getResources()
2409f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            .getDimensionPixelOffset(R.dimen.status_bar_recents_thumbnail_left_margin);
2419f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        setOverScrollEffectPadding(leftPadding, 0);
2429f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    }
2439f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
2449f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    private void setOverScrollEffectPadding(int leftPadding, int i) {
2459f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        // TODO Add to RecentsHorizontalScrollView
2469f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    }
2479f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
2489f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    @Override
2499f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
2509f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        super.onSizeChanged(w, h, oldw, oldh);
2519f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        // Keep track of the last visible item in the list so we can restore it
2529f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        // to the bottom when the orientation changes.
2539f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        mLastScrollPosition = scrollPositionOfMostRecent();
2549f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
2559f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        // This has to happen post-layout, so run it "in the future"
2569f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        post(new Runnable() {
2579f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            public void run() {
2589f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                scrollTo(0, mLastScrollPosition);
2599f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            }
2609f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        });
2619f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    }
2629f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
2639f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    @Override
2649f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    protected void onVisibilityChanged(View changedView, int visibility) {
2659f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        super.onVisibilityChanged(changedView, visibility);
2669f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        // scroll to bottom after reloading
2679f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        if (visibility == View.VISIBLE && changedView == this) {
2689f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            post(new Runnable() {
2699f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                public void run() {
2709f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                    update();
2719f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                }
2729f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            });
2739f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        }
2749f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    }
2759f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
2763b1fc47d004f6b29af8f40d181baa3460b1e3b15Michael Jurka    public void setAdapter(ActivityDescriptionAdapter adapter) {
2779f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        mAdapter = adapter;
2789f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        mAdapter.registerDataSetObserver(new DataSetObserver() {
2799f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            public void onChanged() {
2809f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                update();
2819f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            }
2829f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
2839f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            public void onInvalidated() {
2849f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                update();
2859f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            }
2869f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        });
2879f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    }
2889f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
2899f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    @Override
2909f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    public void setLayoutTransition(LayoutTransition transition) {
2919f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        // The layout transition applies to our embedded LinearLayout
2929f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        mLinearLayout.setLayoutTransition(transition);
2939f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    }
2949f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
2959f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    public void onClick(View view) {
2969f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        mCallback.handleOnClick(view);
2979f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    }
2989f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
2999f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    public void setCallback(RecentsCallback callback) {
3009f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        mCallback = callback;
3019f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    }
3029f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
3039f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    public boolean onTouch(View v, MotionEvent event) {
3049f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        mCurrentView = v;
3059f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        return false;
3069f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    }
3079f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller}
308