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