Bug 863828 - New tab increment animation. r=lucasr

This commit is contained in:
Wes Johnston 2013-04-26 16:17:34 -07:00
Родитель df41ef2a43
Коммит 2ac6d5be66
15 изменённых файлов: 231 добавлений и 76 удалений

Просмотреть файл

@ -33,15 +33,14 @@ import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.MarginLayoutParams;
import android.view.Window;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AlphaAnimation;
import android.view.animation.TranslateAnimation;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
@ -57,8 +56,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class BrowserToolbar implements ViewSwitcher.ViewFactory,
Tabs.OnTabsChangedListener,
public class BrowserToolbar implements Tabs.OnTabsChangedListener,
GeckoMenu.ActionItemBarPresenter,
Animation.AnimationListener,
SharedPreferences.OnSharedPreferenceChangeListener {
@ -84,7 +82,7 @@ public class BrowserToolbar implements ViewSwitcher.ViewFactory,
public ImageButton mSiteSecurity;
public ImageButton mReader;
private AnimationDrawable mProgressSpinner;
private GeckoTextSwitcher mTabsCount;
private TabCounter mTabsCounter;
private ImageView mShadow;
private GeckoImageButton mMenu;
private LinearLayout mActionItemBar;
@ -92,7 +90,6 @@ public class BrowserToolbar implements ViewSwitcher.ViewFactory,
private List<View> mFocusOrder;
final private BrowserApp mActivity;
private LayoutInflater mInflater;
private Handler mHandler;
private boolean mHasSoftMenuButton;
@ -103,12 +100,6 @@ public class BrowserToolbar implements ViewSwitcher.ViewFactory,
private boolean mAnimatingEntry;
private int mDuration;
private TranslateAnimation mSlideUpIn;
private TranslateAnimation mSlideUpOut;
private TranslateAnimation mSlideDownIn;
private TranslateAnimation mSlideDownOut;
private AlphaAnimation mLockFadeIn;
private TranslateAnimation mTitleSlideLeft;
private TranslateAnimation mTitleSlideRight;
@ -117,7 +108,6 @@ public class BrowserToolbar implements ViewSwitcher.ViewFactory,
private int mDefaultForwardMargin;
private PropertyAnimator mForwardAnim = null;
private int mCount;
private int mFaviconSize;
private PropertyAnimator mVisibilityAnimator;
@ -135,7 +125,6 @@ public class BrowserToolbar implements ViewSwitcher.ViewFactory,
public BrowserToolbar(BrowserApp activity) {
// BrowserToolbar is attached to BrowserApp only.
mActivity = activity;
mInflater = LayoutInflater.from(activity);
sActionItems = new ArrayList<View>();
Tabs.registerOnTabsChangedListener(this);
@ -251,23 +240,7 @@ public class BrowserToolbar implements ViewSwitcher.ViewFactory,
});
mTabs.setImageLevel(0);
mTabsCount = (GeckoTextSwitcher) mLayout.findViewById(R.id.tabs_count);
mTabsCount.removeAllViews();
mTabsCount.setFactory(this);
mTabsCount.setText("");
mCount = 0;
if (Build.VERSION.SDK_INT >= 16) {
// This adds the TextSwitcher to the a11y node tree, where we in turn
// could make it return an empty info node. If we don't do this the
// TextSwitcher's child TextViews get picked up, and we don't want
// that since the tabs ImageButton is already properly labeled for
// accessibility.
mTabsCount.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
mTabsCount.setAccessibilityDelegate(new View.AccessibilityDelegate() {
@Override
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {}
});
}
mTabsCounter = (TabCounter) mLayout.findViewById(R.id.tabs_counter);
mBack = (ImageButton) mLayout.findViewById(R.id.back);
mBack.setOnClickListener(new Button.OnClickListener() {
@ -366,16 +339,6 @@ public class BrowserToolbar implements ViewSwitcher.ViewFactory,
});
mHandler = new Handler();
mSlideUpIn = new TranslateAnimation(0, 0, 40, 0);
mSlideUpOut = new TranslateAnimation(0, 0, 0, -40);
mSlideDownIn = new TranslateAnimation(0, 0, -40, 0);
mSlideDownOut = new TranslateAnimation(0, 0, 0, 40);
mDuration = 750;
mSlideUpIn.setDuration(mDuration);
mSlideUpOut.setDuration(mDuration);
mSlideDownIn.setDuration(mDuration);
mSlideDownOut.setDuration(mDuration);
float slideWidth = mActivity.getResources().getDimension(R.dimen.browser_toolbar_lock_width);
@ -579,12 +542,6 @@ public class BrowserToolbar implements ViewSwitcher.ViewFactory,
}
}
@Override
public View makeView() {
// This returns a TextView for the TextSwitcher.
return mInflater.inflate(R.layout.tabs_counter, null);
}
private int getAwesomeBarAnimTranslation() {
return mLayout.getWidth() - mAwesomeBarEntry.getRight();
}
@ -617,7 +574,7 @@ public class BrowserToolbar implements ViewSwitcher.ViewFactory,
proxy.setTranslationX(translation);
proxy = AnimatorProxy.create(mTabs);
proxy.setTranslationX(translation);
proxy = AnimatorProxy.create(mTabsCount);
proxy = AnimatorProxy.create(mTabsCounter);
proxy.setTranslationX(translation);
proxy = AnimatorProxy.create(mActionItemBar);
proxy.setTranslationX(translation);
@ -652,7 +609,7 @@ public class BrowserToolbar implements ViewSwitcher.ViewFactory,
contentAnimator.attach(mTabs,
PropertyAnimator.Property.TRANSLATION_X,
0);
contentAnimator.attach(mTabsCount,
contentAnimator.attach(mTabsCounter,
PropertyAnimator.Property.TRANSLATION_X,
0);
contentAnimator.attach(mActionItemBar,
@ -753,7 +710,7 @@ public class BrowserToolbar implements ViewSwitcher.ViewFactory,
contentAnimator.attach(mTabs,
PropertyAnimator.Property.TRANSLATION_X,
translation);
contentAnimator.attach(mTabsCount,
contentAnimator.attach(mTabsCounter,
PropertyAnimator.Property.TRANSLATION_X,
translation);
contentAnimator.attach(mActionItemBar,
@ -815,29 +772,18 @@ public class BrowserToolbar implements ViewSwitcher.ViewFactory,
return;
}
if (mCount > count) {
mTabsCount.setInAnimation(mSlideDownIn);
mTabsCount.setOutAnimation(mSlideDownOut);
} else if (mCount < count) {
mTabsCount.setInAnimation(mSlideUpIn);
mTabsCount.setOutAnimation(mSlideUpOut);
} else {
return;
}
mTabsCounter.setCount(count);
mTabsCount.setText(String.valueOf(count));
mTabs.setContentDescription((count > 1) ?
mActivity.getString(R.string.num_tabs, count) :
mActivity.getString(R.string.one_tab));
mCount = count;
}
public void updateTabCount(int count) {
mTabsCount.setCurrentText(String.valueOf(count));
mTabsCounter.setCurrentText(String.valueOf(count));
mTabs.setContentDescription((count > 1) ?
mActivity.getString(R.string.num_tabs, count) :
mActivity.getString(R.string.one_tab));
mCount = count;
updateTabs(mActivity.areTabsShown());
}
@ -851,7 +797,7 @@ public class BrowserToolbar implements ViewSwitcher.ViewFactory,
animator.attach(mTabs,
PropertyAnimator.Property.TRANSLATION_X,
width);
animator.attach(mTabsCount,
animator.attach(mTabsCounter,
PropertyAnimator.Property.TRANSLATION_X,
width);
animator.attach(mBack,
@ -887,7 +833,7 @@ public class BrowserToolbar implements ViewSwitcher.ViewFactory,
mAwesomeBarEntry.setTranslationX(width);
mAddressBarBg.setTranslationX(width);
mTabs.setTranslationX(width);
mTabsCount.setTranslationX(width);
mTabsCounter.setTranslationX(width);
mBack.setTranslationX(width);
mForward.setTranslationX(width);
mTitle.setTranslationX(width);

Просмотреть файл

@ -143,6 +143,7 @@ FENNEC_JAVA_FILES = \
ReaderModeUtils.java \
RemoteTabs.java \
RobocopAPI.java \
Rotate3DAnimation.java \
ServiceNotificationClient.java \
SessionParser.java \
SetupScreen.java \
@ -152,6 +153,7 @@ FENNEC_JAVA_FILES = \
SuggestClient.java \
SurfaceBits.java \
Tab.java \
TabCounter.java \
Tabs.java \
TabsPanel.java \
TabsTray.java \
@ -599,6 +601,7 @@ RES_DRAWABLE_MDPI = \
res/drawable-mdpi/tab_thumbnail_default.png \
res/drawable-mdpi/tab_thumbnail_shadow.png \
res/drawable-mdpi/tabs_count.png \
res/drawable-mdpi/tabs_count_foreground.png \
res/drawable-mdpi/address_bar_url_default.9.png \
res/drawable-mdpi/address_bar_url_default_pb.9.png \
res/drawable-mdpi/address_bar_url_pressed.9.png \
@ -697,6 +700,7 @@ RES_DRAWABLE_HDPI = \
res/drawable-hdpi/tab_thumbnail_default.png \
res/drawable-hdpi/tab_thumbnail_shadow.png \
res/drawable-hdpi/tabs_count.png \
res/drawable-hdpi/tabs_count_foreground.png \
res/drawable-hdpi/address_bar_url_default.9.png \
res/drawable-hdpi/address_bar_url_default_pb.9.png \
res/drawable-hdpi/address_bar_url_pressed.9.png \
@ -784,6 +788,7 @@ RES_DRAWABLE_XHDPI = \
res/drawable-xhdpi/tab_thumbnail_default.png \
res/drawable-xhdpi/tab_thumbnail_shadow.png \
res/drawable-xhdpi/tabs_count.png \
res/drawable-xhdpi/tabs_count_foreground.png \
res/drawable-xhdpi/doorhanger_popup_bg.9.png \
res/drawable-xhdpi/find_close.png \
res/drawable-xhdpi/find_next.png \

Просмотреть файл

@ -0,0 +1,97 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mozilla.gecko;
import android.view.animation.Animation;
import android.view.animation.Transformation;
import android.graphics.Camera;
import android.graphics.Matrix;
/**
* An animation that rotates the view on the Y axis between two specified angles.
* This animation also adds a translation on the Z axis (depth) to improve the effect.
*/
public class Rotate3DAnimation extends Animation {
private final float mFromDegrees;
private final float mToDegrees;
private final float mCenterX;
private final float mCenterY;
private final float mDepthZ;
private final boolean mReverse;
private Camera mCamera;
private int mWidth = 1;
private int mHeight = 1;
/**
* Creates a new 3D rotation on the Y axis. The rotation is defined by its
* start angle and its end angle. Both angles are in degrees. The rotation
* is performed around a center point on the 2D space, definied by a pair
* of X and Y coordinates, called centerX and centerY. When the animation
* starts, a translation on the Z axis (depth) is performed. The length
* of the translation can be specified, as well as whether the translation
* should be reversed in time.
*
* @param fromDegrees the start angle of the 3D rotation
* @param toDegrees the end angle of the 3D rotation
* @param centerX the X center of the 3D rotation
* @param centerY the Y center of the 3D rotation
* @param reverse true if the translation should be reversed, false otherwise
*/
public Rotate3DAnimation(float fromDegrees, float toDegrees,
float centerX, float centerY, float depthZ, boolean reverse) {
mFromDegrees = fromDegrees;
mToDegrees = toDegrees;
mCenterX = centerX;
mCenterY = centerY;
mDepthZ = depthZ;
mReverse = reverse;
}
@Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
mCamera = new Camera();
mWidth = width;
mHeight = height;
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
final float fromDegrees = mFromDegrees;
float degrees = fromDegrees + ((mToDegrees - fromDegrees) * interpolatedTime);
final Camera camera = mCamera;
final Matrix matrix = t.getMatrix();
camera.save();
if (mReverse) {
camera.translate(0.0f, 0.0f, mDepthZ * interpolatedTime);
} else {
camera.translate(0.0f, 0.0f, mDepthZ * (1.0f - interpolatedTime));
}
camera.rotateX(degrees);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-mCenterX * mWidth, -mCenterY * mHeight);
matrix.postTranslate(mCenterX * mWidth, mCenterY * mHeight);
}
}

Просмотреть файл

@ -0,0 +1,100 @@
/* -*- 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 android.content.Context;
import android.os.Build;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.AlphaAnimation;
import android.view.LayoutInflater;
import android.view.View;
import android.util.AttributeSet;
import android.widget.ViewSwitcher;
public class TabCounter extends GeckoTextSwitcher
implements ViewSwitcher.ViewFactory {
private static final float CENTER_X = 0.5f;
private static final float CENTER_Y = 1.25f;
private static final int DURATION = 500;
private static final float Z_DISTANCE = 200;
private final AnimationSet mFlipInForward;
private final AnimationSet mFlipInBackward;
private final AnimationSet mFlipOutForward;
private final AnimationSet mFlipOutBackward;
private final LayoutInflater mInflater;
private int mCount = 0;
private enum FadeMode {
FADE_IN,
FADE_OUT
}
public TabCounter(Context context, AttributeSet attrs) {
super(context, attrs);
mInflater = LayoutInflater.from(context);
mFlipInForward = createAnimation(-90, 0, FadeMode.FADE_IN, -1 * Z_DISTANCE, false);
mFlipInBackward = createAnimation(90, 0, FadeMode.FADE_IN, Z_DISTANCE, false);
mFlipOutForward = createAnimation(0, -90, FadeMode.FADE_OUT, -1 * Z_DISTANCE, true);
mFlipOutBackward = createAnimation(0, 90, FadeMode.FADE_OUT, Z_DISTANCE, true);
removeAllViews();
setFactory(this);
setCount(0);
if (Build.VERSION.SDK_INT >= 16) {
// This adds the TextSwitcher to the a11y node tree, where we in turn
// could make it return an empty info node. If we don't do this the
// TextSwitcher's child TextViews get picked up, and we don't want
// that since the tabs ImageButton is already properly labeled for
// accessibility.
setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
setAccessibilityDelegate(new View.AccessibilityDelegate() {
@Override
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {}
});
}
}
public void setCount(int count) {
if (mCount > count) {
setInAnimation(mFlipInBackward);
setOutAnimation(mFlipOutForward);
} else if (mCount < count) {
setInAnimation(mFlipInForward);
setOutAnimation(mFlipOutBackward);
} else {
return;
}
setText(String.valueOf(count));
mCount = count;
}
private AnimationSet createAnimation(float startAngle, float endAngle,
FadeMode fadeMode,
float zEnd, boolean reverse) {
final Context context = getContext();
AnimationSet set = new AnimationSet(context, null);
set.addAnimation(new Rotate3DAnimation(startAngle, endAngle, CENTER_X, CENTER_Y, zEnd, reverse));
set.addAnimation(fadeMode == FadeMode.FADE_IN ? new AlphaAnimation(0.0f, 1.0f) :
new AlphaAnimation(1.0f, 0.0f));
set.setDuration(DURATION);
set.setInterpolator(context, android.R.anim.accelerate_interpolator);
return set;
}
@Override
public View makeView() {
return mInflater.inflate(R.layout.tabs_counter, null);
}
}

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 1.2 KiB

После

Ширина:  |  Высота:  |  Размер: 288 B

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 325 B

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 290 B

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 1.3 KiB

После

Ширина:  |  Высота:  |  Размер: 330 B

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 385 B

Просмотреть файл

@ -31,15 +31,14 @@
2 layers. Hence to center this, an additional 4dp is added to the right.
The margins will be 12dp on left, 48dp on right, instead of ideal 16dp
and 44dp. -->
<Gecko.TextSwitcher android:id="@+id/tabs_count"
style="@style/AddressBar.ImageButton"
<org.mozilla.gecko.TabCounter android:id="@+id/tabs_counter"
style="@style/AddressBar.ImageButton.TabCount"
android:layout_width="24dip"
android:layout_height="24dip"
android:layout_marginLeft="12dip"
android:layout_marginRight="48dip"
android:layout_marginTop="16dp"
android:layout_alignLeft="@id/tabs"
android:gravity="center_horizontal"/>
android:layout_alignLeft="@id/tabs"/>
<FrameLayout android:layout_width="fill_parent"
android:layout_height="fill_parent"

Просмотреть файл

@ -8,6 +8,8 @@
android:id="@+id/browser_toolbar"
style="@style/BrowserToolbar"
android:layout_centerVertical="true"
android:clipChildren="false"
android:clipToPadding="false"
android:clickable="true"
android:focusable="true">
@ -80,15 +82,14 @@
2 layers. Hence to center this, an additional 4dp is added to the left.
The margins will be 40dp on left, 8dp on right, instead of ideal 30dp
and 12dp. -->
<Gecko.TextSwitcher android:id="@+id/tabs_count"
style="@style/AddressBar.ImageButton"
<org.mozilla.gecko.TabCounter android:id="@+id/tabs_counter"
style="@style/AddressBar.ImageButton.TabCount"
android:layout_width="24dip"
android:layout_height="24dip"
android:layout_marginLeft="40dip"
android:layout_marginRight="8dip"
android:layout_marginTop="12dip"
android:layout_alignRight="@id/tabs"
android:gravity="center_horizontal"/>
android:layout_alignRight="@id/tabs"/>
<LinearLayout android:id="@+id/awesome_bar_content"
style="@style/AddressBar.Button"

Просмотреть файл

@ -9,7 +9,7 @@
android:layout_margin="12dip"
android:paddingTop="2dip"
android:paddingLeft="4dip"
android:background="@drawable/tabs_count"
android:background="@drawable/tabs_count_foreground"
android:textAppearance="@style/TextAppearance.Micro"
android:textColor="#FF43484E"
android:textStyle="bold"

Просмотреть файл

@ -161,6 +161,13 @@
<item name="android:orientation">horizontal</item>
</style>
<style name="AddressBar.ImageButton.TabCount">
<item name="android:background">@drawable/tabs_count</item>
<item name="android:gravity">center_horizontal</item>
<item name="android:clipChildren">false</item>
<item name="android:clipToPadding">false</item>
</style>
<!-- Address bar -->
<style name="AddressBar">
<item name="android:layout_width">fill_parent</item>

Просмотреть файл

@ -407,7 +407,7 @@ abstract class BaseTest extends ActivityInstrumentationTestCase2<Activity> {
public final void verifyTabCount(int expectedTabCount) {
Activity activity = getActivity();
Element tabCount = mDriver.findElement(activity, "tabs_count");
Element tabCount = mDriver.findElement(activity, "tabs_counter");
String tabCountText = tabCount.getText();
int tabCountInt = Integer.parseInt(tabCountText);
mAsserter.is(tabCountInt, expectedTabCount, "The correct number of tabs are opened");

Просмотреть файл

@ -25,7 +25,7 @@ public class testNewTab extends BaseTest {
blockForGeckoReady();
Activity activity = getActivity();
tabCount = mDriver.findElement(activity, "tabs_count");
tabCount = mDriver.findElement(activity, "tabs_counter");
tabs = mDriver.findElement(activity, "tabs");
addTab = mDriver.findElement(activity, "add_tab");
mAsserter.ok(tabCount != null &&