diff options
author | Michael Kolb <kolby@google.com> | 2011-08-09 10:24:41 -0700 |
---|---|---|
committer | Michael Kolb <kolby@google.com> | 2011-08-15 13:31:03 -0700 |
commit | c3af06776be83ba64a0d3549cb72ca6e5e7f03cd (patch) | |
tree | 60d1a15205ac0b46f94528c0da0963a79f6d547e /src/com/android | |
parent | 8d5af2d0208aa5b5197f50e12ba11c5565d74dc4 (diff) | |
download | packages_apps_Browser-c3af06776be83ba64a0d3549cb72ca6e5e7f03cd.zip packages_apps_Browser-c3af06776be83ba64a0d3549cb72ca6e5e7f03cd.tar.gz packages_apps_Browser-c3af06776be83ba64a0d3549cb72ca6e5e7f03cd.tar.bz2 |
Tab switcher animation
Bug: 5123884
first step towards animations between browser and tab switcher
Change-Id: I1d959d42d0036f3c4498972fcc8ad434fa7f4437
Diffstat (limited to 'src/com/android')
-rw-r--r-- | src/com/android/browser/BrowserActivity.java | 31 | ||||
-rw-r--r-- | src/com/android/browser/Controller.java | 28 | ||||
-rw-r--r-- | src/com/android/browser/NavScreen.java | 30 | ||||
-rw-r--r-- | src/com/android/browser/NavTabView.java | 10 | ||||
-rw-r--r-- | src/com/android/browser/PhoneUi.java | 201 | ||||
-rw-r--r-- | src/com/android/browser/Tab.java | 2 | ||||
-rw-r--r-- | src/com/android/browser/UiController.java | 2 | ||||
-rw-r--r-- | src/com/android/browser/view/Gallery.java | 62 |
8 files changed, 334 insertions, 32 deletions
diff --git a/src/com/android/browser/BrowserActivity.java b/src/com/android/browser/BrowserActivity.java index 6ec6071..084ace0 100644 --- a/src/com/android/browser/BrowserActivity.java +++ b/src/com/android/browser/BrowserActivity.java @@ -28,6 +28,7 @@ import android.view.ContextMenu.ContextMenuInfo; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; +import android.view.MotionEvent; import android.view.View; import android.view.Window; import android.view.WindowManager; @@ -271,4 +272,34 @@ public class BrowserActivity extends Activity { return mController.onSearchRequested(); } + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + return mController.dispatchKeyEvent(event) + || super.dispatchKeyEvent(event); + } + + @Override + public boolean dispatchKeyShortcutEvent(KeyEvent event) { + return mController.dispatchKeyShortcutEvent(event) + || super.dispatchKeyShortcutEvent(event); + } + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + return mController.dispatchTouchEvent(ev) + || super.dispatchTouchEvent(ev); + } + + @Override + public boolean dispatchTrackballEvent(MotionEvent ev) { + return mController.dispatchTrackballEvent(ev) + || super.dispatchTrackballEvent(ev); + } + + @Override + public boolean dispatchGenericMotionEvent(MotionEvent ev) { + return mController.dispatchGenericMotionEvent(ev) || + super.dispatchGenericMotionEvent(ev); + } + } diff --git a/src/com/android/browser/Controller.java b/src/com/android/browser/Controller.java index 2144dd0..3f126c0 100644 --- a/src/com/android/browser/Controller.java +++ b/src/com/android/browser/Controller.java @@ -64,6 +64,7 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.MenuItem.OnMenuItemClickListener; +import android.view.MotionEvent; import android.view.View; import android.webkit.CookieManager; import android.webkit.CookieSyncManager; @@ -212,6 +213,8 @@ public class Controller private boolean mSimulateActionBarOverlayMode; + private boolean mBlockEvents; + private static class ClearThumbnails extends AsyncTask<File, Void, Void> { @Override public Void doInBackground(File... files) { @@ -2675,4 +2678,29 @@ public class Controller return mUi.shouldCaptureThumbnails(); } + @Override + public void setBlockEvents(boolean block) { + mBlockEvents = block; + } + + public boolean dispatchKeyEvent(KeyEvent event) { + return mBlockEvents; + } + + public boolean dispatchKeyShortcutEvent(KeyEvent event) { + return mBlockEvents; + } + + public boolean dispatchTouchEvent(MotionEvent ev) { + return mBlockEvents; + } + + public boolean dispatchTrackballEvent(MotionEvent ev) { + return mBlockEvents; + } + + public boolean dispatchGenericMotionEvent(MotionEvent ev) { + return mBlockEvents; + } + } diff --git a/src/com/android/browser/NavScreen.java b/src/com/android/browser/NavScreen.java index 22f6257..768f9ba 100644 --- a/src/com/android/browser/NavScreen.java +++ b/src/com/android/browser/NavScreen.java @@ -39,12 +39,17 @@ import android.widget.TextView; import com.android.browser.NavTabGallery.OnRemoveListener; import com.android.browser.TabControl.OnThumbnailUpdatedListener; +import com.android.browser.view.Gallery.OnScrollFinishedListener; import java.util.HashMap; public class NavScreen extends RelativeLayout implements OnClickListener, OnMenuItemClickListener, OnThumbnailUpdatedListener { + + private static final int SCROLL_MIN = 200; + private static final int SCROLL_FACTOR = 20; + UiController mUiController; PhoneUi mUi; Tab mTab; @@ -126,11 +131,11 @@ public class NavScreen extends RelativeLayout TabControl tc = mUiController.getTabControl(); mTabViews = new HashMap<Tab, View>(tc.getTabCount()); mAdapter = new TabAdapter(mContext, tc); - mScroller.setAdapter(mAdapter); mScroller.setOrientation(mOrientation == Configuration.ORIENTATION_LANDSCAPE ? LinearLayout.HORIZONTAL : LinearLayout.VERTICAL); // update state for active tab - mScroller.setSelection(mUiController.getTabControl().getTabPosition(mUi.getActiveTab())); + mScroller.setAdapter(mAdapter, + mUiController.getTabControl().getTabPosition(mUi.getActiveTab())); mScroller.setOnRemoveListener(new OnRemoveListener() { public void onRemovePosition(int pos) { Tab tab = mAdapter.getItem(pos); @@ -185,21 +190,28 @@ public class NavScreen extends RelativeLayout // need to call openTab explicitely with setactive false Tab tab = mUiController.openTab(BrowserSettings.getInstance().getHomePage(), false, false, false); - mAdapter.notifyDataSetChanged(); + int duration = 0; if (tab != null) { - // set tab as the selected in flipper, then hide + mUiController.setBlockEvents(true); + int oldsel = mScroller.getSelectedItemPosition(); final int tix = mUi.mTabControl.getTabPosition(tab); - mScroller.setSelection(tix); - postDelayed(new Runnable() { + duration = SCROLL_MIN + SCROLL_FACTOR * Math.abs(oldsel - tix); + mScroller.handleDataChanged(); + mScroller.smoothScrollToPosition(tix, duration, new OnScrollFinishedListener() { @Override - public void run() { + public void onScrollFinished() { + mUiController.setBlockEvents(false); mUi.hideNavScreen(true); switchToSelected(); } - }, 100); + }); } } + View getSelectedTabView() { + return mScroller.getSelectedTab(); + } + private void switchToSelected() { Tab tab = (Tab) mScroller.getSelectedItem(); if (tab != mUi.getActiveTab()) { @@ -243,7 +255,7 @@ public class NavScreen extends RelativeLayout public View getView(final int position, View convertView, ViewGroup parent) { final NavTabView tabview = new NavTabView(mActivity); final Tab tab = getItem(position); - tabview.setWebView(mUi, tab); + tabview.setWebView(tab); mTabViews.put(tab, tabview.mImage); tabview.setOnClickListener(new OnClickListener() { @Override diff --git a/src/com/android/browser/NavTabView.java b/src/com/android/browser/NavTabView.java index ed6b63d..07ac164 100644 --- a/src/com/android/browser/NavTabView.java +++ b/src/com/android/browser/NavTabView.java @@ -21,12 +21,15 @@ import android.graphics.Bitmap; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; +import android.webkit.WebView; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; public class NavTabView extends LinearLayout { + private ViewGroup mContent; private Tab mTab; private ImageView mClose; private TextView mTitle; @@ -52,6 +55,7 @@ public class NavTabView extends LinearLayout { private void init() { LayoutInflater.from(mContext).inflate(R.layout.nav_tab_view, this); + mContent = (ViewGroup) findViewById(R.id.main); mClose = (ImageView) findViewById(R.id.closetab); mTitle = (TextView) findViewById(R.id.title); mTitleBar = findViewById(R.id.titlebar); @@ -92,7 +96,11 @@ public class NavTabView extends LinearLayout { return mHighlighted; } - protected void setWebView(PhoneUi ui, Tab tab) { + protected void setWebView(WebView w) { + mContent.addView(w, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); + } + + protected void setWebView(Tab tab) { mTab = tab; setTitle(); Bitmap image = tab.getScreenshot(); diff --git a/src/com/android/browser/PhoneUi.java b/src/com/android/browser/PhoneUi.java index 863a628..d77fcdb 100644 --- a/src/com/android/browser/PhoneUi.java +++ b/src/com/android/browser/PhoneUi.java @@ -16,17 +16,29 @@ package com.android.browser; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; import android.app.Activity; +import android.app.ActivityManager; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Matrix; import android.util.Log; import android.view.ActionMode; import android.view.Gravity; import android.view.KeyEvent; +import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.accessibility.AccessibilityEvent; +import android.view.animation.DecelerateInterpolator; import android.webkit.WebView; import android.widget.FrameLayout; +import android.widget.ImageView; import com.android.browser.UrlInputView.StateListener; @@ -241,15 +253,72 @@ public class PhoneUi extends BaseUi { updateUrlBarAutoShowManagerTarget(); } + @Override + public boolean isWebShowing() { + return super.isWebShowing() && mNavScreen == null; + } + + @Override + public void showWeb(boolean animate) { + super.showWeb(animate); + hideNavScreen(animate); + } + void showNavScreen() { - mActiveTab.capture(); - detachTab(mActiveTab); + mUiController.setBlockEvents(true); mNavScreen = new NavScreen(mActivity, mUiController, this); - // Add the custom view to its container. + mActiveTab.capture(); + // Add the custom view to its container mCustomViewContainer.addView(mNavScreen, COVER_SCREEN_PARAMS); - mContentView.setVisibility(View.GONE); + AnimScreen ascreen = new AnimScreen(mActivity, getTitleBar(), getWebView()); + final View animView = ascreen.mMain; + mCustomViewContainer.addView(animView, COVER_SCREEN_PARAMS); mCustomViewContainer.setVisibility(View.VISIBLE); mCustomViewContainer.bringToFront(); + View target = ((NavTabView) mNavScreen.mScroller.getSelectedView()).mImage; + int fromLeft = 0; + int fromTop = getTitleBar().getHeight(); + int fromRight = mContentView.getWidth(); + int fromBottom = mContentView.getHeight(); + int width = target.getWidth(); + int height = target.getHeight(); + int toLeft = (mContentView.getWidth() - width) / 2; + int toTop = fromTop + (mContentView.getHeight() - fromTop - height) / 2; + int toRight = toLeft + width; + int toBottom = toTop + height; + float scaleFactor = width / (float) mContentView.getWidth(); + detachTab(mActiveTab); + mContentView.setVisibility(View.GONE); + AnimatorSet inanim = new AnimatorSet(); + ObjectAnimator tx = ObjectAnimator.ofInt(ascreen.mContent, "left", + fromLeft, toLeft); + ObjectAnimator ty = ObjectAnimator.ofInt(ascreen.mContent, "top", + fromTop, toTop); + ObjectAnimator tr = ObjectAnimator.ofInt(ascreen.mContent, "right", + fromRight, toRight); + ObjectAnimator tb = ObjectAnimator.ofInt(ascreen.mContent, "bottom", + fromBottom, toBottom); + ObjectAnimator title = ObjectAnimator.ofFloat(ascreen.mTitle, "alpha", + 1f, 0f); + ObjectAnimator content = ObjectAnimator.ofFloat(ascreen.mContent, "alpha", + 1f, 0f); + ObjectAnimator sx = ObjectAnimator.ofFloat(ascreen, "scaleFactor", + 1f, scaleFactor); + inanim.playTogether(tx, ty, tr, tb, title, content, sx); + inanim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator anim) { + mCustomViewContainer.removeView(animView); + finishAnimationIn(); + mUiController.setBlockEvents(false); + } + }); + inanim.setInterpolator(new DecelerateInterpolator(2f)); + inanim.setDuration(300); + inanim.start(); + } + + private void finishAnimationIn() { // notify accessibility manager about the screen change mNavScreen.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); mTabControl.setOnThumbnailUpdatedListener(mNavScreen); @@ -257,14 +326,67 @@ public class PhoneUi extends BaseUi { void hideNavScreen(boolean animate) { if (mNavScreen == null) return; + final Tab tab = mNavScreen.getSelectedTab(); + if ((tab != null) && !animate) { + finishAnimateOut(tab); + } + NavTabView tabview = (NavTabView) mNavScreen.getSelectedTabView(); + if (tabview == null) { + finishAnimateOut(tab); + } + mUiController.setBlockEvents(true); + mUiController.setActiveTab(tab); + mContentView.setVisibility(View.VISIBLE); + final AnimScreen screen = new AnimScreen(mActivity, tab.getScreenshot()); + View target = ((NavTabView) mNavScreen.mScroller.getSelectedView()).mImage; + int toLeft = 0; + int toTop = getTitleBar().getHeight(); + int toRight = mContentView.getWidth(); + int width = target.getWidth(); + int height = target.getHeight(); + int[] pos = new int[2]; + tabview.mImage.getLocationInWindow(pos); + int fromLeft = pos[0]; + int fromTop = pos[1]; + int fromRight = fromLeft + width; + int fromBottom = fromTop + height; + float scaleFactor = mContentView.getWidth() / (float) width; + int toBottom = (int) (height * scaleFactor); + screen.mMain.setAlpha(0f); + mCustomViewContainer.addView(screen.mMain, COVER_SCREEN_PARAMS); + AnimatorSet animSet = new AnimatorSet(); + ObjectAnimator l = ObjectAnimator.ofInt(screen.mContent, "left", + fromLeft, toLeft); + ObjectAnimator t = ObjectAnimator.ofInt(screen.mContent, "top", + fromTop, toTop); + ObjectAnimator r = ObjectAnimator.ofInt(screen.mContent, "right", + fromRight, toRight); + ObjectAnimator b = ObjectAnimator.ofInt(screen.mContent, "bottom", + fromBottom, toBottom); + ObjectAnimator scale = ObjectAnimator.ofFloat(screen, "scaleFactor", + 1f, scaleFactor); + ObjectAnimator alpha = ObjectAnimator.ofFloat(screen.mMain, "alpha", 1f, 1f); + ObjectAnimator otheralpha = ObjectAnimator.ofFloat(mCustomViewContainer, "alpha", 1f, 0f); + alpha.setStartDelay(100); + animSet.playTogether(l, t, r, b, scale, alpha, otheralpha); + animSet.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator anim) { + mCustomViewContainer.removeView(screen.mMain); + finishAnimateOut(tab); + mUiController.setBlockEvents(false); + } + }); + animSet.setDuration(250); + animSet.start(); + } + + private void finishAnimateOut(Tab tab) { mTabControl.setOnThumbnailUpdatedListener(null); - Tab tab = mNavScreen.getSelectedTab(); mCustomViewContainer.removeView(mNavScreen); + mCustomViewContainer.setAlpha(1f); mNavScreen = null; mCustomViewContainer.setVisibility(View.GONE); - mUiController.setActiveTab(tab); - // Show the content view. - mContentView.setVisibility(View.VISIBLE); } @Override @@ -285,15 +407,62 @@ public class PhoneUi extends BaseUi { return true; } - @Override - public boolean isWebShowing() { - return super.isWebShowing() && mNavScreen == null; - } + static class AnimScreen { + + private View mMain; + private ImageView mTitle; + private ImageView mContent; + private float mScale; + + public AnimScreen(Context ctx, TitleBar tbar, WebView web) { + mMain = LayoutInflater.from(ctx).inflate(R.layout.anim_screen, + null); + mContent = (ImageView) mMain.findViewById(R.id.content); + mContent.setTop(tbar.getHeight()); + + mTitle = (ImageView) mMain.findViewById(R.id.title); + Bitmap bm1 = Bitmap.createBitmap(tbar.getWidth(), tbar.getHeight(), + Bitmap.Config.RGB_565); + Canvas c1 = new Canvas(bm1); + tbar.draw(c1); + mTitle.setImageBitmap(bm1); + int h = web.getHeight() - tbar.getHeight(); + Bitmap bm2 = Bitmap.createBitmap(web.getWidth(), h, + Bitmap.Config.RGB_565); + Canvas c2 = new Canvas(bm2); + int tx = web.getScrollX(); + int ty = web.getScrollY(); + c2.translate(-tx, -ty - tbar.getHeight()); + web.draw(c2); + mContent.setImageBitmap(bm2); + mContent.setScaleType(ImageView.ScaleType.MATRIX); + mContent.setImageMatrix(new Matrix()); + mScale = 1.0f; + setScaleFactor(getScaleFactor()); + } + + public AnimScreen(Context ctx, Bitmap image) { + mMain = LayoutInflater.from(ctx).inflate(R.layout.anim_screen, + null); + mContent = (ImageView) mMain.findViewById(R.id.content); + mContent.setImageBitmap(image); + mContent.setScaleType(ImageView.ScaleType.MATRIX); + mContent.setImageMatrix(new Matrix()); + mScale = 1.0f; + setScaleFactor(getScaleFactor()); + } + + public void setScaleFactor(float sf) { + mScale = sf; + Matrix m = new Matrix(); + m.postScale(sf,sf); + mContent.setImageMatrix(m); + } + + public float getScaleFactor() { + return mScale; + } - @Override - public void showWeb(boolean animate) { - super.showWeb(animate); - hideNavScreen(animate); } } diff --git a/src/com/android/browser/Tab.java b/src/com/android/browser/Tab.java index 78ae987..c6808e0 100644 --- a/src/com/android/browser/Tab.java +++ b/src/com/android/browser/Tab.java @@ -31,6 +31,7 @@ import android.graphics.Bitmap; import android.graphics.Bitmap.CompressFormat; import android.graphics.BitmapFactory; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.Picture; import android.net.Uri; import android.net.http.SslError; @@ -1455,6 +1456,7 @@ class Tab implements PictureListener { if (mCapture == null) { mCapture = Bitmap.createBitmap(mCaptureWidth, mCaptureHeight, Bitmap.Config.RGB_565); + mCapture.eraseColor(Color.WHITE); if (mInForeground) { postCapture(); } diff --git a/src/com/android/browser/UiController.java b/src/com/android/browser/UiController.java index 14d498c..0da523a 100644 --- a/src/com/android/browser/UiController.java +++ b/src/com/android/browser/UiController.java @@ -98,4 +98,6 @@ public interface UiController { void loadUrl(Tab tab, String url); + void setBlockEvents(boolean block); + } diff --git a/src/com/android/browser/view/Gallery.java b/src/com/android/browser/view/Gallery.java index 78d4bc6..2e2c75f 100644 --- a/src/com/android/browser/view/Gallery.java +++ b/src/com/android/browser/view/Gallery.java @@ -34,6 +34,8 @@ import android.view.SoundEffectConstants; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; +import android.view.animation.BounceInterpolator; +import android.view.animation.DecelerateInterpolator; import android.view.animation.Transformation; import android.widget.BaseAdapter; import android.widget.LinearLayout; @@ -130,6 +132,8 @@ public class Gallery extends ViewGroup implements private float mLastMotionCoord; private float mLastOrthoCoord; + private int mScrollValue; + public Gallery(Context context) { this(context, null); } @@ -172,6 +176,7 @@ public class Gallery extends ViewGroup implements mGap = 0; // proguard setGap(getGap()); + setScrollValue(getScrollValue()); } /** @@ -180,7 +185,10 @@ public class Gallery extends ViewGroup implements */ public interface OnItemSelectedListener { void onItemSelected(ViewGroup parent, View view, int position, long id); + } + public interface OnScrollFinishedListener { + void onScrollFinished(); } /** @@ -226,6 +234,11 @@ public class Gallery extends ViewGroup implements return mGap; } + public void setAdapter(BaseAdapter adapter, int selpos) { + mSelectedPosition = selpos; + setAdapter(adapter); + } + public void setAdapter(BaseAdapter adapter) { mAdapter = adapter; if (mAdapter != null) { @@ -246,7 +259,7 @@ public class Gallery extends ViewGroup implements handleDataChanged(); } - void handleDataChanged() { + public void handleDataChanged() { if (mAdapter != null) { if (mGapAnimator != null) { mGapAnimator.cancel(); @@ -427,7 +440,8 @@ public class Gallery extends ViewGroup implements return; } boolean toLeft = deltaX < 0; - int limitedDeltaX = getLimitedMotionScrollAmount(toLeft, deltaX); + int limitedDeltaX = mFlingRunnable.mScroller.isFinished() + ? deltaX : getLimitedMotionScrollAmount(toLeft, deltaX); if (limitedDeltaX != deltaX) { // The above call returned a limited amount, so stop any // scrolls/flings @@ -1178,7 +1192,7 @@ public class Gallery extends ViewGroup implements } } - boolean moveNext() { + public boolean moveNext() { if (mItemCount > 0 && mSelectedPosition < mItemCount - 1) { scrollToChild(mSelectedPosition - mFirstPosition + 1); return true; @@ -1187,16 +1201,48 @@ public class Gallery extends ViewGroup implements } } - protected boolean scrollToChild(int childPosition) { + public boolean scrollToChild(int childPosition) { View child = getChildAt(childPosition); if (child != null) { int distance = getCenterOfGallery() - getCenterOfView(child); - mFlingRunnable.startUsingDistance(distance); + mFlingRunnable.startUsingDistance(distance, 0); return true; } return false; } + /** + * use the scroller to scroll to a new position, independent + * of whether attached or not + * this uses trackMotionScroll, which will set the selection + */ + public void smoothScrollToPosition(int pos, int duration, + final OnScrollFinishedListener listener) { + if (pos >= mAdapter.getCount() || getChildCount() < 1) return; + int dist = (mSelectedPosition - pos) * (mHorizontal ? getChildHeight(getChildAt(0)) + : getChildWidth(getChildAt(0))); + ObjectAnimator scroll = ObjectAnimator.ofInt(this, "scrollValue", 0, dist); + scroll.setDuration(duration); + scroll.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mScrollValue = 0; + listener.onScrollFinished(); + } + }); + scroll.setInterpolator(new BounceInterpolator()); + scroll.start(); + } + + public void setScrollValue(int scroll) { + trackMotionScroll(scroll - mScrollValue); + mScrollValue = scroll; + } + + public int getScrollValue() { + return mScrollValue; + } + protected void setSelectedPositionInt(int position) { mSelectedPosition = position; updateSelectedItemMetadata(); @@ -1368,11 +1414,15 @@ public class Gallery extends ViewGroup implements } public void startUsingDistance(int distance) { + startUsingDistance(distance, mAnimationDuration); + } + + public void startUsingDistance(int distance, int duration) { if (distance == 0) return; startCommon(); mLastFlingX = 0; - mScroller.startScroll(0, 0, -distance, 0, mAnimationDuration); + mScroller.startScroll(0, 0, -distance, 0, duration); post(this); } |