RecentsVerticalScrollView.java revision fc8fa638617efb5695a1f89ea75375faebbe2a40
1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.systemui.recent;
18
19import android.animation.LayoutTransition;
20import android.content.Context;
21import android.content.res.Configuration;
22import android.database.DataSetObserver;
23import android.util.AttributeSet;
24import android.util.Log;
25import android.view.MotionEvent;
26import android.view.View;
27import android.view.ViewConfiguration;
28import android.widget.LinearLayout;
29import android.widget.ScrollView;
30
31import com.android.systemui.R;
32import com.android.systemui.SwipeHelper;
33import com.android.systemui.recent.RecentsPanelView.ActivityDescriptionAdapter;
34
35public class RecentsVerticalScrollView extends ScrollView implements SwipeHelper.Callback {
36    private static final String TAG = RecentsPanelView.TAG;
37    private static final boolean DEBUG = RecentsPanelView.DEBUG;
38    private LinearLayout mLinearLayout;
39    private ActivityDescriptionAdapter mAdapter;
40    private RecentsCallback mCallback;
41    protected int mLastScrollPosition;
42    private SwipeHelper mSwipeHelper;
43
44    private OnLongClickListener mOnLongClick = new OnLongClickListener() {
45        public boolean onLongClick(View v) {
46            final View anchorView = v.findViewById(R.id.app_description);
47            mCallback.handleLongPress(v, anchorView);
48            return true;
49        }
50    };
51
52    public RecentsVerticalScrollView(Context context) {
53        this(context, null);
54    }
55
56    public RecentsVerticalScrollView(Context context, AttributeSet attrs) {
57        super(context, attrs, 0);
58        float densityScale = getResources().getDisplayMetrics().density;
59        float pagingTouchSlop = ViewConfiguration.get(mContext).getScaledPagingTouchSlop();
60        mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, pagingTouchSlop);
61    }
62
63    private int scrollPositionOfMostRecent() {
64        return mLinearLayout.getHeight() - getHeight();
65    }
66
67    private void update() {
68        mLinearLayout.removeAllViews();
69        // Once we can clear the data associated with individual item views,
70        // we can get rid of the removeAllViews() and the code below will
71        // recycle them.
72        for (int i = 0; i < mAdapter.getCount(); i++) {
73            View old = null;
74            if (i < mLinearLayout.getChildCount()) {
75                old = mLinearLayout.getChildAt(i);
76                old.setVisibility(View.VISIBLE);
77            }
78            final View view = mAdapter.getView(i, old, mLinearLayout);
79
80            if (old == null) {
81                view.setClickable(true);
82                view.setOnLongClickListener(mOnLongClick);
83
84                final View thumbnail = getChildContentView(view);
85                // thumbnail is set to clickable in the layout file
86                thumbnail.setOnClickListener(new OnClickListener() {
87                    public void onClick(View v) {
88                        mCallback.handleOnClick(view);
89                    }
90                });
91
92                mLinearLayout.addView(view);
93            }
94        }
95        for (int i = mAdapter.getCount(); i < mLinearLayout.getChildCount(); i++) {
96            mLinearLayout.getChildAt(i).setVisibility(View.GONE);
97        }
98        // Scroll to end after layout.
99        post(new Runnable() {
100            public void run() {
101                mLastScrollPosition = scrollPositionOfMostRecent();
102                scrollTo(0, mLastScrollPosition);
103            }
104        });
105    }
106
107    @Override
108    public void removeViewInLayout(final View view) {
109        dismissChild(view);
110    }
111
112    public boolean onInterceptTouchEvent(MotionEvent ev) {
113        if (DEBUG) Log.v(TAG, "onInterceptTouchEvent()");
114        return mSwipeHelper.onInterceptTouchEvent(ev) ||
115            super.onInterceptTouchEvent(ev);
116    }
117
118    @Override
119    public boolean onTouchEvent(MotionEvent ev) {
120        return mSwipeHelper.onTouchEvent(ev) ||
121            super.onTouchEvent(ev);
122    }
123
124    public boolean canChildBeDismissed(View v) {
125        return true;
126    }
127
128    public void dismissChild(View v) {
129        mSwipeHelper.dismissChild(v, 0);
130    }
131
132    public void onChildDismissed(View v) {
133        mLinearLayout.removeView(v);
134        mCallback.handleSwipe(v);
135    }
136
137    public void onBeginDrag(View v) {
138    }
139
140    public View getChildAtPosition(MotionEvent ev) {
141        final float x = ev.getX() + getScrollX();
142        final float y = ev.getY() + getScrollY();
143        for (int i = 0; i < mLinearLayout.getChildCount(); i++) {
144            View item = mLinearLayout.getChildAt(i);
145            if (item.getVisibility() == View.VISIBLE
146                    && x >= item.getLeft() && x < item.getRight()
147                    && y >= item.getTop() && y < item.getBottom()) {
148                return item;
149            }
150        }
151        return null;
152    }
153
154    public View getChildContentView(View v) {
155        return v.findViewById(R.id.app_thumbnail);
156    }
157
158    @Override
159    protected void onFinishInflate() {
160        super.onFinishInflate();
161        setScrollbarFadingEnabled(true);
162        mLinearLayout = (LinearLayout) findViewById(R.id.recents_linear_layout);
163        final int leftPadding = mContext.getResources()
164            .getDimensionPixelOffset(R.dimen.status_bar_recents_thumbnail_left_margin);
165        setOverScrollEffectPadding(leftPadding, 0);
166    }
167
168    @Override
169    protected void onConfigurationChanged(Configuration newConfig) {
170        super.onConfigurationChanged(newConfig);
171        float densityScale = getResources().getDisplayMetrics().density;
172        mSwipeHelper.setDensityScale(densityScale);
173        float pagingTouchSlop = ViewConfiguration.get(mContext).getScaledPagingTouchSlop();
174        mSwipeHelper.setPagingTouchSlop(pagingTouchSlop);
175    }
176
177    private void setOverScrollEffectPadding(int leftPadding, int i) {
178        // TODO Add to (Vertical)ScrollView
179    }
180
181    @Override
182    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
183        super.onSizeChanged(w, h, oldw, oldh);
184
185        // Skip this work if a transition is running; it sets the scroll values independently
186        // and should not have those animated values clobbered by this logic
187        LayoutTransition transition = mLinearLayout.getLayoutTransition();
188        if (transition != null && transition.isRunning()) {
189            return;
190        }
191        // Keep track of the last visible item in the list so we can restore it
192        // to the bottom when the orientation changes.
193        mLastScrollPosition = scrollPositionOfMostRecent();
194
195        // This has to happen post-layout, so run it "in the future"
196        post(new Runnable() {
197            public void run() {
198                // Make sure we're still not clobbering the transition-set values, since this
199                // runnable launches asynchronously
200                LayoutTransition transition = mLinearLayout.getLayoutTransition();
201                if (transition == null || !transition.isRunning()) {
202                    scrollTo(0, mLastScrollPosition);
203                }
204            }
205        });
206    }
207
208    @Override
209    protected void onVisibilityChanged(View changedView, int visibility) {
210        super.onVisibilityChanged(changedView, visibility);
211        // scroll to bottom after reloading
212        if (visibility == View.VISIBLE && changedView == this) {
213            post(new Runnable() {
214                public void run() {
215                    update();
216                }
217            });
218        }
219    }
220
221    public void setAdapter(ActivityDescriptionAdapter adapter) {
222        mAdapter = adapter;
223        mAdapter.registerDataSetObserver(new DataSetObserver() {
224            public void onChanged() {
225                update();
226            }
227
228            public void onInvalidated() {
229                update();
230            }
231        });
232    }
233
234    @Override
235    public void setLayoutTransition(LayoutTransition transition) {
236        // The layout transition applies to our embedded LinearLayout
237        mLinearLayout.setLayoutTransition(transition);
238    }
239
240    public void setCallback(RecentsCallback callback) {
241        mCallback = callback;
242    }
243}
244