mirror of
https://github.com/classilla/tenfourfox.git
synced 2024-07-01 14:29:41 +00:00
658 lines
23 KiB
Java
658 lines
23 KiB
Java
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
package org.mozilla.gecko.tabs;
|
|
|
|
import org.mozilla.gecko.GeckoAppShell;
|
|
import org.mozilla.gecko.GeckoEvent;
|
|
import org.mozilla.gecko.R;
|
|
import org.mozilla.gecko.Tab;
|
|
import org.mozilla.gecko.Tabs;
|
|
import org.mozilla.gecko.animation.PropertyAnimator;
|
|
import org.mozilla.gecko.animation.PropertyAnimator.Property;
|
|
import org.mozilla.gecko.animation.ViewHelper;
|
|
import org.mozilla.gecko.tabs.TabsPanel.TabsLayout;
|
|
import org.mozilla.gecko.util.ThreadUtils;
|
|
import org.mozilla.gecko.widget.TwoWayView;
|
|
import org.mozilla.gecko.widget.themed.ThemedRelativeLayout;
|
|
|
|
import android.content.Context;
|
|
import android.content.res.TypedArray;
|
|
import android.graphics.Rect;
|
|
import android.util.AttributeSet;
|
|
import android.view.MotionEvent;
|
|
import android.view.VelocityTracker;
|
|
import android.view.View;
|
|
import android.view.ViewConfiguration;
|
|
import android.view.ViewGroup;
|
|
import android.widget.Button;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
class TabsListLayout extends TwoWayView
|
|
implements TabsLayout,
|
|
Tabs.OnTabsChangedListener {
|
|
|
|
private static final String LOGTAG = "Gecko" + TabsListLayout.class.getSimpleName();
|
|
|
|
// Time to animate non-flinged tabs of screen, in milliseconds
|
|
private static final int ANIMATION_DURATION = 250;
|
|
|
|
// Time between starting successive tab animations in closeAllTabs.
|
|
private static final int ANIMATION_CASCADE_DELAY = 75;
|
|
|
|
private final boolean isPrivate;
|
|
private final TabsLayoutAdapter tabsAdapter;
|
|
private final List<View> pendingClosedTabs;
|
|
private TabsPanel tabsPanel;
|
|
private int closeAnimationCount;
|
|
private int closeAllAnimationCount;
|
|
private int originalSize;
|
|
|
|
public TabsListLayout(Context context, AttributeSet attrs) {
|
|
super(context, attrs);
|
|
|
|
pendingClosedTabs = new ArrayList<View>();
|
|
|
|
setItemsCanFocus(true);
|
|
|
|
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TabsLayout);
|
|
isPrivate = (a.getInt(R.styleable.TabsLayout_tabs, 0x0) == 1);
|
|
a.recycle();
|
|
|
|
tabsAdapter = new TabsListLayoutAdapter(context);
|
|
setAdapter(tabsAdapter);
|
|
|
|
final TabSwipeGestureListener swipeListener = new TabSwipeGestureListener();
|
|
setOnTouchListener(swipeListener);
|
|
setOnScrollListener(swipeListener.makeScrollListener());
|
|
setRecyclerListener(new RecyclerListener() {
|
|
@Override
|
|
public void onMovedToScrapHeap(View view) {
|
|
TabsLayoutItemView item = (TabsLayoutItemView) view;
|
|
item.setThumbnail(null);
|
|
item.setCloseVisible(true);
|
|
}
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public void setTabsPanel(TabsPanel panel) {
|
|
tabsPanel = panel;
|
|
}
|
|
|
|
@Override
|
|
public void show() {
|
|
setVisibility(View.VISIBLE);
|
|
Tabs.getInstance().refreshThumbnails();
|
|
Tabs.registerOnTabsChangedListener(this);
|
|
refreshTabsData();
|
|
}
|
|
|
|
@Override
|
|
public void hide() {
|
|
setVisibility(View.GONE);
|
|
Tabs.unregisterOnTabsChangedListener(this);
|
|
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Tab:Screenshot:Cancel", ""));
|
|
tabsAdapter.clear();
|
|
}
|
|
|
|
@Override
|
|
public boolean shouldExpand() {
|
|
return isVertical();
|
|
}
|
|
|
|
private void autoHidePanel() {
|
|
tabsPanel.autoHidePanel();
|
|
}
|
|
|
|
@Override
|
|
public void onTabChanged(Tab tab, Tabs.TabEvents msg, Object data) {
|
|
switch (msg) {
|
|
case ADDED:
|
|
// Refresh the list to make sure the new tab is added in the right position.
|
|
refreshTabsData();
|
|
break;
|
|
|
|
case CLOSED:
|
|
if (tab.isPrivate() == isPrivate && tabsAdapter.getCount() > 0) {
|
|
if (tabsAdapter.removeTab(tab)) {
|
|
int selected = tabsAdapter.getPositionForTab(Tabs.getInstance().getSelectedTab());
|
|
updateSelectedStyle(selected);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case SELECTED:
|
|
// Update the selected position, then fall through...
|
|
updateSelectedPosition();
|
|
case UNSELECTED:
|
|
// We just need to update the style for the unselected tab...
|
|
case THUMBNAIL:
|
|
case TITLE:
|
|
case RECORDING_CHANGE:
|
|
case AUDIO_PLAYING_CHANGE:
|
|
View view = getChildAt(tabsAdapter.getPositionForTab(tab) - getFirstVisiblePosition());
|
|
if (view == null) {
|
|
return;
|
|
}
|
|
|
|
TabsLayoutItemView item = (TabsLayoutItemView) view;
|
|
item.assignValues(tab);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Updates the selected position in the list so that it will be scrolled to the right place.
|
|
private void updateSelectedPosition() {
|
|
int selected = tabsAdapter.getPositionForTab(Tabs.getInstance().getSelectedTab());
|
|
updateSelectedStyle(selected);
|
|
if (selected != -1) {
|
|
setSelection(selected);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Updates the selected/unselected style for the tabs.
|
|
*
|
|
* @param selected position of the selected tab
|
|
*/
|
|
private void updateSelectedStyle(int selected) {
|
|
for (int i = 0; i < tabsAdapter.getCount(); i++) {
|
|
setItemChecked(i, (i == selected));
|
|
}
|
|
}
|
|
|
|
private void refreshTabsData() {
|
|
// Store a different copy of the tabs, so that we don't have to worry about
|
|
// accidentally updating it on the wrong thread.
|
|
ArrayList<Tab> tabData = new ArrayList<Tab>();
|
|
Iterable<Tab> allTabs = Tabs.getInstance().getTabsInOrder();
|
|
|
|
for (Tab tab : allTabs) {
|
|
if (tab.isPrivate() == isPrivate) {
|
|
tabData.add(tab);
|
|
}
|
|
}
|
|
|
|
tabsAdapter.setTabs(tabData);
|
|
updateSelectedPosition();
|
|
}
|
|
|
|
public void resetTransforms(View view) {
|
|
ViewHelper.setAlpha(view, 1);
|
|
|
|
if (isVertical()) {
|
|
ViewHelper.setTranslationX(view, 0);
|
|
} else {
|
|
ViewHelper.setTranslationY(view, 0);
|
|
}
|
|
|
|
// We only need to reset the height or width after individual tab close animations.
|
|
if (originalSize != 0) {
|
|
if (isVertical()) {
|
|
ViewHelper.setHeight(view, originalSize);
|
|
} else {
|
|
ViewHelper.setWidth(view, originalSize);
|
|
}
|
|
}
|
|
}
|
|
|
|
private boolean isVertical() {
|
|
return (getOrientation().compareTo(TwoWayView.Orientation.VERTICAL) == 0);
|
|
}
|
|
|
|
@Override
|
|
public void closeAll() {
|
|
final int childCount = getChildCount();
|
|
|
|
// Just close the panel if there are no tabs to close.
|
|
if (childCount == 0) {
|
|
autoHidePanel();
|
|
return;
|
|
}
|
|
|
|
// Disable the view so that gestures won't interfere wth the tab close animation.
|
|
setEnabled(false);
|
|
|
|
// Delay starting each successive animation to create a cascade effect.
|
|
int cascadeDelay = 0;
|
|
for (int i = childCount - 1; i >= 0; i--) {
|
|
final View view = getChildAt(i);
|
|
|
|
final PropertyAnimator animator = new PropertyAnimator(ANIMATION_DURATION);
|
|
animator.attach(view, Property.ALPHA, 0);
|
|
|
|
if (isVertical()) {
|
|
animator.attach(view, Property.TRANSLATION_X, view.getWidth());
|
|
} else {
|
|
animator.attach(view, Property.TRANSLATION_Y, view.getHeight());
|
|
}
|
|
|
|
closeAllAnimationCount++;
|
|
|
|
animator.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() {
|
|
@Override
|
|
public void onPropertyAnimationStart() {
|
|
}
|
|
|
|
@Override
|
|
public void onPropertyAnimationEnd() {
|
|
closeAllAnimationCount--;
|
|
if (closeAllAnimationCount > 0) {
|
|
return;
|
|
}
|
|
|
|
// Hide the panel after the animation is done.
|
|
autoHidePanel();
|
|
|
|
// Re-enable the view after the animation is done.
|
|
TabsListLayout.this.setEnabled(true);
|
|
|
|
// Then actually close all the tabs.
|
|
final Iterable<Tab> tabs = Tabs.getInstance().getTabsInOrder();
|
|
|
|
for (Tab tab : tabs) {
|
|
// In the normal panel we want to close all tabs (both private and normal),
|
|
// but in the private panel we only want to close private tabs.
|
|
if (!isPrivate || tab.isPrivate()) {
|
|
Tabs.getInstance().closeTab(tab, false);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
ThreadUtils.getUiHandler().postDelayed(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
animator.start();
|
|
}
|
|
}, cascadeDelay);
|
|
|
|
cascadeDelay += ANIMATION_CASCADE_DELAY;
|
|
}
|
|
}
|
|
|
|
private void animateClose(final View view, int pos) {
|
|
PropertyAnimator animator = new PropertyAnimator(ANIMATION_DURATION);
|
|
animator.attach(view, Property.ALPHA, 0);
|
|
|
|
if (isVertical()) {
|
|
animator.attach(view, Property.TRANSLATION_X, pos);
|
|
} else {
|
|
animator.attach(view, Property.TRANSLATION_Y, pos);
|
|
}
|
|
|
|
closeAnimationCount++;
|
|
|
|
pendingClosedTabs.add(view);
|
|
|
|
animator.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() {
|
|
@Override
|
|
public void onPropertyAnimationStart() {
|
|
}
|
|
|
|
@Override
|
|
public void onPropertyAnimationEnd() {
|
|
closeAnimationCount--;
|
|
if (closeAnimationCount > 0) {
|
|
return;
|
|
}
|
|
|
|
for (View pendingView : pendingClosedTabs) {
|
|
animateFinishClose(pendingView);
|
|
}
|
|
pendingClosedTabs.clear();
|
|
}
|
|
});
|
|
|
|
if (tabsAdapter.getCount() == 1) {
|
|
autoHidePanel();
|
|
}
|
|
|
|
animator.start();
|
|
}
|
|
|
|
private void animateFinishClose(final View view) {
|
|
final boolean isVertical = isVertical();
|
|
|
|
PropertyAnimator animator = new PropertyAnimator(ANIMATION_DURATION);
|
|
|
|
if (isVertical) {
|
|
animator.attach(view, Property.HEIGHT, 1);
|
|
} else {
|
|
animator.attach(view, Property.WIDTH, 1);
|
|
}
|
|
|
|
final int tabId = ((TabsLayoutItemView) view).getTabId();
|
|
|
|
// Caching this assumes that all rows are the same height
|
|
if (originalSize == 0) {
|
|
originalSize = (isVertical ? view.getHeight() : view.getWidth());
|
|
}
|
|
|
|
animator.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() {
|
|
@Override
|
|
public void onPropertyAnimationStart() {
|
|
}
|
|
|
|
@Override
|
|
public void onPropertyAnimationEnd() {
|
|
Tabs tabs = Tabs.getInstance();
|
|
Tab tab = tabs.getTab(tabId);
|
|
tabs.closeTab(tab, true);
|
|
}
|
|
});
|
|
|
|
animator.start();
|
|
}
|
|
|
|
private void animateCancel(final View view) {
|
|
PropertyAnimator animator = new PropertyAnimator(ANIMATION_DURATION);
|
|
animator.attach(view, Property.ALPHA, 1);
|
|
|
|
if (isVertical()) {
|
|
animator.attach(view, Property.TRANSLATION_X, 0);
|
|
} else {
|
|
animator.attach(view, Property.TRANSLATION_Y, 0);
|
|
}
|
|
|
|
animator.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() {
|
|
@Override
|
|
public void onPropertyAnimationStart() {
|
|
}
|
|
|
|
@Override
|
|
public void onPropertyAnimationEnd() {
|
|
TabsLayoutItemView tab = (TabsLayoutItemView) view;
|
|
tab.setCloseVisible(true);
|
|
}
|
|
});
|
|
|
|
animator.start();
|
|
}
|
|
|
|
private class TabsListLayoutAdapter extends TabsLayoutAdapter {
|
|
private final Button.OnClickListener mCloseOnClickListener;
|
|
|
|
public TabsListLayoutAdapter(Context context) {
|
|
super(context, R.layout.tabs_list_item_view);
|
|
mCloseOnClickListener = new Button.OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
// The view here is the close button, which has a reference
|
|
// to the parent TabsLayoutItemView in it's tag, hence the getTag() call
|
|
TabsLayoutItemView item = (TabsLayoutItemView) v.getTag();
|
|
final int pos = (isVertical() ? item.getWidth() : 0 - item.getHeight());
|
|
animateClose(item, pos);
|
|
}
|
|
};
|
|
}
|
|
|
|
@Override
|
|
public TabsLayoutItemView newView(int position, ViewGroup parent) {
|
|
TabsLayoutItemView item = super.newView(position, parent);
|
|
item.setCloseOnClickListener(mCloseOnClickListener);
|
|
((ThemedRelativeLayout) item.findViewById(R.id.wrapper)).setPrivateMode(isPrivate);
|
|
return item;
|
|
}
|
|
|
|
@Override
|
|
public void bindView(TabsLayoutItemView view, Tab tab) {
|
|
super.bindView(view, tab);
|
|
// If we're recycling this view, there's a chance it was transformed during
|
|
// the close animation. Remove any of those properties.
|
|
resetTransforms(view);
|
|
}
|
|
}
|
|
|
|
private class TabSwipeGestureListener implements View.OnTouchListener {
|
|
// same value the stock browser uses for after drag animation velocity in pixels/sec
|
|
// http://androidxref.com/4.0.4/xref/packages/apps/Browser/src/com/android/browser/NavTabScroller.java#61
|
|
private static final float MIN_VELOCITY = 750;
|
|
|
|
private final int swipeThreshold;
|
|
private final int minFlingVelocity;
|
|
private final int maxFlingVelocity;
|
|
private VelocityTracker velocityTracker;
|
|
private int listWidth = 1;
|
|
private int listHeight = 1;
|
|
private View swipeView;
|
|
private Runnable pendingCheckForTap;
|
|
private float swipeStartX;
|
|
private float swipeStartY;
|
|
private boolean swiping;
|
|
private boolean enabled;
|
|
|
|
public TabSwipeGestureListener() {
|
|
enabled = true;
|
|
|
|
ViewConfiguration vc = ViewConfiguration.get(TabsListLayout.this.getContext());
|
|
|
|
swipeThreshold = vc.getScaledTouchSlop();
|
|
minFlingVelocity = (int) (getContext().getResources().getDisplayMetrics().density * MIN_VELOCITY);
|
|
maxFlingVelocity = vc.getScaledMaximumFlingVelocity();
|
|
}
|
|
|
|
public void setEnabled(boolean enabled) {
|
|
this.enabled = enabled;
|
|
}
|
|
|
|
public TwoWayView.OnScrollListener makeScrollListener() {
|
|
return new TwoWayView.OnScrollListener() {
|
|
|
|
@Override
|
|
public void onScrollStateChanged(TwoWayView twoWayView, int scrollState) {
|
|
setEnabled(scrollState != TwoWayView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
|
|
}
|
|
|
|
@Override
|
|
public void onScroll(TwoWayView twoWayView, int i, int i1, int i2) {
|
|
}
|
|
};
|
|
}
|
|
|
|
@Override
|
|
public boolean onTouch(View view, MotionEvent e) {
|
|
if (!enabled) {
|
|
return false;
|
|
}
|
|
|
|
if (listWidth < 2 || listHeight < 2) {
|
|
listWidth = TabsListLayout.this.getWidth();
|
|
listHeight = TabsListLayout.this.getHeight();
|
|
}
|
|
|
|
switch (e.getActionMasked()) {
|
|
case MotionEvent.ACTION_DOWN: {
|
|
// Check if we should set pressed state on the
|
|
// touched view after a standard delay.
|
|
triggerCheckForTap();
|
|
|
|
final float x = e.getRawX();
|
|
final float y = e.getRawY();
|
|
|
|
// Find out which view is being touched
|
|
swipeView = findViewAt(x, y);
|
|
|
|
if (swipeView != null) {
|
|
swipeStartX = e.getRawX();
|
|
swipeStartY = e.getRawY();
|
|
velocityTracker = VelocityTracker.obtain();
|
|
velocityTracker.addMovement(e);
|
|
}
|
|
|
|
view.onTouchEvent(e);
|
|
return true;
|
|
}
|
|
case MotionEvent.ACTION_UP: {
|
|
if (swipeView == null) {
|
|
break;
|
|
}
|
|
|
|
cancelCheckForTap();
|
|
swipeView.setPressed(false);
|
|
|
|
if (!swiping) {
|
|
TabsLayoutItemView item = (TabsLayoutItemView) swipeView;
|
|
Tabs.getInstance().selectTab(item.getTabId());
|
|
autoHidePanel();
|
|
velocityTracker.recycle();
|
|
velocityTracker = null;
|
|
break;
|
|
}
|
|
|
|
velocityTracker.addMovement(e);
|
|
velocityTracker.computeCurrentVelocity(1000, maxFlingVelocity);
|
|
|
|
float velocityX = Math.abs(velocityTracker.getXVelocity());
|
|
float velocityY = Math.abs(velocityTracker.getYVelocity());
|
|
boolean dismiss = false;
|
|
boolean dismissDirection = false;
|
|
int dismissTranslation;
|
|
|
|
if (isVertical()) {
|
|
float deltaX = ViewHelper.getTranslationX(swipeView);
|
|
|
|
if (Math.abs(deltaX) > listWidth / 2) {
|
|
dismiss = true;
|
|
dismissDirection = (deltaX > 0);
|
|
} else if (minFlingVelocity <= velocityX && velocityX <= maxFlingVelocity
|
|
&& velocityY < velocityX) {
|
|
|
|
dismiss = swiping && (deltaX * velocityTracker.getXVelocity() > 0);
|
|
dismissDirection = (velocityTracker.getXVelocity() > 0);
|
|
}
|
|
|
|
dismissTranslation = (dismissDirection ? listWidth : -listWidth);
|
|
} else {
|
|
float deltaY = ViewHelper.getTranslationY(swipeView);
|
|
|
|
if (Math.abs(deltaY) > listHeight / 2) {
|
|
dismiss = true;
|
|
dismissDirection = (deltaY > 0);
|
|
} else if (minFlingVelocity <= velocityY && velocityY <= maxFlingVelocity
|
|
&& velocityX < velocityY) {
|
|
|
|
dismiss = swiping && (deltaY * velocityTracker.getYVelocity() > 0);
|
|
dismissDirection = (velocityTracker.getYVelocity() > 0);
|
|
}
|
|
|
|
dismissTranslation = (dismissDirection ? listHeight : -listHeight);
|
|
}
|
|
|
|
if (dismiss) {
|
|
animateClose(swipeView, dismissTranslation);
|
|
} else {
|
|
animateCancel(swipeView);
|
|
}
|
|
|
|
velocityTracker.recycle();
|
|
velocityTracker = null;
|
|
swipeView = null;
|
|
swipeStartX = 0;
|
|
swipeStartY = 0;
|
|
swiping = false;
|
|
break;
|
|
}
|
|
|
|
case MotionEvent.ACTION_MOVE: {
|
|
if (swipeView == null || velocityTracker == null) {
|
|
break;
|
|
}
|
|
|
|
velocityTracker.addMovement(e);
|
|
|
|
final boolean isVertical = isVertical();
|
|
float deltaX = e.getRawX() - swipeStartX;
|
|
float deltaY = e.getRawY() - swipeStartY;
|
|
float delta = (isVertical ? deltaX : deltaY);
|
|
boolean isScrollingX = Math.abs(deltaX) > swipeThreshold;
|
|
boolean isScrollingY = Math.abs(deltaY) > swipeThreshold;
|
|
boolean isSwipingToClose = (isVertical ? isScrollingX : isScrollingY);
|
|
|
|
// If we're actually swiping, make sure we don't
|
|
// set pressed state on the swiped view.
|
|
if (isScrollingX || isScrollingY) {
|
|
cancelCheckForTap();
|
|
}
|
|
|
|
if (isSwipingToClose) {
|
|
swiping = true;
|
|
TabsListLayout.this.requestDisallowInterceptTouchEvent(true);
|
|
((TabsLayoutItemView) swipeView).setCloseVisible(false);
|
|
|
|
// Stops listview from highlighting the touched item
|
|
// in the list when swiping.
|
|
MotionEvent cancelEvent = MotionEvent.obtain(e);
|
|
cancelEvent.setAction(MotionEvent.ACTION_CANCEL |
|
|
(e.getActionIndex() << MotionEvent.ACTION_POINTER_INDEX_SHIFT));
|
|
|
|
TabsListLayout.this.onTouchEvent(cancelEvent);
|
|
cancelEvent.recycle();
|
|
}
|
|
if (swiping) {
|
|
if (isVertical) {
|
|
ViewHelper.setTranslationX(swipeView, delta);
|
|
} else {
|
|
ViewHelper.setTranslationY(swipeView, delta);
|
|
}
|
|
|
|
ViewHelper.setAlpha(swipeView, Math.max(0.1f, Math.min(1f,
|
|
1f - 2f * Math.abs(delta) / (isVertical ? listWidth : listHeight))));
|
|
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private View findViewAt(float rawX, float rawY) {
|
|
Rect rect = new Rect();
|
|
|
|
int[] listViewCoords = new int[2];
|
|
TabsListLayout.this.getLocationOnScreen(listViewCoords);
|
|
|
|
int x = (int) rawX - listViewCoords[0];
|
|
int y = (int) rawY - listViewCoords[1];
|
|
|
|
for (int i = 0; i < TabsListLayout.this.getChildCount(); i++) {
|
|
View child = TabsListLayout.this.getChildAt(i);
|
|
child.getHitRect(rect);
|
|
|
|
if (rect.contains(x, y)) {
|
|
return child;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private void triggerCheckForTap() {
|
|
if (pendingCheckForTap == null) {
|
|
pendingCheckForTap = new CheckForTap();
|
|
}
|
|
TabsListLayout.this.postDelayed(pendingCheckForTap, ViewConfiguration.getTapTimeout());
|
|
}
|
|
|
|
private void cancelCheckForTap() {
|
|
if (pendingCheckForTap == null) {
|
|
return;
|
|
}
|
|
TabsListLayout.this.removeCallbacks(pendingCheckForTap);
|
|
}
|
|
|
|
private class CheckForTap implements Runnable {
|
|
@Override
|
|
public void run() {
|
|
if (!swiping && swipeView != null && enabled) {
|
|
swipeView.setPressed(true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|