RecentsHorizontalScrollView.java revision 19c8c708f16546fc75ae12659aa190f5e3dfbb52
19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2011 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1768f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrookpackage com.android.systemui.recent;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrookimport android.animation.LayoutTransition;
209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.Context;
21206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.content.res.Configuration;
22206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.database.DataSetObserver;
23206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.graphics.Canvas;
24206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.util.AttributeSet;
25206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.util.DisplayMetrics;
26e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganovimport android.util.FloatMath;
27b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganovimport android.util.Log;
289cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganovimport android.view.MotionEvent;
29aa780c110922148a6a4ba06734bb2b0bb8c98f93Svetoslav Ganovimport android.view.View;
309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.ViewConfiguration;
319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.ViewTreeObserver;
329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.ViewTreeObserver.OnGlobalLayoutListener;
33206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.widget.HorizontalScrollView;
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.widget.LinearLayout;
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
36206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport com.android.systemui.R;
379cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganovimport com.android.systemui.SwipeHelper;
38206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport com.android.systemui.recent.RecentsPanelView.TaskDescriptionAdapter;
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
40b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganovimport java.util.HashSet;
41206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport java.util.Iterator;
42206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic class RecentsHorizontalScrollView extends HorizontalScrollView
44206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        implements SwipeHelper.Callback, RecentsPanelView.RecentsScrollView {
453fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov    private static final String TAG = RecentsPanelView.TAG;
463fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov    private static final boolean DEBUG = RecentsPanelView.DEBUG;
478a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    private LinearLayout mLinearLayout;
48d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private TaskDescriptionAdapter mAdapter;
499cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    private RecentsCallback mCallback;
50a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov    protected int mLastScrollPosition;
51206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private SwipeHelper mSwipeHelper;
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private FadedEdgeDrawHelper mFadedEdgeDrawHelper;
53b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private HashSet<View> mRecycledViews;
54d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio    private int mNumItemsInOneScreenful;
55b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private Runnable mOnScrollListener;
56d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
57d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    public RecentsHorizontalScrollView(Context context, AttributeSet attrs) {
58d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        super(context, attrs, 0);
59d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio        mSwipeHelper = new SwipeHelper(SwipeHelper.Y, this, context);
60d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        mFadedEdgeDrawHelper = FadedEdgeDrawHelper.create(context, attrs, this, false);
6168f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook        mRecycledViews = new HashSet<View>();
624bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    }
63d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
64d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    public void setMinSwipeAlpha(float minAlpha) {
65d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        mSwipeHelper.setMinSwipeProgress(minAlpha);
66d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    }
67d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
68d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private int scrollPositionOfMostRecent() {
69d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        return mLinearLayout.getWidth() - getWidth();
70d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    }
71d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
72d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private void addToRecycledViews(View v) {
73d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (mRecycledViews.size() < mNumItemsInOneScreenful) {
74d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mRecycledViews.add(v);
75d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
76d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    }
77d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
78d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    public View findViewForTask(int persistentTaskId) {
79d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        for (int i = 0; i < mLinearLayout.getChildCount(); i++) {
80d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            View v = mLinearLayout.getChildAt(i);
81d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            RecentsPanelView.ViewHolder holder = (RecentsPanelView.ViewHolder) v.getTag();
82d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            if (holder.taskDescription.persistentTaskId == persistentTaskId) {
83d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                return v;
84d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
854bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov        }
864bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov        return null;
874bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    }
8868f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
8968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    private void update() {
9068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook        for (int i = 0; i < mLinearLayout.getChildCount(); i++) {
91fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor            View v = mLinearLayout.getChildAt(i);
9268f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook            addToRecycledViews(v);
933f9c9eab7b41c409c3c7b503b89ad340e408b6a0Svetoslav Ganov            mAdapter.recycleView(v);
943f9c9eab7b41c409c3c7b503b89ad340e408b6a0Svetoslav Ganov        }
95d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        LayoutTransition transitioner = getLayoutTransition();
963f9c9eab7b41c409c3c7b503b89ad340e408b6a0Svetoslav Ganov        setLayoutTransition(null);
973f9c9eab7b41c409c3c7b503b89ad340e408b6a0Svetoslav Ganov
98e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mLinearLayout.removeAllViews();
99e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        Iterator<View> recycledViews = mRecycledViews.iterator();
100e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        for (int i = 0; i < mAdapter.getCount(); i++) {
101e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            View old = null;
102e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            if (recycledViews.hasNext()) {
103206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                old = recycledViews.next();
10468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook                recycledViews.remove();
105d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                old.setVisibility(VISIBLE);
1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
10768f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
108206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            final View view = mAdapter.getView(i, old, mLinearLayout);
10968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
110206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            if (mFadedEdgeDrawHelper != null) {
1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mFadedEdgeDrawHelper.addViewCallback(view);
112206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
113206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
114206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            OnTouchListener noOpListener = new OnTouchListener() {
115206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                @Override
116fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor                public boolean onTouch(View v, MotionEvent event) {
117206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    return true;
1184213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                }
119b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            };
120fe41ce4ef97e7da585fdbf06988da8047e72b9f6Svetoslav Ganov
121b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            view.setOnClickListener(new OnClickListener() {
122b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                public void onClick(View v) {
123b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                    mCallback.dismiss();
124206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
125b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            });
126206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            // We don't want a click sound when we dimiss recents
127206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            view.setSoundEffectsEnabled(false);
128b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
129206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            OnClickListener launchAppListener = new OnClickListener() {
130b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                public void onClick(View v) {
131206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    mCallback.handleOnClick(view);
132206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
133d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            };
134b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
135d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            RecentsPanelView.ViewHolder holder = (RecentsPanelView.ViewHolder) view.getTag();
136b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            final View thumbnailView = holder.thumbnailView;
137b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            OnLongClickListener longClickListener = new OnLongClickListener() {
138d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                public boolean onLongClick(View v) {
139b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                    final View anchorView = view.findViewById(R.id.app_description);
140d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    mCallback.handleLongPress(view, anchorView, thumbnailView);
1419cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov                    return true;
1429cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov                }
1439f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov            };
1449f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov            thumbnailView.setClickable(true);
1459f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov            thumbnailView.setOnClickListener(launchAppListener);
1469f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov            thumbnailView.setOnLongClickListener(longClickListener);
1479f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov
148206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            // We don't want to dismiss recents if a user clicks on the app title
149206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            // (we also don't want to launch the app either, though, because the
150206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            // app title is a small target and doesn't have great click feedback)
151206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            final View appTitle = view.findViewById(R.id.app_label);
152206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            appTitle.setContentDescription(" ");
153d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio            appTitle.setOnTouchListener(noOpListener);
154206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mLinearLayout.addView(view);
155206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
156d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio        setLayoutTransition(transitioner);
157d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio
158206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // Scroll to end after initial layout.
159206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
160206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        final OnGlobalLayoutListener updateScroll = new OnGlobalLayoutListener() {
161d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio                public void onGlobalLayout() {
162d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio                    mLastScrollPosition = scrollPositionOfMostRecent();
163d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio                    scrollTo(mLastScrollPosition, 0);
164d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio                    final ViewTreeObserver observer = getViewTreeObserver();
165d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio                    if (observer.isAlive()) {
166d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio                        observer.removeOnGlobalLayoutListener(this);
167d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio                    }
168d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio                }
169d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio            };
170d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio        getViewTreeObserver().addOnGlobalLayoutListener(updateScroll);
171e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    }
172d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio
173d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio    @Override
174d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio    public void removeViewInLayout(final View view) {
175d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio        dismissChild(view);
176206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
177206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
178206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public boolean onInterceptTouchEvent(MotionEvent ev) {
179206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (DEBUG) Log.v(TAG, "onInterceptTouchEvent()");
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mSwipeHelper.onInterceptTouchEvent(ev) ||
181d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio            super.onInterceptTouchEvent(ev);
182d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio    }
183d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio
184d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio    @Override
185d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio    public boolean onTouchEvent(MotionEvent ev) {
186d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio        return mSwipeHelper.onTouchEvent(ev) ||
187d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio            super.onTouchEvent(ev);
188d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio    }
189d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio
190d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio    public boolean canChildBeDismissed(View v) {
191d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio        return true;
192d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio    }
193a65fe884d1bba4c64b961df27bd04e33e28a7209Fabrice Di Meglio
194a65fe884d1bba4c64b961df27bd04e33e28a7209Fabrice Di Meglio    @Override
195a65fe884d1bba4c64b961df27bd04e33e28a7209Fabrice Di Meglio    public boolean isAntiFalsingNeeded() {
196a65fe884d1bba4c64b961df27bd04e33e28a7209Fabrice Di Meglio        return false;
197d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio    }
198d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio
199d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio    public void dismissChild(View v) {
200206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mSwipeHelper.dismissChild(v, 0);
201206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
202206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
203206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public void onChildDismissed(View v) {
204206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        addToRecycledViews(v);
205206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mLinearLayout.removeView(v);
206206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mCallback.handleSwipe(v);
207206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // Restore the alpha/translation parameters to what they were before swiping
208206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // (for when these items are recycled)
209206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        View contentView = getChildContentView(v);
210206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        contentView.setAlpha(1f);
211206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        contentView.setTranslationY(0);
212206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
213206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
214206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public void onBeginDrag(View v) {
215206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // We do this so the underlying ScrollView knows that it won't get
216d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        // the chance to intercept events anymore
217d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        requestDisallowInterceptTouchEvent(true);
218d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    }
219d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
220d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    public void onDragCancelled(View v) {
221ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    }
222ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov
223ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    @Override
224ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    public void onChildSnappedBack(View animView) {
225ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    }
226e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov
227e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov    @Override
228e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov    public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) {
229e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov        return false;
230e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov    }
231e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov
232e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov    public View getChildAtPosition(MotionEvent ev) {
233ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final float x = ev.getX() + getScrollX();
234ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final float y = ev.getY() + getScrollY();
235ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        for (int i = 0; i < mLinearLayout.getChildCount(); i++) {
236ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            View item = mLinearLayout.getChildAt(i);
237ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            if (x >= item.getLeft() && x < item.getRight()
238ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                && y >= item.getTop() && y < item.getBottom()) {
239ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                return item;
240ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            }
241ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        }
242ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        return null;
243ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    }
244e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov
245e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov    public View getChildContentView(View v) {
246206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return v.findViewById(R.id.recent_item);
247206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
248206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
250206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public void drawFadedEdges(Canvas canvas, int left, int right, int top, int bottom) {
251b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        if (mFadedEdgeDrawHelper != null) {
252b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
253b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            mFadedEdgeDrawHelper.drawCallback(canvas,
254b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                    left, right, top, bottom, getScrollX(), getScrollY(),
255b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                    0, 0,
256206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    getLeftFadingEdgeStrength(), getRightFadingEdgeStrength(), getPaddingTop());
257206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
25968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
26068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    @Override
26168f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
26268f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook       super.onScrollChanged(l, t, oldl, oldt);
263e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov       if (mOnScrollListener != null) {
26468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook           mOnScrollListener.run();
26568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook       }
26668f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    }
26768f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
268e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public void setOnScrollListener(Runnable listener) {
26968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook        mOnScrollListener = listener;
27068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    }
27168f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
27268f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    @Override
273e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public int getVerticalFadingEdgeLength() {
27468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook        if (mFadedEdgeDrawHelper != null) {
27568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook            return mFadedEdgeDrawHelper.getVerticalFadingEdgeLength();
276206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
27768f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook            return super.getVerticalFadingEdgeLength();
278cedc446684e94c9971c38c3206f1f224314bda2aSvetoslav Ganov        }
27950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    }
28050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
28150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    @Override
28250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    public int getHorizontalFadingEdgeLength() {
28350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        if (mFadedEdgeDrawHelper != null) {
284206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return mFadedEdgeDrawHelper.getHorizontalFadingEdgeLength();
285206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
286206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return super.getHorizontalFadingEdgeLength();
287206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
2889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
289fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
290206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
291206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    protected void onFinishInflate() {
292206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        super.onFinishInflate();
293e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        setScrollbarFadingEnabled(true);
294fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor        mLinearLayout = (LinearLayout) findViewById(R.id.recents_linear_layout);
29568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook        final int leftPadding = getContext().getResources()
296206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            .getDimensionPixelOffset(R.dimen.status_bar_recents_thumbnail_left_margin);
297206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        setOverScrollEffectPadding(leftPadding, 0);
298206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
299206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
300206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
301206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public void onAttachedToWindow() {
302206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mFadedEdgeDrawHelper != null) {
303d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mFadedEdgeDrawHelper.onAttachedToWindowCallback(mLinearLayout, isHardwareAccelerated());
304206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
305206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
306206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
307206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
308b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    protected void onConfigurationChanged(Configuration newConfig) {
309206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        super.onConfigurationChanged(newConfig);
310206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        float densityScale = getResources().getDisplayMetrics().density;
311232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        mSwipeHelper.setDensityScale(densityScale);
312232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
313232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        mSwipeHelper.setPagingTouchSlop(pagingTouchSlop);
314232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov    }
315232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov
316206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void setOverScrollEffectPadding(int leftPadding, int i) {
317206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // TODO Add to (Vertical)ScrollView
318206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
319206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
320206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
321206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
322206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        super.onSizeChanged(w, h, oldw, oldh);
323206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
324206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // Skip this work if a transition is running; it sets the scroll values independently
325206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // and should not have those animated values clobbered by this logic
326206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        LayoutTransition transition = mLinearLayout.getLayoutTransition();
327206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (transition != null && transition.isRunning()) {
328206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return;
329206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
330206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // Keep track of the last visible item in the list so we can restore it
331206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // to the bottom when the orientation changes.
332206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mLastScrollPosition = scrollPositionOfMostRecent();
333206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
334206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // This has to happen post-layout, so run it "in the future"
335206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        post(new Runnable() {
336206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            public void run() {
337206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                // Make sure we're still not clobbering the transition-set values, since this
338206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                // runnable launches asynchronously
339206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                LayoutTransition transition = mLinearLayout.getLayoutTransition();
340206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (transition == null || !transition.isRunning()) {
341206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    scrollTo(mLastScrollPosition, 0);
342206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
343206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
344206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        });
345206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
346206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
347206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public void setAdapter(TaskDescriptionAdapter adapter) {
348206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mAdapter = adapter;
349206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mAdapter.registerDataSetObserver(new DataSetObserver() {
350206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            public void onChanged() {
351b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                update();
352b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            }
353206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
354b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            public void onInvalidated() {
355206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                update();
356206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
357d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        });
358b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        DisplayMetrics dm = getResources().getDisplayMetrics();
359d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        int childWidthMeasureSpec =
360b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                MeasureSpec.makeMeasureSpec(dm.widthPixels, MeasureSpec.AT_MOST);
361b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        int childheightMeasureSpec =
362206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                MeasureSpec.makeMeasureSpec(dm.heightPixels, MeasureSpec.AT_MOST);
363206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        View child = mAdapter.createView(mLinearLayout);
364206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        child.measure(childWidthMeasureSpec, childheightMeasureSpec);
365206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mNumItemsInOneScreenful =
366206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                (int) FloatMath.ceil(dm.widthPixels / (float) child.getMeasuredWidth());
367d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        addToRecycledViews(child);
368206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
369d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        for (int i = 0; i < mNumItemsInOneScreenful - 1; i++) {
370206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            addToRecycledViews(mAdapter.createView(mLinearLayout));
371206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
372d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    }
373206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
374d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    public int numItemsInOneScreenful() {
375206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return mNumItemsInOneScreenful;
376206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
377206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
378206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
379206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public void setLayoutTransition(LayoutTransition transition) {
380206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // The layout transition applies to our embedded LinearLayout
381206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mLinearLayout.setLayoutTransition(transition);
382206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
383206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
384206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public void setCallback(RecentsCallback callback) {
385206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mCallback = callback;
386206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
387206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov}
388206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov