/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.recent; import android.animation.LayoutTransition; import android.content.Context; import android.content.res.Configuration; import android.database.DataSetObserver; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import android.widget.HorizontalScrollView; import android.widget.LinearLayout; import com.android.systemui.R; import com.android.systemui.SwipeHelper; import com.android.systemui.recent.RecentsPanelView.ActivityDescriptionAdapter; public class RecentsHorizontalScrollView extends HorizontalScrollView implements SwipeHelper.Callback { private static final String TAG = RecentsPanelView.TAG; private static final boolean DEBUG = RecentsPanelView.DEBUG; private LinearLayout mLinearLayout; private ActivityDescriptionAdapter mAdapter; private RecentsCallback mCallback; protected int mLastScrollPosition; private SwipeHelper mSwipeHelper; private OnLongClickListener mOnLongClick = new OnLongClickListener() { public boolean onLongClick(View v) { final View anchorView = v.findViewById(R.id.app_description); mCallback.handleLongPress(v, anchorView); return true; } }; public RecentsHorizontalScrollView(Context context) { this(context, null); } public RecentsHorizontalScrollView(Context context, AttributeSet attrs) { super(context, attrs, 0); float densityScale = getResources().getDisplayMetrics().density; float pagingTouchSlop = ViewConfiguration.get(mContext).getScaledPagingTouchSlop(); mSwipeHelper = new SwipeHelper(SwipeHelper.Y, this, densityScale, pagingTouchSlop); } private int scrollPositionOfMostRecent() { return mLinearLayout.getWidth() - getWidth(); } private void update() { mLinearLayout.removeAllViews(); for (int i = 0; i < mAdapter.getCount(); i++) { final View view = mAdapter.getView(i, null, mLinearLayout); view.setLongClickable(true); view.setOnLongClickListener(mOnLongClick); final View thumbnail = getChildContentView(view); // thumbnail is set to clickable in the layout file thumbnail.setOnClickListener(new OnClickListener() { public void onClick(View v) { mCallback.handleOnClick(view); } }); mLinearLayout.addView(view); } // Scroll to end after layout. post(new Runnable() { public void run() { mLastScrollPosition = scrollPositionOfMostRecent(); scrollTo(mLastScrollPosition, 0); } }); } @Override public void removeViewInLayout(final View view) { dismissChild(view); } public boolean onInterceptTouchEvent(MotionEvent ev) { if (DEBUG) Log.v(TAG, "onInterceptTouchEvent()"); return mSwipeHelper.onInterceptTouchEvent(ev) || super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent ev) { return mSwipeHelper.onTouchEvent(ev) || super.onTouchEvent(ev); } public boolean canChildBeDismissed(View v) { return true; } public void dismissChild(View v) { mSwipeHelper.dismissChild(v, 0); } public void onChildDismissed(View v) { mLinearLayout.removeView(v); mCallback.handleSwipe(v); } public void onBeginDrag(View v) { } public View getChildAtPosition(MotionEvent ev) { final float x = ev.getX() + getScrollX(); final float y = ev.getY() + getScrollY(); for (int i = 0; i < mLinearLayout.getChildCount(); i++) { View item = mLinearLayout.getChildAt(i); if (x >= item.getLeft() && x < item.getRight() && y >= item.getTop() && y < item.getBottom()) { return item; } } return null; } public View getChildContentView(View v) { return v.findViewById(R.id.app_thumbnail); } @Override protected void onFinishInflate() { super.onFinishInflate(); setScrollbarFadingEnabled(true); mLinearLayout = (LinearLayout) findViewById(R.id.recents_linear_layout); final int leftPadding = mContext.getResources() .getDimensionPixelOffset(R.dimen.status_bar_recents_thumbnail_left_margin); setOverScrollEffectPadding(leftPadding, 0); } @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); float densityScale = getResources().getDisplayMetrics().density; mSwipeHelper.setDensityScale(densityScale); float pagingTouchSlop = ViewConfiguration.get(mContext).getScaledPagingTouchSlop(); mSwipeHelper.setPagingTouchSlop(pagingTouchSlop); } private void setOverScrollEffectPadding(int leftPadding, int i) { // TODO Add to (Vertical)ScrollView } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); // Keep track of the last visible item in the list so we can restore it // to the bottom when the orientation changes. mLastScrollPosition = scrollPositionOfMostRecent(); // This has to happen post-layout, so run it "in the future" post(new Runnable() { public void run() { scrollTo(mLastScrollPosition, 0); } }); } @Override protected void onVisibilityChanged(View changedView, int visibility) { super.onVisibilityChanged(changedView, visibility); // scroll to bottom after reloading if (visibility == View.VISIBLE && changedView == this) { post(new Runnable() { public void run() { update(); } }); } } public void setAdapter(ActivityDescriptionAdapter adapter) { mAdapter = adapter; mAdapter.registerDataSetObserver(new DataSetObserver() { public void onChanged() { update(); } public void onInvalidated() { update(); } }); } @Override public void setLayoutTransition(LayoutTransition transition) { // The layout transition applies to our embedded LinearLayout mLinearLayout.setLayoutTransition(transition); } public void setCallback(RecentsCallback callback) { mCallback = callback; } }