1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/base/TabsTray.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,664 @@ 1.4 +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.7 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +package org.mozilla.gecko; 1.10 + 1.11 +import java.util.ArrayList; 1.12 +import java.util.List; 1.13 + 1.14 +import org.mozilla.gecko.animation.PropertyAnimator; 1.15 +import org.mozilla.gecko.animation.PropertyAnimator.Property; 1.16 +import org.mozilla.gecko.animation.ViewHelper; 1.17 +import org.mozilla.gecko.widget.TwoWayView; 1.18 +import org.mozilla.gecko.widget.TabThumbnailWrapper; 1.19 + 1.20 +import android.content.Context; 1.21 +import android.content.res.TypedArray; 1.22 +import android.graphics.Rect; 1.23 +import android.graphics.drawable.Drawable; 1.24 +import android.util.AttributeSet; 1.25 +import android.view.LayoutInflater; 1.26 +import android.view.MotionEvent; 1.27 +import android.view.VelocityTracker; 1.28 +import android.view.View; 1.29 +import android.view.ViewConfiguration; 1.30 +import android.view.ViewGroup; 1.31 +import android.widget.BaseAdapter; 1.32 +import android.widget.Button; 1.33 +import android.widget.ImageButton; 1.34 +import android.widget.ImageView; 1.35 +import android.widget.TextView; 1.36 + 1.37 +public class TabsTray extends TwoWayView 1.38 + implements TabsPanel.PanelView { 1.39 + private static final String LOGTAG = "GeckoTabsTray"; 1.40 + 1.41 + private Context mContext; 1.42 + private TabsPanel mTabsPanel; 1.43 + 1.44 + private TabsAdapter mTabsAdapter; 1.45 + 1.46 + private List<View> mPendingClosedTabs; 1.47 + private int mCloseAnimationCount; 1.48 + 1.49 + private TabSwipeGestureListener mSwipeListener; 1.50 + 1.51 + // Time to animate non-flinged tabs of screen, in milliseconds 1.52 + private static final int ANIMATION_DURATION = 250; 1.53 + 1.54 + private static final String ABOUT_HOME = "about:home"; 1.55 + private int mOriginalSize = 0; 1.56 + 1.57 + public TabsTray(Context context, AttributeSet attrs) { 1.58 + super(context, attrs); 1.59 + mContext = context; 1.60 + 1.61 + mCloseAnimationCount = 0; 1.62 + mPendingClosedTabs = new ArrayList<View>(); 1.63 + 1.64 + setItemsCanFocus(true); 1.65 + 1.66 + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TabsTray); 1.67 + boolean isPrivate = (a.getInt(R.styleable.TabsTray_tabs, 0x0) == 1); 1.68 + a.recycle(); 1.69 + 1.70 + mTabsAdapter = new TabsAdapter(mContext, isPrivate); 1.71 + setAdapter(mTabsAdapter); 1.72 + 1.73 + mSwipeListener = new TabSwipeGestureListener(); 1.74 + setOnTouchListener(mSwipeListener); 1.75 + setOnScrollListener(mSwipeListener.makeScrollListener()); 1.76 + 1.77 + setRecyclerListener(new RecyclerListener() { 1.78 + @Override 1.79 + public void onMovedToScrapHeap(View view) { 1.80 + TabRow row = (TabRow) view.getTag(); 1.81 + row.thumbnail.setImageDrawable(null); 1.82 + row.close.setVisibility(View.VISIBLE); 1.83 + } 1.84 + }); 1.85 + } 1.86 + 1.87 + @Override 1.88 + public ViewGroup getLayout() { 1.89 + return this; 1.90 + } 1.91 + 1.92 + @Override 1.93 + public void setTabsPanel(TabsPanel panel) { 1.94 + mTabsPanel = panel; 1.95 + } 1.96 + 1.97 + @Override 1.98 + public void show() { 1.99 + setVisibility(View.VISIBLE); 1.100 + Tabs.getInstance().refreshThumbnails(); 1.101 + Tabs.registerOnTabsChangedListener(mTabsAdapter); 1.102 + mTabsAdapter.refreshTabsData(); 1.103 + } 1.104 + 1.105 + @Override 1.106 + public void hide() { 1.107 + setVisibility(View.GONE); 1.108 + Tabs.unregisterOnTabsChangedListener(mTabsAdapter); 1.109 + GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Tab:Screenshot:Cancel","")); 1.110 + mTabsAdapter.clear(); 1.111 + } 1.112 + 1.113 + @Override 1.114 + public boolean shouldExpand() { 1.115 + return isVertical(); 1.116 + } 1.117 + 1.118 + private void autoHidePanel() { 1.119 + mTabsPanel.autoHidePanel(); 1.120 + } 1.121 + 1.122 + // ViewHolder for a row in the list 1.123 + private class TabRow { 1.124 + int id; 1.125 + TextView title; 1.126 + ImageView thumbnail; 1.127 + ImageButton close; 1.128 + ViewGroup info; 1.129 + TabThumbnailWrapper thumbnailWrapper; 1.130 + 1.131 + public TabRow(View view) { 1.132 + info = (ViewGroup) view; 1.133 + title = (TextView) view.findViewById(R.id.title); 1.134 + thumbnail = (ImageView) view.findViewById(R.id.thumbnail); 1.135 + close = (ImageButton) view.findViewById(R.id.close); 1.136 + thumbnailWrapper = (TabThumbnailWrapper) view.findViewById(R.id.wrapper); 1.137 + } 1.138 + } 1.139 + 1.140 + // Adapter to bind tabs into a list 1.141 + private class TabsAdapter extends BaseAdapter implements Tabs.OnTabsChangedListener { 1.142 + private Context mContext; 1.143 + private boolean mIsPrivate; 1.144 + private ArrayList<Tab> mTabs; 1.145 + private LayoutInflater mInflater; 1.146 + private Button.OnClickListener mOnCloseClickListener; 1.147 + 1.148 + public TabsAdapter(Context context, boolean isPrivate) { 1.149 + mContext = context; 1.150 + mInflater = LayoutInflater.from(mContext); 1.151 + mIsPrivate = isPrivate; 1.152 + 1.153 + mOnCloseClickListener = new Button.OnClickListener() { 1.154 + @Override 1.155 + public void onClick(View v) { 1.156 + TabRow tab = (TabRow) v.getTag(); 1.157 + final int pos = (isVertical() ? tab.info.getWidth() : tab.info.getHeight()); 1.158 + animateClose(tab.info, pos); 1.159 + } 1.160 + }; 1.161 + } 1.162 + 1.163 + @Override 1.164 + public void onTabChanged(Tab tab, Tabs.TabEvents msg, Object data) { 1.165 + switch (msg) { 1.166 + case ADDED: 1.167 + // Refresh the list to make sure the new tab is added in the right position. 1.168 + refreshTabsData(); 1.169 + break; 1.170 + 1.171 + case CLOSED: 1.172 + removeTab(tab); 1.173 + break; 1.174 + 1.175 + case SELECTED: 1.176 + // Update the selected position, then fall through... 1.177 + updateSelectedPosition(); 1.178 + case UNSELECTED: 1.179 + // We just need to update the style for the unselected tab... 1.180 + case THUMBNAIL: 1.181 + case TITLE: 1.182 + case RECORDING_CHANGE: 1.183 + View view = TabsTray.this.getChildAt(getPositionForTab(tab) - TabsTray.this.getFirstVisiblePosition()); 1.184 + if (view == null) 1.185 + return; 1.186 + 1.187 + TabRow row = (TabRow) view.getTag(); 1.188 + assignValues(row, tab); 1.189 + break; 1.190 + } 1.191 + } 1.192 + 1.193 + private void refreshTabsData() { 1.194 + // Store a different copy of the tabs, so that we don't have to worry about 1.195 + // accidentally updating it on the wrong thread. 1.196 + mTabs = new ArrayList<Tab>(); 1.197 + 1.198 + Iterable<Tab> tabs = Tabs.getInstance().getTabsInOrder(); 1.199 + for (Tab tab : tabs) { 1.200 + if (tab.isPrivate() == mIsPrivate) 1.201 + mTabs.add(tab); 1.202 + } 1.203 + 1.204 + notifyDataSetChanged(); // Be sure to call this whenever mTabs changes. 1.205 + updateSelectedPosition(); 1.206 + } 1.207 + 1.208 + // Updates the selected position in the list so that it will be scrolled to the right place. 1.209 + private void updateSelectedPosition() { 1.210 + int selected = getPositionForTab(Tabs.getInstance().getSelectedTab()); 1.211 + updateSelectedStyle(selected); 1.212 + 1.213 + if (selected != -1) { 1.214 + TabsTray.this.setSelection(selected); 1.215 + } 1.216 + } 1.217 + 1.218 + /** 1.219 + * Updates the selected/unselected style for the tabs. 1.220 + * 1.221 + * @param selected position of the selected tab 1.222 + */ 1.223 + private void updateSelectedStyle(int selected) { 1.224 + for (int i = 0; i < getCount(); i++) { 1.225 + TabsTray.this.setItemChecked(i, (i == selected)); 1.226 + } 1.227 + } 1.228 + 1.229 + public void clear() { 1.230 + mTabs = null; 1.231 + notifyDataSetChanged(); // Be sure to call this whenever mTabs changes. 1.232 + } 1.233 + 1.234 + @Override 1.235 + public int getCount() { 1.236 + return (mTabs == null ? 0 : mTabs.size()); 1.237 + } 1.238 + 1.239 + @Override 1.240 + public Tab getItem(int position) { 1.241 + return mTabs.get(position); 1.242 + } 1.243 + 1.244 + @Override 1.245 + public long getItemId(int position) { 1.246 + return position; 1.247 + } 1.248 + 1.249 + private int getPositionForTab(Tab tab) { 1.250 + if (mTabs == null || tab == null) 1.251 + return -1; 1.252 + 1.253 + return mTabs.indexOf(tab); 1.254 + } 1.255 + 1.256 + private void removeTab(Tab tab) { 1.257 + if (tab.isPrivate() == mIsPrivate && mTabs != null) { 1.258 + mTabs.remove(tab); 1.259 + notifyDataSetChanged(); // Be sure to call this whenever mTabs changes. 1.260 + 1.261 + int selected = getPositionForTab(Tabs.getInstance().getSelectedTab()); 1.262 + updateSelectedStyle(selected); 1.263 + } 1.264 + } 1.265 + 1.266 + private void assignValues(TabRow row, Tab tab) { 1.267 + if (row == null || tab == null) 1.268 + return; 1.269 + 1.270 + row.id = tab.getId(); 1.271 + 1.272 + Drawable thumbnailImage = tab.getThumbnail(); 1.273 + if (thumbnailImage != null) { 1.274 + row.thumbnail.setImageDrawable(thumbnailImage); 1.275 + } else if (AboutPages.isAboutHome(tab.getURL())) { 1.276 + row.thumbnail.setImageResource(R.drawable.abouthome_thumbnail); 1.277 + } else { 1.278 + row.thumbnail.setImageResource(R.drawable.tab_thumbnail_default); 1.279 + } 1.280 + if (row.thumbnailWrapper != null) { 1.281 + row.thumbnailWrapper.setRecording(tab.isRecording()); 1.282 + } 1.283 + row.title.setText(tab.getDisplayTitle()); 1.284 + row.close.setTag(row); 1.285 + } 1.286 + 1.287 + private void resetTransforms(View view) { 1.288 + ViewHelper.setAlpha(view, 1); 1.289 + if (mOriginalSize == 0) 1.290 + return; 1.291 + 1.292 + if (isVertical()) { 1.293 + ViewHelper.setHeight(view, mOriginalSize); 1.294 + ViewHelper.setTranslationX(view, 0); 1.295 + } else { 1.296 + ViewHelper.setWidth(view, mOriginalSize); 1.297 + ViewHelper.setTranslationY(view, 0); 1.298 + } 1.299 + } 1.300 + 1.301 + @Override 1.302 + public View getView(int position, View convertView, ViewGroup parent) { 1.303 + TabRow row; 1.304 + 1.305 + if (convertView == null) { 1.306 + convertView = mInflater.inflate(R.layout.tabs_row, null); 1.307 + row = new TabRow(convertView); 1.308 + row.close.setOnClickListener(mOnCloseClickListener); 1.309 + convertView.setTag(row); 1.310 + } else { 1.311 + row = (TabRow) convertView.getTag(); 1.312 + // If we're recycling this view, there's a chance it was transformed during 1.313 + // the close animation. Remove any of those properties. 1.314 + resetTransforms(convertView); 1.315 + } 1.316 + 1.317 + Tab tab = mTabs.get(position); 1.318 + assignValues(row, tab); 1.319 + 1.320 + return convertView; 1.321 + } 1.322 + } 1.323 + 1.324 + private boolean isVertical() { 1.325 + return (getOrientation().compareTo(TwoWayView.Orientation.VERTICAL) == 0); 1.326 + } 1.327 + 1.328 + private void animateClose(final View view, int pos) { 1.329 + PropertyAnimator animator = new PropertyAnimator(ANIMATION_DURATION); 1.330 + animator.attach(view, Property.ALPHA, 0); 1.331 + 1.332 + if (isVertical()) 1.333 + animator.attach(view, Property.TRANSLATION_X, pos); 1.334 + else 1.335 + animator.attach(view, Property.TRANSLATION_Y, pos); 1.336 + 1.337 + mCloseAnimationCount++; 1.338 + mPendingClosedTabs.add(view); 1.339 + 1.340 + animator.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() { 1.341 + @Override 1.342 + public void onPropertyAnimationStart() { } 1.343 + @Override 1.344 + public void onPropertyAnimationEnd() { 1.345 + mCloseAnimationCount--; 1.346 + if (mCloseAnimationCount > 0) 1.347 + return; 1.348 + 1.349 + for (View pendingView : mPendingClosedTabs) { 1.350 + animateFinishClose(pendingView); 1.351 + } 1.352 + 1.353 + mPendingClosedTabs.clear(); 1.354 + } 1.355 + }); 1.356 + 1.357 + if (mTabsAdapter.getCount() == 1) 1.358 + autoHidePanel(); 1.359 + 1.360 + animator.start(); 1.361 + } 1.362 + 1.363 + private void animateFinishClose(final View view) { 1.364 + PropertyAnimator animator = new PropertyAnimator(ANIMATION_DURATION); 1.365 + 1.366 + final boolean isVertical = isVertical(); 1.367 + if (isVertical) 1.368 + animator.attach(view, Property.HEIGHT, 1); 1.369 + else 1.370 + animator.attach(view, Property.WIDTH, 1); 1.371 + 1.372 + TabRow tab = (TabRow)view.getTag(); 1.373 + final int tabId = tab.id; 1.374 + // Caching this assumes that all rows are the same height 1.375 + if (mOriginalSize == 0) 1.376 + mOriginalSize = (isVertical ? view.getHeight() : view.getWidth()); 1.377 + 1.378 + animator.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() { 1.379 + @Override 1.380 + public void onPropertyAnimationStart() { } 1.381 + @Override 1.382 + public void onPropertyAnimationEnd() { 1.383 + Tabs tabs = Tabs.getInstance(); 1.384 + Tab tab = tabs.getTab(tabId); 1.385 + tabs.closeTab(tab); 1.386 + } 1.387 + }); 1.388 + 1.389 + animator.start(); 1.390 + } 1.391 + 1.392 + private void animateCancel(final View view) { 1.393 + PropertyAnimator animator = new PropertyAnimator(ANIMATION_DURATION); 1.394 + animator.attach(view, Property.ALPHA, 1); 1.395 + 1.396 + if (isVertical()) 1.397 + animator.attach(view, Property.TRANSLATION_X, 0); 1.398 + else 1.399 + animator.attach(view, Property.TRANSLATION_Y, 0); 1.400 + 1.401 + 1.402 + animator.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() { 1.403 + @Override 1.404 + public void onPropertyAnimationStart() { } 1.405 + @Override 1.406 + public void onPropertyAnimationEnd() { 1.407 + TabRow tab = (TabRow) view.getTag(); 1.408 + tab.close.setVisibility(View.VISIBLE); 1.409 + } 1.410 + }); 1.411 + 1.412 + animator.start(); 1.413 + } 1.414 + 1.415 + private class TabSwipeGestureListener implements View.OnTouchListener { 1.416 + // same value the stock browser uses for after drag animation velocity in pixels/sec 1.417 + // http://androidxref.com/4.0.4/xref/packages/apps/Browser/src/com/android/browser/NavTabScroller.java#61 1.418 + private static final float MIN_VELOCITY = 750; 1.419 + 1.420 + private int mSwipeThreshold; 1.421 + private int mMinFlingVelocity; 1.422 + 1.423 + private int mMaxFlingVelocity; 1.424 + private VelocityTracker mVelocityTracker; 1.425 + 1.426 + private int mListWidth = 1; 1.427 + private int mListHeight = 1; 1.428 + 1.429 + private View mSwipeView; 1.430 + private int mSwipeViewPosition; 1.431 + private Runnable mPendingCheckForTap; 1.432 + 1.433 + private float mSwipeStartX; 1.434 + private float mSwipeStartY; 1.435 + private boolean mSwiping; 1.436 + private boolean mEnabled; 1.437 + 1.438 + public TabSwipeGestureListener() { 1.439 + mSwipeView = null; 1.440 + mSwipeViewPosition = TwoWayView.INVALID_POSITION; 1.441 + mSwiping = false; 1.442 + mEnabled = true; 1.443 + 1.444 + ViewConfiguration vc = ViewConfiguration.get(TabsTray.this.getContext()); 1.445 + mSwipeThreshold = vc.getScaledTouchSlop(); 1.446 + mMinFlingVelocity = (int) (getContext().getResources().getDisplayMetrics().density * MIN_VELOCITY); 1.447 + mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity(); 1.448 + } 1.449 + 1.450 + public void setEnabled(boolean enabled) { 1.451 + mEnabled = enabled; 1.452 + } 1.453 + 1.454 + public TwoWayView.OnScrollListener makeScrollListener() { 1.455 + return new TwoWayView.OnScrollListener() { 1.456 + @Override 1.457 + public void onScrollStateChanged(TwoWayView twoWayView, int scrollState) { 1.458 + setEnabled(scrollState != TwoWayView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL); 1.459 + } 1.460 + 1.461 + @Override 1.462 + public void onScroll(TwoWayView twoWayView, int i, int i1, int i2) { 1.463 + } 1.464 + }; 1.465 + } 1.466 + 1.467 + @Override 1.468 + public boolean onTouch(View view, MotionEvent e) { 1.469 + if (!mEnabled) 1.470 + return false; 1.471 + 1.472 + if (mListWidth < 2 || mListHeight < 2) { 1.473 + mListWidth = TabsTray.this.getWidth(); 1.474 + mListHeight = TabsTray.this.getHeight(); 1.475 + } 1.476 + 1.477 + switch (e.getActionMasked()) { 1.478 + case MotionEvent.ACTION_DOWN: { 1.479 + // Check if we should set pressed state on the 1.480 + // touched view after a standard delay. 1.481 + triggerCheckForTap(); 1.482 + 1.483 + final float x = e.getRawX(); 1.484 + final float y = e.getRawY(); 1.485 + 1.486 + // Find out which view is being touched 1.487 + mSwipeView = findViewAt(x, y); 1.488 + 1.489 + if (mSwipeView != null) { 1.490 + mSwipeStartX = e.getRawX(); 1.491 + mSwipeStartY = e.getRawY(); 1.492 + mSwipeViewPosition = TabsTray.this.getPositionForView(mSwipeView); 1.493 + 1.494 + mVelocityTracker = VelocityTracker.obtain(); 1.495 + mVelocityTracker.addMovement(e); 1.496 + } 1.497 + 1.498 + view.onTouchEvent(e); 1.499 + return true; 1.500 + } 1.501 + 1.502 + case MotionEvent.ACTION_UP: { 1.503 + if (mSwipeView == null) 1.504 + break; 1.505 + 1.506 + cancelCheckForTap(); 1.507 + mSwipeView.setPressed(false); 1.508 + 1.509 + if (!mSwiping) { 1.510 + TabRow tab = (TabRow) mSwipeView.getTag(); 1.511 + Tabs.getInstance().selectTab(tab.id); 1.512 + autoHidePanel(); 1.513 + break; 1.514 + } 1.515 + 1.516 + mVelocityTracker.addMovement(e); 1.517 + mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity); 1.518 + 1.519 + float velocityX = Math.abs(mVelocityTracker.getXVelocity()); 1.520 + float velocityY = Math.abs(mVelocityTracker.getYVelocity()); 1.521 + 1.522 + boolean dismiss = false; 1.523 + boolean dismissDirection = false; 1.524 + int dismissTranslation = 0; 1.525 + 1.526 + if (isVertical()) { 1.527 + float deltaX = ViewHelper.getTranslationX(mSwipeView); 1.528 + 1.529 + if (Math.abs(deltaX) > mListWidth / 2) { 1.530 + dismiss = true; 1.531 + dismissDirection = (deltaX > 0); 1.532 + } else if (mMinFlingVelocity <= velocityX && velocityX <= mMaxFlingVelocity 1.533 + && velocityY < velocityX) { 1.534 + dismiss = mSwiping && (deltaX * mVelocityTracker.getXVelocity() > 0); 1.535 + dismissDirection = (mVelocityTracker.getXVelocity() > 0); 1.536 + } 1.537 + 1.538 + dismissTranslation = (dismissDirection ? mListWidth : -mListWidth); 1.539 + } else { 1.540 + float deltaY = ViewHelper.getTranslationY(mSwipeView); 1.541 + 1.542 + if (Math.abs(deltaY) > mListHeight / 2) { 1.543 + dismiss = true; 1.544 + dismissDirection = (deltaY > 0); 1.545 + } else if (mMinFlingVelocity <= velocityY && velocityY <= mMaxFlingVelocity 1.546 + && velocityX < velocityY) { 1.547 + dismiss = mSwiping && (deltaY * mVelocityTracker.getYVelocity() > 0); 1.548 + dismissDirection = (mVelocityTracker.getYVelocity() > 0); 1.549 + } 1.550 + 1.551 + dismissTranslation = (dismissDirection ? mListHeight : -mListHeight); 1.552 + } 1.553 + 1.554 + if (dismiss) 1.555 + animateClose(mSwipeView, dismissTranslation); 1.556 + else 1.557 + animateCancel(mSwipeView); 1.558 + 1.559 + mVelocityTracker = null; 1.560 + mSwipeView = null; 1.561 + mSwipeViewPosition = TwoWayView.INVALID_POSITION; 1.562 + 1.563 + mSwipeStartX = 0; 1.564 + mSwipeStartY = 0; 1.565 + mSwiping = false; 1.566 + 1.567 + break; 1.568 + } 1.569 + 1.570 + case MotionEvent.ACTION_MOVE: { 1.571 + if (mSwipeView == null) 1.572 + break; 1.573 + 1.574 + mVelocityTracker.addMovement(e); 1.575 + 1.576 + final boolean isVertical = isVertical(); 1.577 + 1.578 + float deltaX = e.getRawX() - mSwipeStartX; 1.579 + float deltaY = e.getRawY() - mSwipeStartY; 1.580 + float delta = (isVertical ? deltaX : deltaY); 1.581 + 1.582 + boolean isScrollingX = Math.abs(deltaX) > mSwipeThreshold; 1.583 + boolean isScrollingY = Math.abs(deltaY) > mSwipeThreshold; 1.584 + boolean isSwipingToClose = (isVertical ? isScrollingX : isScrollingY); 1.585 + 1.586 + // If we're actually swiping, make sure we don't 1.587 + // set pressed state on the swiped view. 1.588 + if (isScrollingX || isScrollingY) 1.589 + cancelCheckForTap(); 1.590 + 1.591 + if (isSwipingToClose) { 1.592 + mSwiping = true; 1.593 + TabsTray.this.requestDisallowInterceptTouchEvent(true); 1.594 + 1.595 + TabRow tab = (TabRow) mSwipeView.getTag(); 1.596 + tab.close.setVisibility(View.INVISIBLE); 1.597 + 1.598 + // Stops listview from highlighting the touched item 1.599 + // in the list when swiping. 1.600 + MotionEvent cancelEvent = MotionEvent.obtain(e); 1.601 + cancelEvent.setAction(MotionEvent.ACTION_CANCEL | 1.602 + (e.getActionIndex() << MotionEvent.ACTION_POINTER_INDEX_SHIFT)); 1.603 + TabsTray.this.onTouchEvent(cancelEvent); 1.604 + } 1.605 + 1.606 + if (mSwiping) { 1.607 + if (isVertical) 1.608 + ViewHelper.setTranslationX(mSwipeView, delta); 1.609 + else 1.610 + ViewHelper.setTranslationY(mSwipeView, delta); 1.611 + 1.612 + ViewHelper.setAlpha(mSwipeView, Math.max(0.1f, Math.min(1f, 1.613 + 1f - 2f * Math.abs(delta) / (isVertical ? mListWidth : mListHeight)))); 1.614 + 1.615 + return true; 1.616 + } 1.617 + 1.618 + break; 1.619 + } 1.620 + } 1.621 + 1.622 + return false; 1.623 + } 1.624 + 1.625 + private View findViewAt(float rawX, float rawY) { 1.626 + Rect rect = new Rect(); 1.627 + 1.628 + int[] listViewCoords = new int[2]; 1.629 + TabsTray.this.getLocationOnScreen(listViewCoords); 1.630 + 1.631 + int x = (int) rawX - listViewCoords[0]; 1.632 + int y = (int) rawY - listViewCoords[1]; 1.633 + 1.634 + for (int i = 0; i < TabsTray.this.getChildCount(); i++) { 1.635 + View child = TabsTray.this.getChildAt(i); 1.636 + child.getHitRect(rect); 1.637 + 1.638 + if (rect.contains(x, y)) 1.639 + return child; 1.640 + } 1.641 + 1.642 + return null; 1.643 + } 1.644 + 1.645 + private void triggerCheckForTap() { 1.646 + if (mPendingCheckForTap == null) 1.647 + mPendingCheckForTap = new CheckForTap(); 1.648 + 1.649 + TabsTray.this.postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout()); 1.650 + } 1.651 + 1.652 + private void cancelCheckForTap() { 1.653 + if (mPendingCheckForTap == null) 1.654 + return; 1.655 + 1.656 + TabsTray.this.removeCallbacks(mPendingCheckForTap); 1.657 + } 1.658 + 1.659 + private class CheckForTap implements Runnable { 1.660 + @Override 1.661 + public void run() { 1.662 + if (!mSwiping && mSwipeView != null && mEnabled) 1.663 + mSwipeView.setPressed(true); 1.664 + } 1.665 + } 1.666 + } 1.667 +}