RecentsHorizontalScrollView.java revision 07d4046ea914c999b7aaa587b7bfe81d548121f7
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
199f0f0e0e3100caec459a5b5ef836317844c83b3fJim Millerimport android.animation.LayoutTransition;
209f0f0e0e3100caec459a5b5ef836317844c83b3fJim Millerimport android.content.Context;
213c728fe61dcf49354ea761a3e4ef6032ec271630Jim Millerimport android.content.res.Configuration;
229f0f0e0e3100caec459a5b5ef836317844c83b3fJim Millerimport android.database.DataSetObserver;
239f0f0e0e3100caec459a5b5ef836317844c83b3fJim Millerimport android.util.AttributeSet;
249f0f0e0e3100caec459a5b5ef836317844c83b3fJim Millerimport android.util.Log;
259f0f0e0e3100caec459a5b5ef836317844c83b3fJim Millerimport android.view.MotionEvent;
269f0f0e0e3100caec459a5b5ef836317844c83b3fJim Millerimport android.view.View;
273c728fe61dcf49354ea761a3e4ef6032ec271630Jim Millerimport android.view.ViewConfiguration;
289f0f0e0e3100caec459a5b5ef836317844c83b3fJim Millerimport android.widget.HorizontalScrollView;
299f0f0e0e3100caec459a5b5ef836317844c83b3fJim Millerimport android.widget.LinearLayout;
309f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
319f0f0e0e3100caec459a5b5ef836317844c83b3fJim Millerimport com.android.systemui.R;
3207d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurkaimport com.android.systemui.SwipeHelper;
3307d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurkaimport com.android.systemui.recent.RecentsPanelView.ActivityDescriptionAdapter;
349f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
3507d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurkapublic class RecentsHorizontalScrollView extends HorizontalScrollView
3607d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    implements SwipeHelper.Callback {
373c728fe61dcf49354ea761a3e4ef6032ec271630Jim Miller    private static final String TAG = RecentsPanelView.TAG;
383c728fe61dcf49354ea761a3e4ef6032ec271630Jim Miller    private static final boolean DEBUG = RecentsPanelView.DEBUG;
399f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    private LinearLayout mLinearLayout;
403b1fc47d004f6b29af8f40d181baa3460b1e3b15Michael Jurka    private ActivityDescriptionAdapter mAdapter;
419f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    private RecentsCallback mCallback;
429f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    protected int mLastScrollPosition;
4307d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    private SwipeHelper mSwipeHelper;
44c0d2731047e2da4eff84a40615e29e08dcb708c5Jim Miller    private OnLongClickListener mOnLongClick = new OnLongClickListener() {
45c0d2731047e2da4eff84a40615e29e08dcb708c5Jim Miller        public boolean onLongClick(View v) {
46c0d2731047e2da4eff84a40615e29e08dcb708c5Jim Miller            final View anchorView = v.findViewById(R.id.app_description);
47c0d2731047e2da4eff84a40615e29e08dcb708c5Jim Miller            mCallback.handleLongPress(v, anchorView);
48c0d2731047e2da4eff84a40615e29e08dcb708c5Jim Miller            return true;
49c0d2731047e2da4eff84a40615e29e08dcb708c5Jim Miller        }
50c0d2731047e2da4eff84a40615e29e08dcb708c5Jim Miller    };
519f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
529f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    public RecentsHorizontalScrollView(Context context) {
539f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        this(context, null);
549f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    }
559f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
569f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    public RecentsHorizontalScrollView(Context context, AttributeSet attrs) {
579f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        super(context, attrs, 0);
5807d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        float densityScale = getResources().getDisplayMetrics().density;
5907d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        float pagingTouchSlop = ViewConfiguration.get(mContext).getScaledPagingTouchSlop();
6007d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        mSwipeHelper = new SwipeHelper(SwipeHelper.Y, this, densityScale, pagingTouchSlop);
619f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    }
629f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
639f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    private int scrollPositionOfMostRecent() {
649f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        return mLinearLayout.getWidth() - getWidth();
659f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    }
669f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
67c0d2731047e2da4eff84a40615e29e08dcb708c5Jim Miller    private void update() {
689f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        mLinearLayout.removeAllViews();
699f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        for (int i = 0; i < mAdapter.getCount(); i++) {
70c0d2731047e2da4eff84a40615e29e08dcb708c5Jim Miller            final View view = mAdapter.getView(i, null, mLinearLayout);
7107d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            view.setLongClickable(true);
72c0d2731047e2da4eff84a40615e29e08dcb708c5Jim Miller            view.setOnLongClickListener(mOnLongClick);
7307d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka
7407d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            final View thumbnail = getChildContentView(view);
7507d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            // thumbnail is set to clickable in the layout file
7607d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            thumbnail.setOnClickListener(new OnClickListener() {
7707d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                public void onClick(View v) {
7807d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                    mCallback.handleOnClick(view);
7907d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                }
8007d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            });
819f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            mLinearLayout.addView(view);
829f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        }
839f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        // Scroll to end after layout.
849f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        post(new Runnable() {
859f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            public void run() {
869f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                mLastScrollPosition = scrollPositionOfMostRecent();
879f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                scrollTo(mLastScrollPosition, 0);
889f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            }
899f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        });
909f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    }
919f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
929f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    @Override
93c0d2731047e2da4eff84a40615e29e08dcb708c5Jim Miller    public void removeViewInLayout(final View view) {
9407d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        dismissChild(view);
95c0d2731047e2da4eff84a40615e29e08dcb708c5Jim Miller    }
96c0d2731047e2da4eff84a40615e29e08dcb708c5Jim Miller
979f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    public boolean onInterceptTouchEvent(MotionEvent ev) {
98c0d2731047e2da4eff84a40615e29e08dcb708c5Jim Miller        if (DEBUG) Log.v(TAG, "onInterceptTouchEvent()");
9907d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        return mSwipeHelper.onInterceptTouchEvent(ev) ||
10007d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            super.onInterceptTouchEvent(ev);
1019f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    }
1029f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
1039f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    @Override
1049f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    public boolean onTouchEvent(MotionEvent ev) {
10507d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        return mSwipeHelper.onTouchEvent(ev) ||
10607d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            super.onTouchEvent(ev);
10707d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    }
1089f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
10907d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    public boolean canChildBeDismissed(View v) {
11007d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        return true;
11107d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    }
1129f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
11307d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    public void dismissChild(View v) {
11407d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        mSwipeHelper.dismissChild(v, 0);
11507d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    }
1169f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
11707d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    public void onChildDismissed(View v) {
11807d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        mLinearLayout.removeView(v);
11907d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        mCallback.handleSwipe(v);
1209f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    }
1219f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
12207d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    public void onBeginDrag(View v) {
123c0d2731047e2da4eff84a40615e29e08dcb708c5Jim Miller    }
124c0d2731047e2da4eff84a40615e29e08dcb708c5Jim Miller
12507d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    public View getChildAtPosition(MotionEvent ev) {
12607d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        final float x = ev.getX() + getScrollX();
12707d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        final float y = ev.getY() + getScrollY();
12807d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        for (int i = 0; i < mLinearLayout.getChildCount(); i++) {
12907d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            View item = mLinearLayout.getChildAt(i);
13007d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            if (x >= item.getLeft() && x < item.getRight()
13107d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                && y >= item.getTop() && y < item.getBottom()) {
13207d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                return item;
1339f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            }
1349f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        }
13507d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        return null;
13607d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    }
13707d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka
13807d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    public View getChildContentView(View v) {
13907d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        return v.findViewById(R.id.app_thumbnail);
1409f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    }
1419f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
1429f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    @Override
1439f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    protected void onFinishInflate() {
1449f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        super.onFinishInflate();
1459f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        setScrollbarFadingEnabled(true);
1469f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        mLinearLayout = (LinearLayout) findViewById(R.id.recents_linear_layout);
1479f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        final int leftPadding = mContext.getResources()
1489f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            .getDimensionPixelOffset(R.dimen.status_bar_recents_thumbnail_left_margin);
1499f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        setOverScrollEffectPadding(leftPadding, 0);
1509f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    }
1519f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
1523c728fe61dcf49354ea761a3e4ef6032ec271630Jim Miller    @Override
1533c728fe61dcf49354ea761a3e4ef6032ec271630Jim Miller    protected void onConfigurationChanged(Configuration newConfig) {
1543c728fe61dcf49354ea761a3e4ef6032ec271630Jim Miller        super.onConfigurationChanged(newConfig);
15507d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        float densityScale = getResources().getDisplayMetrics().density;
15607d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        mSwipeHelper.setDensityScale(densityScale);
15707d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        float pagingTouchSlop = ViewConfiguration.get(mContext).getScaledPagingTouchSlop();
15807d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        mSwipeHelper.setPagingTouchSlop(pagingTouchSlop);
1593c728fe61dcf49354ea761a3e4ef6032ec271630Jim Miller    }
1603c728fe61dcf49354ea761a3e4ef6032ec271630Jim Miller
1619f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    private void setOverScrollEffectPadding(int leftPadding, int i) {
1623c728fe61dcf49354ea761a3e4ef6032ec271630Jim Miller        // TODO Add to (Vertical)ScrollView
1639f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    }
1649f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
1659f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    @Override
1669f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
1679f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        super.onSizeChanged(w, h, oldw, oldh);
1689f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        // Keep track of the last visible item in the list so we can restore it
1699f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        // to the bottom when the orientation changes.
1709f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        mLastScrollPosition = scrollPositionOfMostRecent();
1719f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
1729f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        // This has to happen post-layout, so run it "in the future"
1739f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        post(new Runnable() {
1749f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            public void run() {
17517dfec7111fcc53a4f6ae6e92b4a7f85a278fe71Jim Miller                scrollTo(mLastScrollPosition, 0);
1769f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            }
1779f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        });
1789f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    }
1799f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
1809f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    @Override
1819f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    protected void onVisibilityChanged(View changedView, int visibility) {
1829f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        super.onVisibilityChanged(changedView, visibility);
1839f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        // scroll to bottom after reloading
1849f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        if (visibility == View.VISIBLE && changedView == this) {
1859f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            post(new Runnable() {
1869f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                public void run() {
1879f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                    update();
1889f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                }
1899f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            });
1909f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        }
1919f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    }
1929f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
1933b1fc47d004f6b29af8f40d181baa3460b1e3b15Michael Jurka    public void setAdapter(ActivityDescriptionAdapter adapter) {
1949f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        mAdapter = adapter;
1959f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        mAdapter.registerDataSetObserver(new DataSetObserver() {
1969f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            public void onChanged() {
1979f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                update();
1989f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            }
1999f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
2009f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            public void onInvalidated() {
2019f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller                update();
2029f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller            }
2039f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        });
2049f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    }
2059f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
2069f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    @Override
2079f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    public void setLayoutTransition(LayoutTransition transition) {
2089f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        // The layout transition applies to our embedded LinearLayout
2099f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        mLinearLayout.setLayoutTransition(transition);
2109f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    }
2119f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller
2129f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    public void setCallback(RecentsCallback callback) {
2139f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller        mCallback = callback;
2149f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller    }
2159f0f0e0e3100caec459a5b5ef836317844c83b3fJim Miller}
216