mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1193745 - Implement the tablet tabs tray grid view on mobile r=mcomella
This commit is contained in:
parent
6f1ecf9555
commit
85bb863813
@ -449,8 +449,6 @@ public class BrowserApp extends GeckoApp
|
||||
final View view;
|
||||
if (BrowserToolbar.class.getName().equals(name)) {
|
||||
view = BrowserToolbar.create(context, attrs);
|
||||
} else if (TabsPanel.TabsLayout.class.getName().equals(name)) {
|
||||
view = TabsPanel.createTabsLayout(context, attrs);
|
||||
} else {
|
||||
view = super.onCreateView(name, context, attrs);
|
||||
}
|
||||
|
@ -470,7 +470,6 @@ gbjar.sources += [
|
||||
'tabs/TabsGridLayout.java',
|
||||
'tabs/TabsLayoutAdapter.java',
|
||||
'tabs/TabsLayoutItemView.java',
|
||||
'tabs/TabsListLayout.java',
|
||||
'tabs/TabsPanel.java',
|
||||
'tabs/TabsPanelThumbnailView.java',
|
||||
'Telemetry.java',
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
<!-- Note: for an unknown reason, scrolling in the TabsLayout
|
||||
does not work unless it is laid out after the empty view. -->
|
||||
<view class="org.mozilla.gecko.tabs.TabsPanel$TabsLayout"
|
||||
<org.mozilla.gecko.tabs.TabsGridLayout
|
||||
android:id="@+id/private_tabs_layout"
|
||||
style="@style/TabsLayout"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -16,9 +16,9 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:duplicateParentState="true"
|
||||
android:paddingLeft="@dimen/tablet_tab_highlight_stroke_width"
|
||||
android:paddingRight="@dimen/tablet_tab_highlight_stroke_width"
|
||||
android:paddingBottom="@dimen/tablet_tab_highlight_stroke_width">
|
||||
android:paddingLeft="@dimen/tab_highlight_stroke_width"
|
||||
android:paddingRight="@dimen/tab_highlight_stroke_width"
|
||||
android:paddingBottom="@dimen/tab_highlight_stroke_width">
|
||||
|
||||
<org.mozilla.gecko.widget.FadedSingleColorTextView
|
||||
android:id="@+id/title"
|
||||
@ -62,13 +62,13 @@
|
||||
android:id="@+id/wrapper"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/tablet_tab_highlight_stroke_width"
|
||||
android:padding="@dimen/tab_highlight_stroke_width"
|
||||
android:background="@drawable/tab_thumbnail"
|
||||
android:duplicateParentState="true">
|
||||
|
||||
<org.mozilla.gecko.tabs.TabsPanelThumbnailView android:id="@+id/thumbnail"
|
||||
android:layout_width="@dimen/tablet_tab_thumbnail_width"
|
||||
android:layout_height="@dimen/tablet_tab_thumbnail_height"
|
||||
android:layout_width="@dimen/tab_thumbnail_width"
|
||||
android:layout_height="@dimen/tab_thumbnail_height"
|
||||
/>
|
||||
|
||||
</org.mozilla.gecko.widget.TabThumbnailWrapper>
|
||||
|
@ -66,7 +66,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<view class="org.mozilla.gecko.tabs.TabsPanel$TabsLayout"
|
||||
<org.mozilla.gecko.tabs.TabsGridLayout
|
||||
android:id="@+id/normal_tabs"
|
||||
style="@style/TabsLayout"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -9,5 +9,5 @@
|
||||
<dimen name="home_remote_tabs_top_padding">16dp</dimen>
|
||||
<dimen name="page_group_height">64dp</dimen>
|
||||
|
||||
<dimen name="tablet_tab_panel_grid_padding">48dp</dimen>
|
||||
<dimen name="tab_panel_grid_padding">48dp</dimen>
|
||||
</resources>
|
||||
|
10
mobile/android/base/resources/values-sw240dp/dimens.xml
Normal file
10
mobile/android/base/resources/values-sw240dp/dimens.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<resources>
|
||||
<dimen name="tab_panel_column_width">143dip</dimen>
|
||||
<dimen name="tab_thumbnail_height">100dip</dimen>
|
||||
<dimen name="tab_thumbnail_width">135dip</dimen>
|
||||
</resources>
|
10
mobile/android/base/resources/values-sw400dp/dimens.xml
Normal file
10
mobile/android/base/resources/values-sw400dp/dimens.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<resources>
|
||||
<dimen name="tab_panel_column_width">174dip</dimen>
|
||||
<dimen name="tab_thumbnail_height">120dip</dimen>
|
||||
<dimen name="tab_thumbnail_width">168dip</dimen>
|
||||
</resources>
|
@ -5,6 +5,6 @@
|
||||
|
||||
<resources>
|
||||
|
||||
<dimen name="tablet_tab_panel_grid_padding">64dp</dimen>
|
||||
<dimen name="tab_panel_grid_padding">64dp</dimen>
|
||||
|
||||
</resources>
|
||||
|
@ -6,6 +6,6 @@
|
||||
<resources>
|
||||
|
||||
<dimen name="panel_grid_view_column_width">250dp</dimen>
|
||||
<dimen name="tablet_tab_panel_grid_padding">48dp</dimen>
|
||||
<dimen name="tab_panel_grid_padding">48dp</dimen>
|
||||
|
||||
</resources>
|
||||
|
@ -130,8 +130,6 @@
|
||||
<dimen name="prompt_service_inputs_padding">16dp</dimen>
|
||||
<dimen name="prompt_service_left_right_text_with_icon_padding">10dp</dimen>
|
||||
<dimen name="prompt_service_top_bottom_text_with_icon_padding">8dp</dimen>
|
||||
<dimen name="tab_thumbnail_height">90dp</dimen>
|
||||
<dimen name="tab_thumbnail_width">160dp</dimen>
|
||||
<dimen name="tabs_panel_indicator_width">60dp</dimen>
|
||||
<dimen name="tabs_panel_button_width">48dp</dimen>
|
||||
<dimen name="tabs_strip_height">48dp</dimen>
|
||||
@ -145,14 +143,14 @@
|
||||
<dimen name="validation_message_height">50dp</dimen>
|
||||
<dimen name="validation_message_margin_top">6dp</dimen>
|
||||
|
||||
<dimen name="tablet_tab_thumbnail_width">168dp</dimen>
|
||||
<dimen name="tablet_tab_thumbnail_height">140dp</dimen>
|
||||
<dimen name="tablet_tab_panel_column_width">178dp</dimen>
|
||||
<dimen name="tablet_tab_panel_grid_padding">19dp</dimen>
|
||||
<dimen name="tablet_tab_panel_grid_vspacing">21dp</dimen>
|
||||
<dimen name="tablet_tab_panel_grid_padding_top">24dp</dimen>
|
||||
<dimen name="tab_thumbnail_width">121dp</dimen>
|
||||
<dimen name="tab_thumbnail_height">90dp</dimen>
|
||||
<dimen name="tab_panel_column_width">129dp</dimen>
|
||||
<dimen name="tab_panel_grid_padding">20dp</dimen>
|
||||
<dimen name="tab_panel_grid_vspacing">20dp</dimen>
|
||||
<dimen name="tab_panel_grid_padding_top">19dp</dimen>
|
||||
|
||||
<dimen name="tablet_tab_highlight_stroke_width">5dp</dimen>
|
||||
<dimen name="tab_highlight_stroke_width">4dp</dimen>
|
||||
|
||||
<!-- PageActionButtons dimensions -->
|
||||
<dimen name="page_action_button_width">32dp</dimen>
|
||||
|
@ -205,9 +205,9 @@
|
||||
<item name="android:scrollbarStyle">outsideOverlay</item>
|
||||
<item name="android:gravity">center</item>
|
||||
<item name="android:numColumns">auto_fit</item>
|
||||
<item name="android:columnWidth">@dimen/tablet_tab_panel_column_width</item>
|
||||
<item name="android:columnWidth">@dimen/tab_panel_column_width</item>
|
||||
<item name="android:horizontalSpacing">2dp</item>
|
||||
<item name="android:verticalSpacing">@dimen/tablet_tab_panel_grid_vspacing</item>
|
||||
<item name="android:verticalSpacing">@dimen/tab_panel_grid_vspacing</item>
|
||||
<item name="android:drawSelectorOnTop">true</item>
|
||||
<item name="android:clipToPadding">false</item>
|
||||
</style>
|
||||
|
@ -13,9 +13,7 @@ import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
/**
|
||||
* A container that wraps the private tabs {@link android.widget.AdapterView} and empty
|
||||
@ -26,7 +24,7 @@ import android.widget.LinearLayout;
|
||||
class PrivateTabsPanel extends FrameLayout implements CloseAllPanelView {
|
||||
private final TabsLayout tabsLayout;
|
||||
|
||||
public PrivateTabsPanel(Context context, AttributeSet attrs) {
|
||||
public PrivateTabsPanel(final Context context, final AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
LayoutInflater.from(context).inflate(R.layout.private_tabs_panel, this);
|
||||
@ -37,7 +35,7 @@ class PrivateTabsPanel extends FrameLayout implements CloseAllPanelView {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTabsPanel(TabsPanel panel) {
|
||||
public void setTabsPanel(final TabsPanel panel) {
|
||||
tabsLayout.setTabsPanel(panel);
|
||||
}
|
||||
|
||||
@ -53,11 +51,6 @@ class PrivateTabsPanel extends FrameLayout implements CloseAllPanelView {
|
||||
tabsLayout.hide();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldExpand() {
|
||||
return tabsLayout.shouldExpand();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeAll() {
|
||||
tabsLayout.closeAll();
|
||||
|
@ -5,13 +5,13 @@
|
||||
|
||||
package org.mozilla.gecko.tabs;
|
||||
|
||||
import org.mozilla.gecko.AppConstants;
|
||||
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.ViewHelper;
|
||||
import org.mozilla.gecko.tabs.TabsPanel.TabsLayout;
|
||||
import org.mozilla.gecko.widget.themed.ThemedRelativeLayout;
|
||||
|
||||
@ -20,6 +20,7 @@ import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Build;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.SparseArray;
|
||||
import android.view.MotionEvent;
|
||||
@ -38,13 +39,13 @@ import com.nineoldandroids.animation.AnimatorSet;
|
||||
import com.nineoldandroids.animation.ObjectAnimator;
|
||||
import com.nineoldandroids.animation.PropertyValuesHolder;
|
||||
import com.nineoldandroids.animation.ValueAnimator;
|
||||
import com.nineoldandroids.view.ViewHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A tabs layout implementation for the tablet redesign (bug 1014156).
|
||||
* Expected to replace TabsListLayout once complete.
|
||||
* A tabs layout implementation for the tablet redesign (bug 1014156) and later ported to mobile (bug 1193745).
|
||||
*/
|
||||
|
||||
class TabsGridLayout extends GridView
|
||||
@ -56,24 +57,22 @@ class TabsGridLayout extends GridView
|
||||
private static final int ANIM_TIME_MS = 200;
|
||||
private static final DecelerateInterpolator ANIM_INTERPOLATOR = new DecelerateInterpolator();
|
||||
|
||||
private final Context mContext;
|
||||
private final SparseArray<PointF> mTabLocations = new SparseArray<PointF>();
|
||||
private final boolean mIsPrivate;
|
||||
private final TabsLayoutAdapter mTabsAdapter;
|
||||
private final int mColumnWidth;
|
||||
private TabsPanel mTabsPanel;
|
||||
private final SparseArray<PointF> tabLocations = new SparseArray<PointF>();
|
||||
private final boolean isPrivate;
|
||||
private final TabsLayoutAdapter tabsAdapter;
|
||||
private final int columnWidth;
|
||||
private TabsPanel tabsPanel;
|
||||
private int lastSelectedTabId;
|
||||
|
||||
public TabsGridLayout(Context context, AttributeSet attrs) {
|
||||
public TabsGridLayout(final Context context, final AttributeSet attrs) {
|
||||
super(context, attrs, R.attr.tabGridLayoutViewStyle);
|
||||
mContext = context;
|
||||
|
||||
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TabsLayout);
|
||||
mIsPrivate = (a.getInt(R.styleable.TabsLayout_tabs, 0x0) == 1);
|
||||
isPrivate = (a.getInt(R.styleable.TabsLayout_tabs, 0x0) == 1);
|
||||
a.recycle();
|
||||
|
||||
mTabsAdapter = new TabsGridLayoutAdapter(mContext);
|
||||
setAdapter(mTabsAdapter);
|
||||
tabsAdapter = new TabsGridLayoutAdapter(context);
|
||||
setAdapter(tabsAdapter);
|
||||
|
||||
setRecyclerListener(new RecyclerListener() {
|
||||
@Override
|
||||
@ -88,10 +87,10 @@ class TabsGridLayout extends GridView
|
||||
setClipToPadding(false);
|
||||
|
||||
final Resources resources = getResources();
|
||||
mColumnWidth = resources.getDimensionPixelSize(R.dimen.tablet_tab_panel_column_width);
|
||||
columnWidth = resources.getDimensionPixelSize(R.dimen.tab_panel_column_width);
|
||||
|
||||
final int padding = resources.getDimensionPixelSize(R.dimen.tablet_tab_panel_grid_padding);
|
||||
final int paddingTop = resources.getDimensionPixelSize(R.dimen.tablet_tab_panel_grid_padding_top);
|
||||
final int padding = resources.getDimensionPixelSize(R.dimen.tab_panel_grid_padding);
|
||||
final int paddingTop = resources.getDimensionPixelSize(R.dimen.tab_panel_grid_padding_top);
|
||||
|
||||
// Lets set double the top padding on the bottom so that the last row shows up properly!
|
||||
// Your demise, GridView, cannot come fast enough.
|
||||
@ -114,13 +113,13 @@ class TabsGridLayout extends GridView
|
||||
}
|
||||
|
||||
private void populateTabLocations(final Tab removedTab) {
|
||||
mTabLocations.clear();
|
||||
tabLocations.clear();
|
||||
|
||||
final int firstPosition = getFirstVisiblePosition();
|
||||
final int lastPosition = getLastVisiblePosition();
|
||||
final int numberOfColumns = getNumColumns();
|
||||
final int childCount = getChildCount();
|
||||
final int removedPosition = mTabsAdapter.getPositionForTab(removedTab);
|
||||
final int removedPosition = tabsAdapter.getPositionForTab(removedTab);
|
||||
|
||||
for (int x = 1, i = (removedPosition - firstPosition) + 1; i < childCount; i++, x++) {
|
||||
final View child = getChildAt(i);
|
||||
@ -128,7 +127,8 @@ class TabsGridLayout extends GridView
|
||||
// Reset the transformations here in case the user is swiping tabs away fast and they swipe a tab
|
||||
// before the last animation has finished (bug 1179195).
|
||||
resetTransforms(child);
|
||||
mTabLocations.append(x, new PointF(child.getX(), child.getY()));
|
||||
|
||||
tabLocations.append(x, new PointF(child.getX(), child.getY()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -142,7 +142,7 @@ class TabsGridLayout extends GridView
|
||||
|
||||
final int removedHeight = getChildAt(0).getMeasuredHeight();
|
||||
final int verticalSpacing =
|
||||
getResources().getDimensionPixelOffset(R.dimen.tablet_tab_panel_grid_vspacing);
|
||||
getResources().getDimensionPixelOffset(R.dimen.tab_panel_grid_vspacing);
|
||||
|
||||
ValueAnimator paddingAnimator = ValueAnimator.ofInt(getPaddingBottom() + removedHeight + verticalSpacing, getPaddingBottom());
|
||||
paddingAnimator.setDuration(ANIM_TIME_MS * 2);
|
||||
@ -160,7 +160,7 @@ class TabsGridLayout extends GridView
|
||||
|
||||
@Override
|
||||
public void setTabsPanel(TabsPanel panel) {
|
||||
mTabsPanel = panel;
|
||||
tabsPanel = panel;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -172,7 +172,7 @@ class TabsGridLayout extends GridView
|
||||
|
||||
Tab currentlySelectedTab = Tabs.getInstance().getSelectedTab();
|
||||
if (lastSelectedTabId != currentlySelectedTab.getId()) {
|
||||
smoothScrollToPosition(mTabsAdapter.getPositionForTab(currentlySelectedTab));
|
||||
smoothScrollToPosition(tabsAdapter.getPositionForTab(currentlySelectedTab));
|
||||
}
|
||||
}
|
||||
|
||||
@ -182,16 +182,11 @@ class TabsGridLayout extends GridView
|
||||
setVisibility(View.GONE);
|
||||
Tabs.unregisterOnTabsChangedListener(this);
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Tab:Screenshot:Cancel", ""));
|
||||
mTabsAdapter.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldExpand() {
|
||||
return true;
|
||||
tabsAdapter.clear();
|
||||
}
|
||||
|
||||
private void autoHidePanel() {
|
||||
mTabsPanel.autoHidePanel();
|
||||
tabsPanel.autoHidePanel();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -203,15 +198,17 @@ class TabsGridLayout extends GridView
|
||||
break;
|
||||
|
||||
case CLOSED:
|
||||
if (mTabsAdapter.getCount() > 0) {
|
||||
|
||||
// This is limited to >= ICS as animations on GB devices are generally pants
|
||||
if (Build.VERSION.SDK_INT >= 11 && tabsAdapter.getCount() > 0) {
|
||||
animateRemoveTab(tab);
|
||||
}
|
||||
|
||||
final Tabs tabsInstance = Tabs.getInstance();
|
||||
|
||||
if (mTabsAdapter.removeTab(tab)) {
|
||||
if (tab.isPrivate() == mIsPrivate && mTabsAdapter.getCount() > 0) {
|
||||
int selected = mTabsAdapter.getPositionForTab(tabsInstance.getSelectedTab());
|
||||
if (tabsAdapter.removeTab(tab)) {
|
||||
if (tab.isPrivate() == isPrivate && tabsAdapter.getCount() > 0) {
|
||||
int selected = tabsAdapter.getPositionForTab(tabsInstance.getSelectedTab());
|
||||
updateSelectedStyle(selected);
|
||||
}
|
||||
if (!tab.isPrivate()) {
|
||||
@ -240,7 +237,7 @@ class TabsGridLayout extends GridView
|
||||
case TITLE:
|
||||
case RECORDING_CHANGE:
|
||||
case AUDIO_PLAYING_CHANGE:
|
||||
View view = getChildAt(mTabsAdapter.getPositionForTab(tab) - getFirstVisiblePosition());
|
||||
View view = getChildAt(tabsAdapter.getPositionForTab(tab) - getFirstVisiblePosition());
|
||||
if (view == null)
|
||||
return;
|
||||
|
||||
@ -251,7 +248,7 @@ class TabsGridLayout extends GridView
|
||||
|
||||
// Updates the selected position in the list so that it will be scrolled to the right place.
|
||||
private void updateSelectedPosition() {
|
||||
int selected = mTabsAdapter.getPositionForTab(Tabs.getInstance().getSelectedTab());
|
||||
int selected = tabsAdapter.getPositionForTab(Tabs.getInstance().getSelectedTab());
|
||||
updateSelectedStyle(selected);
|
||||
|
||||
if (selected != -1) {
|
||||
@ -265,8 +262,13 @@ class TabsGridLayout extends GridView
|
||||
* @param selected position of the selected tab
|
||||
*/
|
||||
private void updateSelectedStyle(int selected) {
|
||||
for (int i = 0; i < mTabsAdapter.getCount(); i++) {
|
||||
setItemChecked(i, (i == selected));
|
||||
for (int i = 0; i < tabsAdapter.getCount(); i++) {
|
||||
// setItemChecked doesn't exist until API 11, despite what the API docs say!
|
||||
if (AppConstants.Versions.feature11Plus) {
|
||||
setItemChecked(i, (i == selected));
|
||||
} else {
|
||||
setSelection(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -277,11 +279,11 @@ class TabsGridLayout extends GridView
|
||||
|
||||
Iterable<Tab> allTabs = Tabs.getInstance().getTabsInOrder();
|
||||
for (Tab tab : allTabs) {
|
||||
if (tab.isPrivate() == mIsPrivate)
|
||||
if (tab.isPrivate() == isPrivate)
|
||||
tabData.add(tab);
|
||||
}
|
||||
|
||||
mTabsAdapter.setTabs(tabData);
|
||||
tabsAdapter.setTabs(tabData);
|
||||
updateSelectedPosition();
|
||||
}
|
||||
|
||||
@ -306,14 +308,14 @@ class TabsGridLayout extends GridView
|
||||
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 (!mIsPrivate || tab.isPrivate()) {
|
||||
if (!isPrivate || tab.isPrivate()) {
|
||||
Tabs.getInstance().closeTab(tab, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private View getViewForTab(Tab tab) {
|
||||
final int position = mTabsAdapter.getPositionForTab(tab);
|
||||
final int position = tabsAdapter.getPositionForTab(tab);
|
||||
return getChildAt(position - getFirstVisiblePosition());
|
||||
}
|
||||
|
||||
@ -325,7 +327,7 @@ class TabsGridLayout extends GridView
|
||||
}
|
||||
|
||||
private void animateRemoveTab(final Tab removedTab) {
|
||||
final int removedPosition = mTabsAdapter.getPositionForTab(removedTab);
|
||||
final int removedPosition = tabsAdapter.getPositionForTab(removedTab);
|
||||
|
||||
final View removedView = getViewForTab(removedTab);
|
||||
|
||||
@ -358,12 +360,12 @@ class TabsGridLayout extends GridView
|
||||
|
||||
if (i % numberOfColumns == numberOfColumns - 1) {
|
||||
// Animate X & Y
|
||||
translateX = PropertyValuesHolder.ofFloat("translationX", -(mColumnWidth * numberOfColumns), 0);
|
||||
translateX = PropertyValuesHolder.ofFloat("translationX", -(columnWidth * numberOfColumns), 0);
|
||||
translateY = PropertyValuesHolder.ofFloat("translationY", removedHeight, 0);
|
||||
animator = ObjectAnimator.ofPropertyValuesHolder(child, translateX, translateY);
|
||||
} else {
|
||||
// Just animate X
|
||||
translateX = PropertyValuesHolder.ofFloat("translationX", mColumnWidth, 0);
|
||||
translateX = PropertyValuesHolder.ofFloat("translationX", columnWidth, 0);
|
||||
animator = ObjectAnimator.ofPropertyValuesHolder(child, translateX);
|
||||
}
|
||||
animator.setStartDelay(x * ANIM_DELAY_MULTIPLE_MS);
|
||||
@ -382,7 +384,7 @@ class TabsGridLayout extends GridView
|
||||
for (int x = 1, i = (removedPosition - firstPosition) + 1; i < childCount; i++, x++) {
|
||||
final View child = getChildAt(i);
|
||||
|
||||
final PointF targetLocation = mTabLocations.get(x + 1);
|
||||
final PointF targetLocation = tabLocations.get(x + 1);
|
||||
if (targetLocation == null) {
|
||||
continue;
|
||||
}
|
||||
@ -396,6 +398,7 @@ class TabsGridLayout extends GridView
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private void animateCancel(final View view) {
|
||||
PropertyAnimator animator = new PropertyAnimator(ANIM_TIME_MS);
|
||||
animator.attach(view, PropertyAnimator.Property.ALPHA, 1);
|
||||
@ -436,7 +439,7 @@ class TabsGridLayout extends GridView
|
||||
final TabsLayoutItemView item = super.newView(position, parent);
|
||||
|
||||
item.setCloseOnClickListener(mCloseClickListener);
|
||||
((ThemedRelativeLayout) item.findViewById(R.id.wrapper)).setPrivateMode(mIsPrivate);
|
||||
((ThemedRelativeLayout) item.findViewById(R.id.wrapper)).setPrivateMode(isPrivate);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
@ -1,660 +0,0 @@
|
||||
/* -*- 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 java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.mozilla.gecko.animation.PropertyAnimator.Property;
|
||||
import org.mozilla.gecko.animation.PropertyAnimator;
|
||||
import org.mozilla.gecko.animation.ViewHelper;
|
||||
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.TabsPanel.TabsLayout;
|
||||
import org.mozilla.gecko.Tabs;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
import org.mozilla.gecko.widget.themed.ThemedRelativeLayout;
|
||||
import org.mozilla.gecko.widget.TwoWayView;
|
||||
|
||||
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.ViewGroup;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.widget.Button;
|
||||
|
||||
class TabsListLayout extends TwoWayView
|
||||
implements TabsLayout,
|
||||
Tabs.OnTabsChangedListener {
|
||||
private static final String LOGTAG = "Gecko" + TabsListLayout.class.getSimpleName();
|
||||
|
||||
private final Context mContext;
|
||||
private TabsPanel mTabsPanel;
|
||||
|
||||
final private boolean mIsPrivate;
|
||||
|
||||
private final TabsLayoutAdapter mTabsAdapter;
|
||||
|
||||
private final List<View> mPendingClosedTabs;
|
||||
private int mCloseAnimationCount;
|
||||
private int mCloseAllAnimationCount;
|
||||
|
||||
private final TabSwipeGestureListener mSwipeListener;
|
||||
|
||||
// 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 int mOriginalSize;
|
||||
|
||||
public TabsListLayout(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mContext = context;
|
||||
|
||||
mPendingClosedTabs = new ArrayList<View>();
|
||||
|
||||
setItemsCanFocus(true);
|
||||
|
||||
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TabsLayout);
|
||||
mIsPrivate = (a.getInt(R.styleable.TabsLayout_tabs, 0x0) == 1);
|
||||
a.recycle();
|
||||
|
||||
mTabsAdapter = new TabsListLayoutAdapter(mContext);
|
||||
setAdapter(mTabsAdapter);
|
||||
|
||||
mSwipeListener = new TabSwipeGestureListener();
|
||||
setOnTouchListener(mSwipeListener);
|
||||
setOnScrollListener(mSwipeListener.makeScrollListener());
|
||||
|
||||
setRecyclerListener(new RecyclerListener() {
|
||||
@Override
|
||||
public void onMovedToScrapHeap(View view) {
|
||||
TabsLayoutItemView item = (TabsLayoutItemView) view;
|
||||
item.setThumbnail(null);
|
||||
item.setCloseVisible(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private class TabsListLayoutAdapter extends TabsLayoutAdapter {
|
||||
private final Button.OnClickListener mCloseOnClickListener;
|
||||
public TabsListLayoutAdapter (Context context) {
|
||||
super(context, R.layout.tabs_layout_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(mIsPrivate);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTabsPanel(TabsPanel panel) {
|
||||
mTabsPanel = 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",""));
|
||||
mTabsAdapter.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldExpand() {
|
||||
return isVertical();
|
||||
}
|
||||
|
||||
private void autoHidePanel() {
|
||||
mTabsPanel.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() == mIsPrivate && mTabsAdapter.getCount() > 0) {
|
||||
if (mTabsAdapter.removeTab(tab)) {
|
||||
int selected = mTabsAdapter.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(mTabsAdapter.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 = mTabsAdapter.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 < mTabsAdapter.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() == mIsPrivate)
|
||||
tabData.add(tab);
|
||||
}
|
||||
|
||||
mTabsAdapter.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 (mOriginalSize != 0) {
|
||||
if (isVertical()) {
|
||||
ViewHelper.setHeight(view, mOriginalSize);
|
||||
} else {
|
||||
ViewHelper.setWidth(view, mOriginalSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
mCloseAllAnimationCount++;
|
||||
|
||||
animator.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() {
|
||||
@Override
|
||||
public void onPropertyAnimationStart() { }
|
||||
|
||||
@Override
|
||||
public void onPropertyAnimationEnd() {
|
||||
mCloseAllAnimationCount--;
|
||||
if (mCloseAllAnimationCount > 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 (!mIsPrivate || 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);
|
||||
|
||||
mCloseAnimationCount++;
|
||||
mPendingClosedTabs.add(view);
|
||||
|
||||
animator.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() {
|
||||
@Override
|
||||
public void onPropertyAnimationStart() { }
|
||||
@Override
|
||||
public void onPropertyAnimationEnd() {
|
||||
mCloseAnimationCount--;
|
||||
if (mCloseAnimationCount > 0)
|
||||
return;
|
||||
|
||||
for (View pendingView : mPendingClosedTabs) {
|
||||
animateFinishClose(pendingView);
|
||||
}
|
||||
|
||||
mPendingClosedTabs.clear();
|
||||
}
|
||||
});
|
||||
|
||||
if (mTabsAdapter.getCount() == 1)
|
||||
autoHidePanel();
|
||||
|
||||
animator.start();
|
||||
}
|
||||
|
||||
private void animateFinishClose(final View view) {
|
||||
PropertyAnimator animator = new PropertyAnimator(ANIMATION_DURATION);
|
||||
|
||||
final boolean isVertical = isVertical();
|
||||
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 (mOriginalSize == 0) {
|
||||
mOriginalSize = (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 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 mSwipeThreshold;
|
||||
private final int mMinFlingVelocity;
|
||||
|
||||
private final int mMaxFlingVelocity;
|
||||
private VelocityTracker mVelocityTracker;
|
||||
|
||||
private int mListWidth = 1;
|
||||
private int mListHeight = 1;
|
||||
|
||||
private View mSwipeView;
|
||||
private Runnable mPendingCheckForTap;
|
||||
|
||||
private float mSwipeStartX;
|
||||
private float mSwipeStartY;
|
||||
private boolean mSwiping;
|
||||
private boolean mEnabled;
|
||||
|
||||
public TabSwipeGestureListener() {
|
||||
mEnabled = true;
|
||||
|
||||
ViewConfiguration vc = ViewConfiguration.get(TabsListLayout.this.getContext());
|
||||
mSwipeThreshold = vc.getScaledTouchSlop();
|
||||
mMinFlingVelocity = (int) (getContext().getResources().getDisplayMetrics().density * MIN_VELOCITY);
|
||||
mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
mEnabled = 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 (!mEnabled)
|
||||
return false;
|
||||
|
||||
if (mListWidth < 2 || mListHeight < 2) {
|
||||
mListWidth = TabsListLayout.this.getWidth();
|
||||
mListHeight = 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
|
||||
mSwipeView = findViewAt(x, y);
|
||||
|
||||
if (mSwipeView != null) {
|
||||
mSwipeStartX = e.getRawX();
|
||||
mSwipeStartY = e.getRawY();
|
||||
|
||||
mVelocityTracker = VelocityTracker.obtain();
|
||||
mVelocityTracker.addMovement(e);
|
||||
}
|
||||
|
||||
view.onTouchEvent(e);
|
||||
return true;
|
||||
}
|
||||
|
||||
case MotionEvent.ACTION_UP: {
|
||||
if (mSwipeView == null)
|
||||
break;
|
||||
|
||||
cancelCheckForTap();
|
||||
mSwipeView.setPressed(false);
|
||||
|
||||
if (!mSwiping) {
|
||||
TabsLayoutItemView item = (TabsLayoutItemView) mSwipeView;
|
||||
Tabs.getInstance().selectTab(item.getTabId());
|
||||
autoHidePanel();
|
||||
|
||||
mVelocityTracker.recycle();
|
||||
mVelocityTracker = null;
|
||||
break;
|
||||
}
|
||||
|
||||
mVelocityTracker.addMovement(e);
|
||||
mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity);
|
||||
|
||||
float velocityX = Math.abs(mVelocityTracker.getXVelocity());
|
||||
float velocityY = Math.abs(mVelocityTracker.getYVelocity());
|
||||
|
||||
boolean dismiss = false;
|
||||
boolean dismissDirection = false;
|
||||
int dismissTranslation = 0;
|
||||
|
||||
if (isVertical()) {
|
||||
float deltaX = ViewHelper.getTranslationX(mSwipeView);
|
||||
|
||||
if (Math.abs(deltaX) > mListWidth / 2) {
|
||||
dismiss = true;
|
||||
dismissDirection = (deltaX > 0);
|
||||
} else if (mMinFlingVelocity <= velocityX && velocityX <= mMaxFlingVelocity
|
||||
&& velocityY < velocityX) {
|
||||
dismiss = mSwiping && (deltaX * mVelocityTracker.getXVelocity() > 0);
|
||||
dismissDirection = (mVelocityTracker.getXVelocity() > 0);
|
||||
}
|
||||
|
||||
dismissTranslation = (dismissDirection ? mListWidth : -mListWidth);
|
||||
} else {
|
||||
float deltaY = ViewHelper.getTranslationY(mSwipeView);
|
||||
|
||||
if (Math.abs(deltaY) > mListHeight / 2) {
|
||||
dismiss = true;
|
||||
dismissDirection = (deltaY > 0);
|
||||
} else if (mMinFlingVelocity <= velocityY && velocityY <= mMaxFlingVelocity
|
||||
&& velocityX < velocityY) {
|
||||
dismiss = mSwiping && (deltaY * mVelocityTracker.getYVelocity() > 0);
|
||||
dismissDirection = (mVelocityTracker.getYVelocity() > 0);
|
||||
}
|
||||
|
||||
dismissTranslation = (dismissDirection ? mListHeight : -mListHeight);
|
||||
}
|
||||
|
||||
if (dismiss)
|
||||
animateClose(mSwipeView, dismissTranslation);
|
||||
else
|
||||
animateCancel(mSwipeView);
|
||||
|
||||
mVelocityTracker.recycle();
|
||||
mVelocityTracker = null;
|
||||
mSwipeView = null;
|
||||
|
||||
mSwipeStartX = 0;
|
||||
mSwipeStartY = 0;
|
||||
mSwiping = false;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case MotionEvent.ACTION_MOVE: {
|
||||
if (mSwipeView == null || mVelocityTracker == null)
|
||||
break;
|
||||
|
||||
mVelocityTracker.addMovement(e);
|
||||
|
||||
final boolean isVertical = isVertical();
|
||||
|
||||
float deltaX = e.getRawX() - mSwipeStartX;
|
||||
float deltaY = e.getRawY() - mSwipeStartY;
|
||||
float delta = (isVertical ? deltaX : deltaY);
|
||||
|
||||
boolean isScrollingX = Math.abs(deltaX) > mSwipeThreshold;
|
||||
boolean isScrollingY = Math.abs(deltaY) > mSwipeThreshold;
|
||||
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) {
|
||||
mSwiping = true;
|
||||
TabsListLayout.this.requestDisallowInterceptTouchEvent(true);
|
||||
|
||||
((TabsLayoutItemView) mSwipeView).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 (mSwiping) {
|
||||
if (isVertical)
|
||||
ViewHelper.setTranslationX(mSwipeView, delta);
|
||||
else
|
||||
ViewHelper.setTranslationY(mSwipeView, delta);
|
||||
|
||||
ViewHelper.setAlpha(mSwipeView, Math.max(0.1f, Math.min(1f,
|
||||
1f - 2f * Math.abs(delta) / (isVertical ? mListWidth : mListHeight))));
|
||||
|
||||
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 (mPendingCheckForTap == null)
|
||||
mPendingCheckForTap = new CheckForTap();
|
||||
|
||||
TabsListLayout.this.postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
|
||||
}
|
||||
|
||||
private void cancelCheckForTap() {
|
||||
if (mPendingCheckForTap == null)
|
||||
return;
|
||||
|
||||
TabsListLayout.this.removeCallbacks(mPendingCheckForTap);
|
||||
}
|
||||
|
||||
private class CheckForTap implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!mSwiping && mSwipeView != null && mEnabled)
|
||||
mSwipeView.setPressed(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -55,7 +55,6 @@ public class TabsPanel extends LinearLayout
|
||||
void setTabsPanel(TabsPanel panel);
|
||||
void show();
|
||||
void hide();
|
||||
boolean shouldExpand();
|
||||
}
|
||||
|
||||
public interface CloseAllPanelView extends PanelView {
|
||||
@ -66,14 +65,6 @@ public class TabsPanel extends LinearLayout
|
||||
void setEmptyView(View view);
|
||||
}
|
||||
|
||||
public static View createTabsLayout(final Context context, final AttributeSet attrs) {
|
||||
if (HardwareUtils.isTablet()) {
|
||||
return new TabsGridLayout(context, attrs);
|
||||
} else {
|
||||
return new TabsListLayout(context, attrs);
|
||||
}
|
||||
}
|
||||
|
||||
public interface TabsLayoutChangeListener {
|
||||
void onTabsLayoutChange(int width, int height);
|
||||
}
|
||||
@ -432,10 +423,6 @@ public class TabsPanel extends LinearLayout
|
||||
return mVisible;
|
||||
}
|
||||
|
||||
public Panel getCurrentPanel() {
|
||||
return mCurrentPanel;
|
||||
}
|
||||
|
||||
public void setHWLayerEnabled(boolean enabled) {
|
||||
if (Versions.preHC) {
|
||||
return;
|
||||
|
Loading…
Reference in New Issue
Block a user