Bug 739407: Perform animation while showing/hiding tabs. [r=mfinkle]

This commit is contained in:
Sriram Ramasubramanian 2012-06-07 21:48:29 -07:00
parent acc455e350
commit 873878486e
7 changed files with 175 additions and 18 deletions

View File

@ -64,7 +64,8 @@ import dalvik.system.*;
abstract public class GeckoApp
extends GeckoActivity
implements GeckoEventListener, SensorEventListener, LocationListener,
GeckoApplication.ApplicationLifecycleCallbacks {
GeckoApplication.ApplicationLifecycleCallbacks,
TabsPanel.TabsLayoutChangeListener {
private static final String LOGTAG = "GeckoApp";
public static enum StartupMode {
@ -112,6 +113,8 @@ abstract public class GeckoApp
private static AbsoluteLayout mPluginContainer;
private static FindInPageBar mFindInPageBar;
private PropertyAnimator mMainLayoutAnimator;
private FullScreenHolder mFullScreenPluginContainer;
private View mFullScreenPluginView;
@ -1020,6 +1023,28 @@ abstract public class GeckoApp
return mTabsPanel.isShown();
}
@Override
public void onTabsLayoutChange(int width, int height) {
if (mMainLayoutAnimator != null)
mMainLayoutAnimator.stop();
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) mMainLayout.getLayoutParams();
if (isTablet())
mMainLayoutAnimator = new PropertyAnimator(mMainLayout,
PropertyAnimator.Property.MARGIN_LEFT,
params.leftMargin,
width,
200);
else
mMainLayoutAnimator = new PropertyAnimator(mMainLayout,
PropertyAnimator.Property.MARGIN_TOP,
params.topMargin,
height,
200);
mMainLayoutAnimator.start();
}
public void handleMessage(String event, JSONObject message) {
Log.i(LOGTAG, "Got message: " + event);
try {
@ -1889,6 +1914,7 @@ abstract public class GeckoApp
// setup tabs panel
mTabsPanel = (TabsPanel) findViewById(R.id.tabs_panel);
mTabsPanel.setTabsLayoutChangeListener(this);
if (savedInstanceState != null) {
mBrowserToolbar.setTitle(savedInstanceState.getString(SAVED_STATE_TITLE));

View File

@ -88,6 +88,7 @@ FENNEC_JAVA_FILES = \
MenuItemActionBar.java \
MenuItemDefault.java \
NSSBridge.java \
PropertyAnimator.java \
ProfileMigrator.java \
PromptService.java \
sqlite/ByteBufferInputStream.java \

View File

@ -0,0 +1,113 @@
/* -*- 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;
import java.util.Timer;
import java.util.TimerTask;
import android.os.Handler;
import android.view.animation.Interpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.View;
import android.widget.RelativeLayout;
public class PropertyAnimator extends TimerTask {
private static final String LOGTAG = "GeckoPropertyAnimator";
private Timer mTimer;
private TimerTask mShowTask;
private Interpolator mInterpolator;
public static enum Property {
MARGIN_LEFT,
MARGIN_RIGHT,
MARGIN_TOP,
MARGIN_BOTTOM
}
private View mView;
private Property mProperty;
private int mDuration;
private int mFrom;
private int mTo;
private int mCount;
// Default refresh rate in ms.
private static final int sInterval = 10;
public PropertyAnimator(View view, Property property, int from, int to, int duration) {
mView = view;
mProperty = property;
mDuration = duration;
mFrom = from;
mTo = to;
mTimer = new Timer();
mInterpolator = new DecelerateInterpolator();
}
@Override
public void run() {
float interpolation = mInterpolator.getInterpolation((float) (mCount * sInterval) / (float) mDuration);
int delta;
if (mFrom < mTo)
delta = mFrom + (int) ((mTo - mFrom) * interpolation);
else
delta = mFrom - (int) ((mFrom - mTo) * interpolation);
invalidate(delta);
mCount++;
if (mCount * sInterval >= mDuration)
stop();
}
public void start() {
mCount = 0;
mTimer.scheduleAtFixedRate(this, 0, sInterval);
}
public void stop() {
cancel();
mTimer.cancel();
mTimer.purge();
// Make sure to snap to the end position.
invalidate(mTo);
}
private void invalidate(final int delta) {
// Post the layout changes on the view's UI thread.
mView.getHandler().post(new Runnable() {
@Override
public void run() {
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(mView.getLayoutParams());
switch(mProperty) {
case MARGIN_TOP:
params.setMargins(0, delta, 0, 0);
break;
case MARGIN_BOTTOM:
params.setMargins(0, 0, 0, delta);
break;
case MARGIN_LEFT:
params.setMargins(delta, 0, 0, 0);
break;
case MARGIN_RIGHT:
params.setMargins(0, 0, delta, 0);
break;
}
mView.setLayoutParams(params);
mView.requestLayout();
}
});
}
}

View File

@ -34,8 +34,13 @@ public class TabsPanel extends LinearLayout {
public void hide();
}
public static interface TabsLayoutChangeListener {
public void onTabsLayoutChange(int width, int height);
}
private Context mContext;
private PanelView mPanel;
private TabsLayoutChangeListener mLayoutChangeListener;
private static ImageButton mRemoteTabs;
private TextView mTitle;
@ -80,6 +85,7 @@ public class TabsPanel extends LinearLayout {
public void show(Panel panel) {
if (mPanel != null) {
// Remove the old panel.
mPanel.hide();
if (getChildCount() == 2)
removeViewAt(1);
@ -102,7 +108,10 @@ public class TabsPanel extends LinearLayout {
mPanel.setHeightRestriction(mHeightRestricted);
mPanel.show();
addView(mPanel.getLayout(), 1);
setVisibility(View.VISIBLE);
// Tablet has fixed sized panel, hence we need to inform when we show.
// Phones can be informed too. But the list wouldn't have been inflated by now.
dispatchLayoutChange(getWidth(), getHeight());
// If Sync is set up, query the database for remote clients.
final Context context = mContext;
@ -124,11 +133,16 @@ public class TabsPanel extends LinearLayout {
}
public void hide() {
setVisibility(View.GONE);
mPanel.hide();
if (getChildCount() == 2)
removeViewAt(1);
mVisible = false;
dispatchLayoutChange(0, 0);
}
@Override
public void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
// This is used only for sliding action on phones.
// Tablets have a fixed size pane, hence this should be done while showing.
if (mVisible)
dispatchLayoutChange(width, height);
}
public void refresh() {
@ -140,4 +154,13 @@ public class TabsPanel extends LinearLayout {
public boolean isShown() {
return mVisible;
}
public void setTabsLayoutChangeListener(TabsLayoutChangeListener listener) {
mLayoutChangeListener = listener;
}
private void dispatchLayoutChange(int width, int height) {
if (mLayoutChangeListener != null)
mLayoutChangeListener.onTabsLayoutChange(width, height);
}
}

View File

@ -10,12 +10,10 @@
<org.mozilla.gecko.TabsPanel android:id="@+id/tabs_panel"
android:layout_width="200dip"
android:layout_height="fill_parent"
android:background="@drawable/tabs_tray_bg_repeat"
android:visibility="gone"/>
android:background="@drawable/tabs_tray_bg_repeat"/>
<LinearLayout android:id="@+id/main_layout"
style="@style/Screen"
android:layout_toRightOf="@id/tabs_panel">
style="@style/Screen">
<include layout="@layout/browser_toolbar"/>

View File

@ -10,12 +10,10 @@
<org.mozilla.gecko.TabsPanel android:id="@+id/tabs_panel"
android:layout_width="200dip"
android:layout_height="fill_parent"
android:background="@drawable/tabs_tray_bg_repeat"
android:visibility="gone"/>
android:background="@drawable/tabs_tray_bg_repeat"/>
<LinearLayout android:id="@+id/main_layout"
style="@style/Screen"
android:layout_toRightOf="@id/tabs_panel">
style="@style/Screen">
<include layout="@layout/browser_toolbar"/>

View File

@ -10,12 +10,10 @@
<org.mozilla.gecko.TabsPanel android:id="@+id/tabs_panel"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@drawable/tabs_tray_bg_repeat"
android:visibility="gone"/>
android:background="@drawable/tabs_tray_bg_repeat"/>
<LinearLayout android:id="@+id/main_layout"
style="@style/Screen"
android:layout_below="@id/tabs_panel">
style="@style/Screen">
<include layout="@layout/browser_toolbar"/>