mobile/android/base/TabsTray.java

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 4 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 package org.mozilla.gecko;
michael@0 7
michael@0 8 import java.util.ArrayList;
michael@0 9 import java.util.List;
michael@0 10
michael@0 11 import org.mozilla.gecko.animation.PropertyAnimator;
michael@0 12 import org.mozilla.gecko.animation.PropertyAnimator.Property;
michael@0 13 import org.mozilla.gecko.animation.ViewHelper;
michael@0 14 import org.mozilla.gecko.widget.TwoWayView;
michael@0 15 import org.mozilla.gecko.widget.TabThumbnailWrapper;
michael@0 16
michael@0 17 import android.content.Context;
michael@0 18 import android.content.res.TypedArray;
michael@0 19 import android.graphics.Rect;
michael@0 20 import android.graphics.drawable.Drawable;
michael@0 21 import android.util.AttributeSet;
michael@0 22 import android.view.LayoutInflater;
michael@0 23 import android.view.MotionEvent;
michael@0 24 import android.view.VelocityTracker;
michael@0 25 import android.view.View;
michael@0 26 import android.view.ViewConfiguration;
michael@0 27 import android.view.ViewGroup;
michael@0 28 import android.widget.BaseAdapter;
michael@0 29 import android.widget.Button;
michael@0 30 import android.widget.ImageButton;
michael@0 31 import android.widget.ImageView;
michael@0 32 import android.widget.TextView;
michael@0 33
michael@0 34 public class TabsTray extends TwoWayView
michael@0 35 implements TabsPanel.PanelView {
michael@0 36 private static final String LOGTAG = "GeckoTabsTray";
michael@0 37
michael@0 38 private Context mContext;
michael@0 39 private TabsPanel mTabsPanel;
michael@0 40
michael@0 41 private TabsAdapter mTabsAdapter;
michael@0 42
michael@0 43 private List<View> mPendingClosedTabs;
michael@0 44 private int mCloseAnimationCount;
michael@0 45
michael@0 46 private TabSwipeGestureListener mSwipeListener;
michael@0 47
michael@0 48 // Time to animate non-flinged tabs of screen, in milliseconds
michael@0 49 private static final int ANIMATION_DURATION = 250;
michael@0 50
michael@0 51 private static final String ABOUT_HOME = "about:home";
michael@0 52 private int mOriginalSize = 0;
michael@0 53
michael@0 54 public TabsTray(Context context, AttributeSet attrs) {
michael@0 55 super(context, attrs);
michael@0 56 mContext = context;
michael@0 57
michael@0 58 mCloseAnimationCount = 0;
michael@0 59 mPendingClosedTabs = new ArrayList<View>();
michael@0 60
michael@0 61 setItemsCanFocus(true);
michael@0 62
michael@0 63 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TabsTray);
michael@0 64 boolean isPrivate = (a.getInt(R.styleable.TabsTray_tabs, 0x0) == 1);
michael@0 65 a.recycle();
michael@0 66
michael@0 67 mTabsAdapter = new TabsAdapter(mContext, isPrivate);
michael@0 68 setAdapter(mTabsAdapter);
michael@0 69
michael@0 70 mSwipeListener = new TabSwipeGestureListener();
michael@0 71 setOnTouchListener(mSwipeListener);
michael@0 72 setOnScrollListener(mSwipeListener.makeScrollListener());
michael@0 73
michael@0 74 setRecyclerListener(new RecyclerListener() {
michael@0 75 @Override
michael@0 76 public void onMovedToScrapHeap(View view) {
michael@0 77 TabRow row = (TabRow) view.getTag();
michael@0 78 row.thumbnail.setImageDrawable(null);
michael@0 79 row.close.setVisibility(View.VISIBLE);
michael@0 80 }
michael@0 81 });
michael@0 82 }
michael@0 83
michael@0 84 @Override
michael@0 85 public ViewGroup getLayout() {
michael@0 86 return this;
michael@0 87 }
michael@0 88
michael@0 89 @Override
michael@0 90 public void setTabsPanel(TabsPanel panel) {
michael@0 91 mTabsPanel = panel;
michael@0 92 }
michael@0 93
michael@0 94 @Override
michael@0 95 public void show() {
michael@0 96 setVisibility(View.VISIBLE);
michael@0 97 Tabs.getInstance().refreshThumbnails();
michael@0 98 Tabs.registerOnTabsChangedListener(mTabsAdapter);
michael@0 99 mTabsAdapter.refreshTabsData();
michael@0 100 }
michael@0 101
michael@0 102 @Override
michael@0 103 public void hide() {
michael@0 104 setVisibility(View.GONE);
michael@0 105 Tabs.unregisterOnTabsChangedListener(mTabsAdapter);
michael@0 106 GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Tab:Screenshot:Cancel",""));
michael@0 107 mTabsAdapter.clear();
michael@0 108 }
michael@0 109
michael@0 110 @Override
michael@0 111 public boolean shouldExpand() {
michael@0 112 return isVertical();
michael@0 113 }
michael@0 114
michael@0 115 private void autoHidePanel() {
michael@0 116 mTabsPanel.autoHidePanel();
michael@0 117 }
michael@0 118
michael@0 119 // ViewHolder for a row in the list
michael@0 120 private class TabRow {
michael@0 121 int id;
michael@0 122 TextView title;
michael@0 123 ImageView thumbnail;
michael@0 124 ImageButton close;
michael@0 125 ViewGroup info;
michael@0 126 TabThumbnailWrapper thumbnailWrapper;
michael@0 127
michael@0 128 public TabRow(View view) {
michael@0 129 info = (ViewGroup) view;
michael@0 130 title = (TextView) view.findViewById(R.id.title);
michael@0 131 thumbnail = (ImageView) view.findViewById(R.id.thumbnail);
michael@0 132 close = (ImageButton) view.findViewById(R.id.close);
michael@0 133 thumbnailWrapper = (TabThumbnailWrapper) view.findViewById(R.id.wrapper);
michael@0 134 }
michael@0 135 }
michael@0 136
michael@0 137 // Adapter to bind tabs into a list
michael@0 138 private class TabsAdapter extends BaseAdapter implements Tabs.OnTabsChangedListener {
michael@0 139 private Context mContext;
michael@0 140 private boolean mIsPrivate;
michael@0 141 private ArrayList<Tab> mTabs;
michael@0 142 private LayoutInflater mInflater;
michael@0 143 private Button.OnClickListener mOnCloseClickListener;
michael@0 144
michael@0 145 public TabsAdapter(Context context, boolean isPrivate) {
michael@0 146 mContext = context;
michael@0 147 mInflater = LayoutInflater.from(mContext);
michael@0 148 mIsPrivate = isPrivate;
michael@0 149
michael@0 150 mOnCloseClickListener = new Button.OnClickListener() {
michael@0 151 @Override
michael@0 152 public void onClick(View v) {
michael@0 153 TabRow tab = (TabRow) v.getTag();
michael@0 154 final int pos = (isVertical() ? tab.info.getWidth() : tab.info.getHeight());
michael@0 155 animateClose(tab.info, pos);
michael@0 156 }
michael@0 157 };
michael@0 158 }
michael@0 159
michael@0 160 @Override
michael@0 161 public void onTabChanged(Tab tab, Tabs.TabEvents msg, Object data) {
michael@0 162 switch (msg) {
michael@0 163 case ADDED:
michael@0 164 // Refresh the list to make sure the new tab is added in the right position.
michael@0 165 refreshTabsData();
michael@0 166 break;
michael@0 167
michael@0 168 case CLOSED:
michael@0 169 removeTab(tab);
michael@0 170 break;
michael@0 171
michael@0 172 case SELECTED:
michael@0 173 // Update the selected position, then fall through...
michael@0 174 updateSelectedPosition();
michael@0 175 case UNSELECTED:
michael@0 176 // We just need to update the style for the unselected tab...
michael@0 177 case THUMBNAIL:
michael@0 178 case TITLE:
michael@0 179 case RECORDING_CHANGE:
michael@0 180 View view = TabsTray.this.getChildAt(getPositionForTab(tab) - TabsTray.this.getFirstVisiblePosition());
michael@0 181 if (view == null)
michael@0 182 return;
michael@0 183
michael@0 184 TabRow row = (TabRow) view.getTag();
michael@0 185 assignValues(row, tab);
michael@0 186 break;
michael@0 187 }
michael@0 188 }
michael@0 189
michael@0 190 private void refreshTabsData() {
michael@0 191 // Store a different copy of the tabs, so that we don't have to worry about
michael@0 192 // accidentally updating it on the wrong thread.
michael@0 193 mTabs = new ArrayList<Tab>();
michael@0 194
michael@0 195 Iterable<Tab> tabs = Tabs.getInstance().getTabsInOrder();
michael@0 196 for (Tab tab : tabs) {
michael@0 197 if (tab.isPrivate() == mIsPrivate)
michael@0 198 mTabs.add(tab);
michael@0 199 }
michael@0 200
michael@0 201 notifyDataSetChanged(); // Be sure to call this whenever mTabs changes.
michael@0 202 updateSelectedPosition();
michael@0 203 }
michael@0 204
michael@0 205 // Updates the selected position in the list so that it will be scrolled to the right place.
michael@0 206 private void updateSelectedPosition() {
michael@0 207 int selected = getPositionForTab(Tabs.getInstance().getSelectedTab());
michael@0 208 updateSelectedStyle(selected);
michael@0 209
michael@0 210 if (selected != -1) {
michael@0 211 TabsTray.this.setSelection(selected);
michael@0 212 }
michael@0 213 }
michael@0 214
michael@0 215 /**
michael@0 216 * Updates the selected/unselected style for the tabs.
michael@0 217 *
michael@0 218 * @param selected position of the selected tab
michael@0 219 */
michael@0 220 private void updateSelectedStyle(int selected) {
michael@0 221 for (int i = 0; i < getCount(); i++) {
michael@0 222 TabsTray.this.setItemChecked(i, (i == selected));
michael@0 223 }
michael@0 224 }
michael@0 225
michael@0 226 public void clear() {
michael@0 227 mTabs = null;
michael@0 228 notifyDataSetChanged(); // Be sure to call this whenever mTabs changes.
michael@0 229 }
michael@0 230
michael@0 231 @Override
michael@0 232 public int getCount() {
michael@0 233 return (mTabs == null ? 0 : mTabs.size());
michael@0 234 }
michael@0 235
michael@0 236 @Override
michael@0 237 public Tab getItem(int position) {
michael@0 238 return mTabs.get(position);
michael@0 239 }
michael@0 240
michael@0 241 @Override
michael@0 242 public long getItemId(int position) {
michael@0 243 return position;
michael@0 244 }
michael@0 245
michael@0 246 private int getPositionForTab(Tab tab) {
michael@0 247 if (mTabs == null || tab == null)
michael@0 248 return -1;
michael@0 249
michael@0 250 return mTabs.indexOf(tab);
michael@0 251 }
michael@0 252
michael@0 253 private void removeTab(Tab tab) {
michael@0 254 if (tab.isPrivate() == mIsPrivate && mTabs != null) {
michael@0 255 mTabs.remove(tab);
michael@0 256 notifyDataSetChanged(); // Be sure to call this whenever mTabs changes.
michael@0 257
michael@0 258 int selected = getPositionForTab(Tabs.getInstance().getSelectedTab());
michael@0 259 updateSelectedStyle(selected);
michael@0 260 }
michael@0 261 }
michael@0 262
michael@0 263 private void assignValues(TabRow row, Tab tab) {
michael@0 264 if (row == null || tab == null)
michael@0 265 return;
michael@0 266
michael@0 267 row.id = tab.getId();
michael@0 268
michael@0 269 Drawable thumbnailImage = tab.getThumbnail();
michael@0 270 if (thumbnailImage != null) {
michael@0 271 row.thumbnail.setImageDrawable(thumbnailImage);
michael@0 272 } else if (AboutPages.isAboutHome(tab.getURL())) {
michael@0 273 row.thumbnail.setImageResource(R.drawable.abouthome_thumbnail);
michael@0 274 } else {
michael@0 275 row.thumbnail.setImageResource(R.drawable.tab_thumbnail_default);
michael@0 276 }
michael@0 277 if (row.thumbnailWrapper != null) {
michael@0 278 row.thumbnailWrapper.setRecording(tab.isRecording());
michael@0 279 }
michael@0 280 row.title.setText(tab.getDisplayTitle());
michael@0 281 row.close.setTag(row);
michael@0 282 }
michael@0 283
michael@0 284 private void resetTransforms(View view) {
michael@0 285 ViewHelper.setAlpha(view, 1);
michael@0 286 if (mOriginalSize == 0)
michael@0 287 return;
michael@0 288
michael@0 289 if (isVertical()) {
michael@0 290 ViewHelper.setHeight(view, mOriginalSize);
michael@0 291 ViewHelper.setTranslationX(view, 0);
michael@0 292 } else {
michael@0 293 ViewHelper.setWidth(view, mOriginalSize);
michael@0 294 ViewHelper.setTranslationY(view, 0);
michael@0 295 }
michael@0 296 }
michael@0 297
michael@0 298 @Override
michael@0 299 public View getView(int position, View convertView, ViewGroup parent) {
michael@0 300 TabRow row;
michael@0 301
michael@0 302 if (convertView == null) {
michael@0 303 convertView = mInflater.inflate(R.layout.tabs_row, null);
michael@0 304 row = new TabRow(convertView);
michael@0 305 row.close.setOnClickListener(mOnCloseClickListener);
michael@0 306 convertView.setTag(row);
michael@0 307 } else {
michael@0 308 row = (TabRow) convertView.getTag();
michael@0 309 // If we're recycling this view, there's a chance it was transformed during
michael@0 310 // the close animation. Remove any of those properties.
michael@0 311 resetTransforms(convertView);
michael@0 312 }
michael@0 313
michael@0 314 Tab tab = mTabs.get(position);
michael@0 315 assignValues(row, tab);
michael@0 316
michael@0 317 return convertView;
michael@0 318 }
michael@0 319 }
michael@0 320
michael@0 321 private boolean isVertical() {
michael@0 322 return (getOrientation().compareTo(TwoWayView.Orientation.VERTICAL) == 0);
michael@0 323 }
michael@0 324
michael@0 325 private void animateClose(final View view, int pos) {
michael@0 326 PropertyAnimator animator = new PropertyAnimator(ANIMATION_DURATION);
michael@0 327 animator.attach(view, Property.ALPHA, 0);
michael@0 328
michael@0 329 if (isVertical())
michael@0 330 animator.attach(view, Property.TRANSLATION_X, pos);
michael@0 331 else
michael@0 332 animator.attach(view, Property.TRANSLATION_Y, pos);
michael@0 333
michael@0 334 mCloseAnimationCount++;
michael@0 335 mPendingClosedTabs.add(view);
michael@0 336
michael@0 337 animator.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() {
michael@0 338 @Override
michael@0 339 public void onPropertyAnimationStart() { }
michael@0 340 @Override
michael@0 341 public void onPropertyAnimationEnd() {
michael@0 342 mCloseAnimationCount--;
michael@0 343 if (mCloseAnimationCount > 0)
michael@0 344 return;
michael@0 345
michael@0 346 for (View pendingView : mPendingClosedTabs) {
michael@0 347 animateFinishClose(pendingView);
michael@0 348 }
michael@0 349
michael@0 350 mPendingClosedTabs.clear();
michael@0 351 }
michael@0 352 });
michael@0 353
michael@0 354 if (mTabsAdapter.getCount() == 1)
michael@0 355 autoHidePanel();
michael@0 356
michael@0 357 animator.start();
michael@0 358 }
michael@0 359
michael@0 360 private void animateFinishClose(final View view) {
michael@0 361 PropertyAnimator animator = new PropertyAnimator(ANIMATION_DURATION);
michael@0 362
michael@0 363 final boolean isVertical = isVertical();
michael@0 364 if (isVertical)
michael@0 365 animator.attach(view, Property.HEIGHT, 1);
michael@0 366 else
michael@0 367 animator.attach(view, Property.WIDTH, 1);
michael@0 368
michael@0 369 TabRow tab = (TabRow)view.getTag();
michael@0 370 final int tabId = tab.id;
michael@0 371 // Caching this assumes that all rows are the same height
michael@0 372 if (mOriginalSize == 0)
michael@0 373 mOriginalSize = (isVertical ? view.getHeight() : view.getWidth());
michael@0 374
michael@0 375 animator.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() {
michael@0 376 @Override
michael@0 377 public void onPropertyAnimationStart() { }
michael@0 378 @Override
michael@0 379 public void onPropertyAnimationEnd() {
michael@0 380 Tabs tabs = Tabs.getInstance();
michael@0 381 Tab tab = tabs.getTab(tabId);
michael@0 382 tabs.closeTab(tab);
michael@0 383 }
michael@0 384 });
michael@0 385
michael@0 386 animator.start();
michael@0 387 }
michael@0 388
michael@0 389 private void animateCancel(final View view) {
michael@0 390 PropertyAnimator animator = new PropertyAnimator(ANIMATION_DURATION);
michael@0 391 animator.attach(view, Property.ALPHA, 1);
michael@0 392
michael@0 393 if (isVertical())
michael@0 394 animator.attach(view, Property.TRANSLATION_X, 0);
michael@0 395 else
michael@0 396 animator.attach(view, Property.TRANSLATION_Y, 0);
michael@0 397
michael@0 398
michael@0 399 animator.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() {
michael@0 400 @Override
michael@0 401 public void onPropertyAnimationStart() { }
michael@0 402 @Override
michael@0 403 public void onPropertyAnimationEnd() {
michael@0 404 TabRow tab = (TabRow) view.getTag();
michael@0 405 tab.close.setVisibility(View.VISIBLE);
michael@0 406 }
michael@0 407 });
michael@0 408
michael@0 409 animator.start();
michael@0 410 }
michael@0 411
michael@0 412 private class TabSwipeGestureListener implements View.OnTouchListener {
michael@0 413 // same value the stock browser uses for after drag animation velocity in pixels/sec
michael@0 414 // http://androidxref.com/4.0.4/xref/packages/apps/Browser/src/com/android/browser/NavTabScroller.java#61
michael@0 415 private static final float MIN_VELOCITY = 750;
michael@0 416
michael@0 417 private int mSwipeThreshold;
michael@0 418 private int mMinFlingVelocity;
michael@0 419
michael@0 420 private int mMaxFlingVelocity;
michael@0 421 private VelocityTracker mVelocityTracker;
michael@0 422
michael@0 423 private int mListWidth = 1;
michael@0 424 private int mListHeight = 1;
michael@0 425
michael@0 426 private View mSwipeView;
michael@0 427 private int mSwipeViewPosition;
michael@0 428 private Runnable mPendingCheckForTap;
michael@0 429
michael@0 430 private float mSwipeStartX;
michael@0 431 private float mSwipeStartY;
michael@0 432 private boolean mSwiping;
michael@0 433 private boolean mEnabled;
michael@0 434
michael@0 435 public TabSwipeGestureListener() {
michael@0 436 mSwipeView = null;
michael@0 437 mSwipeViewPosition = TwoWayView.INVALID_POSITION;
michael@0 438 mSwiping = false;
michael@0 439 mEnabled = true;
michael@0 440
michael@0 441 ViewConfiguration vc = ViewConfiguration.get(TabsTray.this.getContext());
michael@0 442 mSwipeThreshold = vc.getScaledTouchSlop();
michael@0 443 mMinFlingVelocity = (int) (getContext().getResources().getDisplayMetrics().density * MIN_VELOCITY);
michael@0 444 mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
michael@0 445 }
michael@0 446
michael@0 447 public void setEnabled(boolean enabled) {
michael@0 448 mEnabled = enabled;
michael@0 449 }
michael@0 450
michael@0 451 public TwoWayView.OnScrollListener makeScrollListener() {
michael@0 452 return new TwoWayView.OnScrollListener() {
michael@0 453 @Override
michael@0 454 public void onScrollStateChanged(TwoWayView twoWayView, int scrollState) {
michael@0 455 setEnabled(scrollState != TwoWayView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
michael@0 456 }
michael@0 457
michael@0 458 @Override
michael@0 459 public void onScroll(TwoWayView twoWayView, int i, int i1, int i2) {
michael@0 460 }
michael@0 461 };
michael@0 462 }
michael@0 463
michael@0 464 @Override
michael@0 465 public boolean onTouch(View view, MotionEvent e) {
michael@0 466 if (!mEnabled)
michael@0 467 return false;
michael@0 468
michael@0 469 if (mListWidth < 2 || mListHeight < 2) {
michael@0 470 mListWidth = TabsTray.this.getWidth();
michael@0 471 mListHeight = TabsTray.this.getHeight();
michael@0 472 }
michael@0 473
michael@0 474 switch (e.getActionMasked()) {
michael@0 475 case MotionEvent.ACTION_DOWN: {
michael@0 476 // Check if we should set pressed state on the
michael@0 477 // touched view after a standard delay.
michael@0 478 triggerCheckForTap();
michael@0 479
michael@0 480 final float x = e.getRawX();
michael@0 481 final float y = e.getRawY();
michael@0 482
michael@0 483 // Find out which view is being touched
michael@0 484 mSwipeView = findViewAt(x, y);
michael@0 485
michael@0 486 if (mSwipeView != null) {
michael@0 487 mSwipeStartX = e.getRawX();
michael@0 488 mSwipeStartY = e.getRawY();
michael@0 489 mSwipeViewPosition = TabsTray.this.getPositionForView(mSwipeView);
michael@0 490
michael@0 491 mVelocityTracker = VelocityTracker.obtain();
michael@0 492 mVelocityTracker.addMovement(e);
michael@0 493 }
michael@0 494
michael@0 495 view.onTouchEvent(e);
michael@0 496 return true;
michael@0 497 }
michael@0 498
michael@0 499 case MotionEvent.ACTION_UP: {
michael@0 500 if (mSwipeView == null)
michael@0 501 break;
michael@0 502
michael@0 503 cancelCheckForTap();
michael@0 504 mSwipeView.setPressed(false);
michael@0 505
michael@0 506 if (!mSwiping) {
michael@0 507 TabRow tab = (TabRow) mSwipeView.getTag();
michael@0 508 Tabs.getInstance().selectTab(tab.id);
michael@0 509 autoHidePanel();
michael@0 510 break;
michael@0 511 }
michael@0 512
michael@0 513 mVelocityTracker.addMovement(e);
michael@0 514 mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity);
michael@0 515
michael@0 516 float velocityX = Math.abs(mVelocityTracker.getXVelocity());
michael@0 517 float velocityY = Math.abs(mVelocityTracker.getYVelocity());
michael@0 518
michael@0 519 boolean dismiss = false;
michael@0 520 boolean dismissDirection = false;
michael@0 521 int dismissTranslation = 0;
michael@0 522
michael@0 523 if (isVertical()) {
michael@0 524 float deltaX = ViewHelper.getTranslationX(mSwipeView);
michael@0 525
michael@0 526 if (Math.abs(deltaX) > mListWidth / 2) {
michael@0 527 dismiss = true;
michael@0 528 dismissDirection = (deltaX > 0);
michael@0 529 } else if (mMinFlingVelocity <= velocityX && velocityX <= mMaxFlingVelocity
michael@0 530 && velocityY < velocityX) {
michael@0 531 dismiss = mSwiping && (deltaX * mVelocityTracker.getXVelocity() > 0);
michael@0 532 dismissDirection = (mVelocityTracker.getXVelocity() > 0);
michael@0 533 }
michael@0 534
michael@0 535 dismissTranslation = (dismissDirection ? mListWidth : -mListWidth);
michael@0 536 } else {
michael@0 537 float deltaY = ViewHelper.getTranslationY(mSwipeView);
michael@0 538
michael@0 539 if (Math.abs(deltaY) > mListHeight / 2) {
michael@0 540 dismiss = true;
michael@0 541 dismissDirection = (deltaY > 0);
michael@0 542 } else if (mMinFlingVelocity <= velocityY && velocityY <= mMaxFlingVelocity
michael@0 543 && velocityX < velocityY) {
michael@0 544 dismiss = mSwiping && (deltaY * mVelocityTracker.getYVelocity() > 0);
michael@0 545 dismissDirection = (mVelocityTracker.getYVelocity() > 0);
michael@0 546 }
michael@0 547
michael@0 548 dismissTranslation = (dismissDirection ? mListHeight : -mListHeight);
michael@0 549 }
michael@0 550
michael@0 551 if (dismiss)
michael@0 552 animateClose(mSwipeView, dismissTranslation);
michael@0 553 else
michael@0 554 animateCancel(mSwipeView);
michael@0 555
michael@0 556 mVelocityTracker = null;
michael@0 557 mSwipeView = null;
michael@0 558 mSwipeViewPosition = TwoWayView.INVALID_POSITION;
michael@0 559
michael@0 560 mSwipeStartX = 0;
michael@0 561 mSwipeStartY = 0;
michael@0 562 mSwiping = false;
michael@0 563
michael@0 564 break;
michael@0 565 }
michael@0 566
michael@0 567 case MotionEvent.ACTION_MOVE: {
michael@0 568 if (mSwipeView == null)
michael@0 569 break;
michael@0 570
michael@0 571 mVelocityTracker.addMovement(e);
michael@0 572
michael@0 573 final boolean isVertical = isVertical();
michael@0 574
michael@0 575 float deltaX = e.getRawX() - mSwipeStartX;
michael@0 576 float deltaY = e.getRawY() - mSwipeStartY;
michael@0 577 float delta = (isVertical ? deltaX : deltaY);
michael@0 578
michael@0 579 boolean isScrollingX = Math.abs(deltaX) > mSwipeThreshold;
michael@0 580 boolean isScrollingY = Math.abs(deltaY) > mSwipeThreshold;
michael@0 581 boolean isSwipingToClose = (isVertical ? isScrollingX : isScrollingY);
michael@0 582
michael@0 583 // If we're actually swiping, make sure we don't
michael@0 584 // set pressed state on the swiped view.
michael@0 585 if (isScrollingX || isScrollingY)
michael@0 586 cancelCheckForTap();
michael@0 587
michael@0 588 if (isSwipingToClose) {
michael@0 589 mSwiping = true;
michael@0 590 TabsTray.this.requestDisallowInterceptTouchEvent(true);
michael@0 591
michael@0 592 TabRow tab = (TabRow) mSwipeView.getTag();
michael@0 593 tab.close.setVisibility(View.INVISIBLE);
michael@0 594
michael@0 595 // Stops listview from highlighting the touched item
michael@0 596 // in the list when swiping.
michael@0 597 MotionEvent cancelEvent = MotionEvent.obtain(e);
michael@0 598 cancelEvent.setAction(MotionEvent.ACTION_CANCEL |
michael@0 599 (e.getActionIndex() << MotionEvent.ACTION_POINTER_INDEX_SHIFT));
michael@0 600 TabsTray.this.onTouchEvent(cancelEvent);
michael@0 601 }
michael@0 602
michael@0 603 if (mSwiping) {
michael@0 604 if (isVertical)
michael@0 605 ViewHelper.setTranslationX(mSwipeView, delta);
michael@0 606 else
michael@0 607 ViewHelper.setTranslationY(mSwipeView, delta);
michael@0 608
michael@0 609 ViewHelper.setAlpha(mSwipeView, Math.max(0.1f, Math.min(1f,
michael@0 610 1f - 2f * Math.abs(delta) / (isVertical ? mListWidth : mListHeight))));
michael@0 611
michael@0 612 return true;
michael@0 613 }
michael@0 614
michael@0 615 break;
michael@0 616 }
michael@0 617 }
michael@0 618
michael@0 619 return false;
michael@0 620 }
michael@0 621
michael@0 622 private View findViewAt(float rawX, float rawY) {
michael@0 623 Rect rect = new Rect();
michael@0 624
michael@0 625 int[] listViewCoords = new int[2];
michael@0 626 TabsTray.this.getLocationOnScreen(listViewCoords);
michael@0 627
michael@0 628 int x = (int) rawX - listViewCoords[0];
michael@0 629 int y = (int) rawY - listViewCoords[1];
michael@0 630
michael@0 631 for (int i = 0; i < TabsTray.this.getChildCount(); i++) {
michael@0 632 View child = TabsTray.this.getChildAt(i);
michael@0 633 child.getHitRect(rect);
michael@0 634
michael@0 635 if (rect.contains(x, y))
michael@0 636 return child;
michael@0 637 }
michael@0 638
michael@0 639 return null;
michael@0 640 }
michael@0 641
michael@0 642 private void triggerCheckForTap() {
michael@0 643 if (mPendingCheckForTap == null)
michael@0 644 mPendingCheckForTap = new CheckForTap();
michael@0 645
michael@0 646 TabsTray.this.postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
michael@0 647 }
michael@0 648
michael@0 649 private void cancelCheckForTap() {
michael@0 650 if (mPendingCheckForTap == null)
michael@0 651 return;
michael@0 652
michael@0 653 TabsTray.this.removeCallbacks(mPendingCheckForTap);
michael@0 654 }
michael@0 655
michael@0 656 private class CheckForTap implements Runnable {
michael@0 657 @Override
michael@0 658 public void run() {
michael@0 659 if (!mSwiping && mSwipeView != null && mEnabled)
michael@0 660 mSwipeView.setPressed(true);
michael@0 661 }
michael@0 662 }
michael@0 663 }
michael@0 664 }

mercurial