diff options
-rw-r--r-- | core/java/android/app/ActionBar.java | 21 | ||||
-rw-r--r-- | core/java/android/app/Activity.java | 23 | ||||
-rw-r--r-- | core/java/android/widget/ActionMenuView.java | 7 | ||||
-rw-r--r-- | core/java/android/widget/Toolbar.java | 17 | ||||
-rw-r--r-- | core/java/com/android/internal/app/ToolbarActionBar.java | 179 | ||||
-rw-r--r-- | core/java/com/android/internal/widget/ToolbarWidgetWrapper.java | 9 | ||||
-rw-r--r-- | core/res/res/layout/alert_dialog_quantum.xml | 19 | ||||
-rw-r--r-- | core/res/res/values/dimens_quantum.xml | 5 | ||||
-rw-r--r-- | core/res/res/values/donottranslate_quantum.xml | 2 | ||||
-rw-r--r-- | core/res/res/values/styles_quantum.xml | 2 | ||||
-rw-r--r-- | core/res/res/values/themes_quantum.xml | 6 | ||||
-rw-r--r-- | graphics/java/android/graphics/drawable/Ripple.java | 255 | ||||
-rw-r--r-- | graphics/java/android/graphics/drawable/RippleDrawable.java | 14 |
13 files changed, 314 insertions, 245 deletions
diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java index f05f4c7..d4c4318 100644 --- a/core/java/android/app/ActionBar.java +++ b/core/java/android/app/ActionBar.java @@ -26,6 +26,7 @@ import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.ActionMode; import android.view.Gravity; +import android.view.KeyEvent; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; @@ -1013,6 +1014,26 @@ public abstract class ActionBar { return null; } + /** @hide */ + public boolean openOptionsMenu() { + return false; + } + + /** @hide */ + public boolean invalidateOptionsMenu() { + return false; + } + + /** @hide */ + public boolean onMenuKeyEvent(KeyEvent event) { + return false; + } + + /** @hide */ + public boolean collapseActionView() { + return false; + } + /** * Listener interface for ActionBar navigation events. * diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 4c32989..23b5f29 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -2083,7 +2083,8 @@ public class Activity extends ContextThemeWrapper "by the window decor. Do not request Window.FEATURE_ACTION_BAR and set " + "android:windowActionBar to false in your theme to use a Toolbar instead."); } - mActionBar = new ToolbarActionBar(toolbar); + mActionBar = new ToolbarActionBar(toolbar, getTitle(), this); + mActionBar.invalidateOptionsMenu(); } /** @@ -2449,6 +2450,10 @@ public class Activity extends ContextThemeWrapper * but you can override this to do whatever you want. */ public void onBackPressed() { + if (mActionBar != null && mActionBar.collapseActionView()) { + return; + } + if (!mFragments.popBackStackImmediate()) { finishAfterTransition(); } @@ -2660,6 +2665,14 @@ public class Activity extends ContextThemeWrapper */ public boolean dispatchKeyEvent(KeyEvent event) { onUserInteraction(); + + // Let action bars open menus in response to the menu key prioritized over + // the window handling it + if (event.getKeyCode() == KeyEvent.KEYCODE_MENU && + mActionBar != null && mActionBar.onMenuKeyEvent(event)) { + return true; + } + Window win = getWindow(); if (win.superDispatchKeyEvent(event)) { return true; @@ -2907,7 +2920,9 @@ public class Activity extends ContextThemeWrapper * time it needs to be displayed. */ public void invalidateOptionsMenu() { - mWindow.invalidatePanelMenu(Window.FEATURE_OPTIONS_PANEL); + if (mActionBar == null || !mActionBar.invalidateOptionsMenu()) { + mWindow.invalidatePanelMenu(Window.FEATURE_OPTIONS_PANEL); + } } /** @@ -3117,7 +3132,9 @@ public class Activity extends ContextThemeWrapper * open, this method does nothing. */ public void openOptionsMenu() { - mWindow.openPanel(Window.FEATURE_OPTIONS_PANEL, null); + if (mActionBar == null || !mActionBar.openOptionsMenu()) { + mWindow.openPanel(Window.FEATURE_OPTIONS_PANEL, null); + } } /** diff --git a/core/java/android/widget/ActionMenuView.java b/core/java/android/widget/ActionMenuView.java index a9a5eae..acee592 100644 --- a/core/java/android/widget/ActionMenuView.java +++ b/core/java/android/widget/ActionMenuView.java @@ -570,9 +570,9 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo mMenu = new MenuBuilder(context); mMenu.setCallback(new MenuBuilderCallback()); mPresenter = new ActionMenuPresenter(context); - mPresenter.setMenuView(this); mPresenter.setCallback(new ActionMenuPresenterCallback()); mMenu.addMenuPresenter(mPresenter); + mPresenter.setMenuView(this); } return mMenu; @@ -652,6 +652,11 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo return false; } + /** @hide */ + public void setExpandedActionViewsExclusive(boolean exclusive) { + mPresenter.setExpandedActionViewsExclusive(exclusive); + } + /** * Interface responsible for receiving menu item click events if the items themselves * do not have individual item click listeners. diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java index c41266e..419c582 100644 --- a/core/java/android/widget/Toolbar.java +++ b/core/java/android/widget/Toolbar.java @@ -222,7 +222,7 @@ public class Toolbar extends ViewGroup { final CharSequence subtitle = a.getText(R.styleable.Toolbar_subtitle); if (!TextUtils.isEmpty(subtitle)) { - setSubtitle(title); + setSubtitle(subtitle); } a.recycle(); } @@ -705,10 +705,23 @@ public class Toolbar extends ViewGroup { * @return The toolbar's Menu */ public Menu getMenu() { - ensureMenuView(); + ensureMenu(); return mMenuView.getMenu(); } + private void ensureMenu() { + ensureMenuView(); + if (mMenuView.peekMenu() == null) { + // Initialize a new menu for the first time. + final MenuBuilder menu = (MenuBuilder) mMenuView.getMenu(); + if (mExpandedMenuPresenter == null) { + mExpandedMenuPresenter = new ExpandedActionViewMenuPresenter(); + } + mMenuView.setExpandedActionViewsExclusive(true); + menu.addMenuPresenter(mExpandedMenuPresenter); + } + } + private void ensureMenuView() { if (mMenuView == null) { mMenuView = new ActionMenuView(getContext()); diff --git a/core/java/com/android/internal/app/ToolbarActionBar.java b/core/java/com/android/internal/app/ToolbarActionBar.java index afb6f7c..6056bf2 100644 --- a/core/java/com/android/internal/app/ToolbarActionBar.java +++ b/core/java/com/android/internal/app/ToolbarActionBar.java @@ -23,37 +23,50 @@ import android.content.Context; import android.content.res.Configuration; import android.graphics.drawable.Drawable; import android.view.ActionMode; +import android.view.KeyEvent; import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; import android.view.View; +import android.view.Window; import android.widget.SpinnerAdapter; import android.widget.Toolbar; +import com.android.internal.view.menu.MenuBuilder; +import com.android.internal.widget.DecorToolbar; +import com.android.internal.widget.ToolbarWidgetWrapper; import java.util.ArrayList; -import java.util.Map; public class ToolbarActionBar extends ActionBar { private Toolbar mToolbar; - private View mCustomView; - - private int mDisplayOptions; - - private int mNavResId; - private int mIconResId; - private int mLogoResId; - private Drawable mNavDrawable; - private Drawable mIconDrawable; - private Drawable mLogoDrawable; - private int mTitleResId; - private int mSubtitleResId; - private CharSequence mTitle; - private CharSequence mSubtitle; + private DecorToolbar mDecorToolbar; + private Window.Callback mWindowCallback; private boolean mLastMenuVisibility; private ArrayList<OnMenuVisibilityListener> mMenuVisibilityListeners = new ArrayList<OnMenuVisibilityListener>(); - public ToolbarActionBar(Toolbar toolbar) { + private final Runnable mMenuInvalidator = new Runnable() { + @Override + public void run() { + populateOptionsMenu(); + } + }; + + private final Toolbar.OnMenuItemClickListener mMenuClicker = + new Toolbar.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + return mWindowCallback.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, item); + } + }; + + public ToolbarActionBar(Toolbar toolbar, CharSequence title, Window.Callback windowCallback) { mToolbar = toolbar; + mDecorToolbar = new ToolbarWidgetWrapper(toolbar); + mWindowCallback = windowCallback; + toolbar.setOnMenuItemClickListener(mMenuClicker); + mDecorToolbar.setWindowTitle(title); } @Override @@ -63,19 +76,8 @@ public class ToolbarActionBar extends ActionBar { @Override public void setCustomView(View view, LayoutParams layoutParams) { - if (mCustomView != null) { - mToolbar.removeView(mCustomView); - } - mCustomView = view; - if (view != null) { - mToolbar.addView(view, generateLayoutParams(layoutParams)); - } - } - - private Toolbar.LayoutParams generateLayoutParams(LayoutParams lp) { - final Toolbar.LayoutParams result = new Toolbar.LayoutParams(lp); - result.gravity = lp.gravity; - return result; + view.setLayoutParams(layoutParams); + mDecorToolbar.setCustomView(view); } @Override @@ -86,48 +88,22 @@ public class ToolbarActionBar extends ActionBar { @Override public void setIcon(int resId) { - mIconResId = resId; - mIconDrawable = null; - updateToolbarLogo(); + mDecorToolbar.setIcon(resId); } @Override public void setIcon(Drawable icon) { - mIconResId = 0; - mIconDrawable = icon; - updateToolbarLogo(); + mDecorToolbar.setIcon(icon); } @Override public void setLogo(int resId) { - mLogoResId = resId; - mLogoDrawable = null; - updateToolbarLogo(); + mDecorToolbar.setLogo(resId); } @Override public void setLogo(Drawable logo) { - mLogoResId = 0; - mLogoDrawable = logo; - updateToolbarLogo(); - } - - private void updateToolbarLogo() { - Drawable drawable = null; - if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_HOME) != 0) { - final int resId; - if ((mDisplayOptions & ActionBar.DISPLAY_USE_LOGO) != 0) { - resId = mLogoResId; - drawable = mLogoDrawable; - } else { - resId = mIconResId; - drawable = mIconDrawable; - } - if (resId != 0) { - drawable = mToolbar.getContext().getDrawable(resId); - } - } - mToolbar.setLogo(drawable); + mDecorToolbar.setLogo(logo); } @Override @@ -219,42 +195,22 @@ public class ToolbarActionBar extends ActionBar { @Override public void setTitle(CharSequence title) { - mTitle = title; - mTitleResId = 0; - updateToolbarTitle(); + mDecorToolbar.setTitle(title); } @Override public void setTitle(int resId) { - mTitleResId = resId; - mTitle = null; - updateToolbarTitle(); + mDecorToolbar.setTitle(resId != 0 ? mDecorToolbar.getContext().getText(resId) : null); } @Override public void setSubtitle(CharSequence subtitle) { - mSubtitle = subtitle; - mSubtitleResId = 0; - updateToolbarTitle(); + mDecorToolbar.setSubtitle(subtitle); } @Override public void setSubtitle(int resId) { - mSubtitleResId = resId; - mSubtitle = null; - updateToolbarTitle(); - } - - private void updateToolbarTitle() { - final Context context = mToolbar.getContext(); - CharSequence title = null; - CharSequence subtitle = null; - if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0) { - title = mTitleResId != 0 ? context.getText(mTitleResId) : mTitle; - subtitle = mSubtitleResId != 0 ? context.getText(mSubtitleResId) : mSubtitle; - } - mToolbar.setTitle(title); - mToolbar.setSubtitle(subtitle); + mDecorToolbar.setSubtitle(resId != 0 ? mDecorToolbar.getContext().getText(resId) : null); } @Override @@ -264,9 +220,8 @@ public class ToolbarActionBar extends ActionBar { @Override public void setDisplayOptions(@DisplayOptions int options, @DisplayOptions int mask) { - final int oldOptions = mDisplayOptions; - mDisplayOptions = (options & mask) | (mDisplayOptions & ~mask); - final int optionsChanged = oldOptions ^ mDisplayOptions; + mDecorToolbar.setDisplayOptions((options & mask) | + mDecorToolbar.getDisplayOptions() & ~mask); } @Override @@ -301,7 +256,7 @@ public class ToolbarActionBar extends ActionBar { @Override public View getCustomView() { - return mCustomView; + return mDecorToolbar.getCustomView(); } @Override @@ -327,7 +282,7 @@ public class ToolbarActionBar extends ActionBar { @Override public int getDisplayOptions() { - return mDisplayOptions; + return mDecorToolbar.getDisplayOptions(); } @Override @@ -425,6 +380,54 @@ public class ToolbarActionBar extends ActionBar { return mToolbar.getVisibility() == View.VISIBLE; } + @Override + public boolean openOptionsMenu() { + return mToolbar.showOverflowMenu(); + } + + @Override + public boolean invalidateOptionsMenu() { + mToolbar.removeCallbacks(mMenuInvalidator); + mToolbar.postOnAnimation(mMenuInvalidator); + return true; + } + + @Override + public boolean collapseActionView() { + if (mToolbar.hasExpandedActionView()) { + mToolbar.collapseActionView(); + return true; + } + return false; + } + + void populateOptionsMenu() { + final Menu menu = mToolbar.getMenu(); + final MenuBuilder mb = menu instanceof MenuBuilder ? (MenuBuilder) menu : null; + if (mb != null) { + mb.stopDispatchingItemsChanged(); + } + try { + menu.clear(); + if (!mWindowCallback.onCreatePanelMenu(Window.FEATURE_OPTIONS_PANEL, menu) || + !mWindowCallback.onPreparePanel(Window.FEATURE_OPTIONS_PANEL, null, menu)) { + menu.clear(); + } + } finally { + if (mb != null) { + mb.startDispatchingItemsChanged(); + } + } + } + + @Override + public boolean onMenuKeyEvent(KeyEvent event) { + if (event.getAction() == KeyEvent.ACTION_UP) { + openOptionsMenu(); + } + return true; + } + public void addOnMenuVisibilityListener(OnMenuVisibilityListener listener) { mMenuVisibilityListeners.add(listener); } diff --git a/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java b/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java index b8dc307..3e15c32 100644 --- a/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java +++ b/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java @@ -24,6 +24,7 @@ import android.content.Context; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.os.Parcelable; +import android.text.TextUtils; import android.util.Log; import android.util.SparseArray; import android.view.LayoutInflater; @@ -80,16 +81,20 @@ public class ToolbarWidgetWrapper implements DecorToolbar { public ToolbarWidgetWrapper(Toolbar toolbar) { mToolbar = toolbar; + mTitle = toolbar.getTitle(); + mSubtitle = toolbar.getSubtitle(); + mTitleSet = !TextUtils.isEmpty(mTitle); + final TypedArray a = toolbar.getContext().obtainStyledAttributes(null, R.styleable.ActionBar, R.attr.actionBarStyle, 0); final CharSequence title = a.getText(R.styleable.ActionBar_title); - if (title != null) { + if (!TextUtils.isEmpty(title)) { setTitle(title); } final CharSequence subtitle = a.getText(R.styleable.ActionBar_subtitle); - if (subtitle != null) { + if (!TextUtils.isEmpty(subtitle)) { setSubtitle(subtitle); } diff --git a/core/res/res/layout/alert_dialog_quantum.xml b/core/res/res/layout/alert_dialog_quantum.xml index e109425..7fd22ad 100644 --- a/core/res/res/layout/alert_dialog_quantum.xml +++ b/core/res/res/layout/alert_dialog_quantum.xml @@ -23,7 +23,10 @@ android:orientation="vertical" android:background="@drawable/dialog_background_quantum" android:translationZ="@dimen/floating_window_z" - android:layout_margin="@dimen/floating_window_margin"> + android:layout_marginLeft="@dimen/floating_window_margin_left" + android:layout_marginTop="@dimen/floating_window_margin_top" + android:layout_marginRight="@dimen/floating_window_margin_right" + android:layout_marginBottom="@dimen/floating_window_margin_bottom"> <LinearLayout android:id="@+id/topPanel" android:layout_width="match_parent" @@ -44,7 +47,7 @@ android:scaleType="fitCenter" android:src="@null" /> <TextView android:id="@+id/alertTitle" - style="?android:attr/windowTitleStyle" + style="?attr/windowTitleStyle" android:singleLine="true" android:ellipsize="end" android:layout_width="match_parent" @@ -65,7 +68,7 @@ android:layout_height="wrap_content" android:clipToPadding="false"> <TextView android:id="@+id/message" - style="?android:attr/textAppearanceMedium" + style="?attr/textAppearanceMedium" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingStart="16dip" @@ -92,26 +95,24 @@ android:gravity="end" android:padding="16dip"> <LinearLayout - style="?android:attr/buttonBarStyle" + style="?attr/buttonBarStyle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layoutDirection="locale"> <Button android:id="@+id/button3" - style="?android:attr/buttonBarButtonStyle" + style="?attr/buttonBarButtonStyle" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginRight="8dip" android:maxLines="2" android:minHeight="@dimen/alert_dialog_button_bar_height" /> <Button android:id="@+id/button2" - style="?android:attr/buttonBarButtonStyle" + style="?attr/buttonBarButtonStyle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:maxLines="2" android:minHeight="@dimen/alert_dialog_button_bar_height" /> <Button android:id="@+id/button1" - style="?android:attr/buttonBarButtonStyle" - android:layout_marginLeft="8dip" + style="?attr/buttonBarButtonStyle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:maxLines="2" diff --git a/core/res/res/values/dimens_quantum.xml b/core/res/res/values/dimens_quantum.xml index 2defee2..b5ba1ca 100644 --- a/core/res/res/values/dimens_quantum.xml +++ b/core/res/res/values/dimens_quantum.xml @@ -52,7 +52,10 @@ <dimen name="text_size_small_quantum">14sp</dimen> <dimen name="floating_window_z">16dp</dimen> - <dimen name="floating_window_margin">32dp</dimen> + <dimen name="floating_window_margin_left">16dp</dimen> + <dimen name="floating_window_margin_top">8dp</dimen> + <dimen name="floating_window_margin_right">16dp</dimen> + <dimen name="floating_window_margin_bottom">32dp</dimen> <!-- the amount of elevation for pressed button state--> <dimen name="button_pressed_z">2dp</dimen> diff --git a/core/res/res/values/donottranslate_quantum.xml b/core/res/res/values/donottranslate_quantum.xml index 83cc4e5..e53c40e 100644 --- a/core/res/res/values/donottranslate_quantum.xml +++ b/core/res/res/values/donottranslate_quantum.xml @@ -27,6 +27,6 @@ <string name="font_family_body_1_quantum">sans-serif</string> <string name="font_family_caption_quantum">sans-serif</string> <string name="font_family_menu_quantum">sans-serif-medium</string> - <string name="font_family_button_quantum">sans-serif</string> + <string name="font_family_button_quantum">sans-serif-medium</string> </resources> diff --git a/core/res/res/values/styles_quantum.xml b/core/res/res/values/styles_quantum.xml index b55edf8..2f51048 100644 --- a/core/res/res/values/styles_quantum.xml +++ b/core/res/res/values/styles_quantum.xml @@ -404,7 +404,7 @@ please see styles_device_defaults.xml. <item name="textAppearance">?attr/textAppearanceButton</item> <item name="textColor">?attr/textColorPrimary</item> <item name="minHeight">48dip</item> - <item name="minWidth">96dip</item> + <item name="minWidth">88dip</item> <!-- TODO: Turn this back on when we support inset drawable outlines. --> <!-- <item name="stateListAnimator">@anim/button_state_list_anim_quantum</item> --> diff --git a/core/res/res/values/themes_quantum.xml b/core/res/res/values/themes_quantum.xml index 7d6bbdf..cdbd771 100644 --- a/core/res/res/values/themes_quantum.xml +++ b/core/res/res/values/themes_quantum.xml @@ -668,7 +668,7 @@ please see themes_device_defaults.xml. <item name="dividerVertical">?attr/listDivider</item> <item name="dividerHorizontal">?attr/listDivider</item> <item name="buttonBarStyle">@style/Widget.Quantum.Light.ButtonBar</item> - <item name="buttonBarButtonStyle">@style/Widget.Quantum.Light.Button.Borderless.Small</item> + <item name="buttonBarButtonStyle">@style/Widget.Quantum.Light.Button.Borderless</item> <item name="segmentedButtonStyle">@style/Widget.Quantum.Light.SegmentedButton</item> <!-- SearchView attributes --> @@ -1004,7 +1004,7 @@ please see themes_device_defaults.xml. <item name="colorBackgroundCacheHint">@null</item> <item name="buttonBarStyle">@style/Widget.Quantum.ButtonBar.AlertDialog</item> - <item name="borderlessButtonStyle">@style/Widget.Quantum.Button.Borderless.Small</item> + <item name="borderlessButtonStyle">@style/Widget.Quantum.Button.Borderless</item> <item name="textAppearance">@style/TextAppearance.Quantum</item> <item name="textAppearanceInverse">@style/TextAppearance.Quantum.Inverse</item> @@ -1123,7 +1123,7 @@ please see themes_device_defaults.xml. <item name="colorBackgroundCacheHint">@null</item> <item name="buttonBarStyle">@style/Widget.Quantum.Light.ButtonBar.AlertDialog</item> - <item name="borderlessButtonStyle">@style/Widget.Quantum.Light.Button.Borderless.Small</item> + <item name="borderlessButtonStyle">@style/Widget.Quantum.Light.Button.Borderless</item> <item name="textAppearance">@style/TextAppearance.Quantum</item> <item name="textAppearanceInverse">@style/TextAppearance.Quantum.Inverse</item> diff --git a/graphics/java/android/graphics/drawable/Ripple.java b/graphics/java/android/graphics/drawable/Ripple.java index 65b6814..ada741b 100644 --- a/graphics/java/android/graphics/drawable/Ripple.java +++ b/graphics/java/android/graphics/drawable/Ripple.java @@ -85,15 +85,13 @@ class Ripple { // Software rendering properties. private float mOuterOpacity = 0; private float mOpacity = 1; - private float mRadius = 0; private float mOuterX; private float mOuterY; - private float mX; - private float mY; // Values used to tween between the start and end positions. - private float mXGravity = 0; - private float mYGravity = 0; + private float mTweenRadius = 0; + private float mTweenX = 0; + private float mTweenY = 0; /** Whether we should be drawing hardware animations. */ private boolean mHardwareAnimating; @@ -101,6 +99,9 @@ class Ripple { /** Whether we can use hardware acceleration for the exit animation. */ private boolean mCanUseHardware; + /** Whether we have an explicit maximum radius. */ + private boolean mHasMaxRadius; + /** * Creates a new ripple. */ @@ -115,6 +116,7 @@ class Ripple { mColor = color | 0xFF000000; if (maxRadius != RippleDrawable.RADIUS_AUTO) { + mHasMaxRadius = true; mOuterRadius = maxRadius; } else { final float halfWidth = mBounds.width() / 2.0f; @@ -127,13 +129,12 @@ class Ripple { mDensity = density; } - public void setRadius(float r) { - mRadius = r; - invalidateSelf(); - } - - public float getRadius() { - return mRadius; + public void onHotspotBoundsChanged() { + if (!mHasMaxRadius) { + final float halfWidth = mBounds.width() / 2.0f; + final float halfHeight = mBounds.height() / 2.0f; + mOuterRadius = (float) Math.sqrt(halfWidth * halfWidth + halfHeight * halfHeight); + } } public void setOpacity(float a) { @@ -154,40 +155,31 @@ class Ripple { return mOuterOpacity; } - public void setXGravity(float x) { - mXGravity = x; - invalidateSelf(); - } - - public float getXGravity() { - return mXGravity; - } - - public void setYGravity(float y) { - mYGravity = y; + public void setRadiusGravity(float r) { + mTweenRadius = r; invalidateSelf(); } - public float getYGravity() { - return mYGravity; + public float getRadiusGravity() { + return mTweenRadius; } - public void setX(float x) { - mX = x; + public void setXGravity(float x) { + mTweenX = x; invalidateSelf(); } - public float getX() { - return mX; + public float getXGravity() { + return mTweenX; } - public void setY(float y) { - mY = y; + public void setYGravity(float y) { + mTweenY = y; invalidateSelf(); } - public float getY() { - return mY; + public float getYGravity() { + return mTweenY; } /** @@ -249,12 +241,13 @@ class Ripple { } final int alpha = (int) (255 * mOpacity + 0.5f); - if (alpha > 0 && mRadius > 0) { - final float x = MathUtils.lerp(mStartingX - mBounds.exactCenterX(), mOuterX, mXGravity); - final float y = MathUtils.lerp(mStartingY - mBounds.exactCenterY(), mOuterY, mYGravity); + final float radius = MathUtils.lerp(0, mOuterRadius, mTweenRadius); + if (alpha > 0 && radius > 0) { + final float x = MathUtils.lerp(mStartingX - mBounds.exactCenterX(), mOuterX, mTweenX); + final float y = MathUtils.lerp(mStartingY - mBounds.exactCenterY(), mOuterY, mTweenY); p.setAlpha(alpha); p.setStyle(Style.FILL); - c.drawCircle(x, y, mRadius, p); + c.drawCircle(x, y, radius, p); hasContent = true; } @@ -264,17 +257,13 @@ class Ripple { } /** - * Returns the maximum bounds for this ripple. + * Returns the maximum bounds of the ripple relative to the ripple center. */ public void getBounds(Rect bounds) { final int outerX = (int) mOuterX; final int outerY = (int) mOuterY; final int r = (int) mOuterRadius; bounds.set(outerX - r, outerY - r, outerX + r, outerY + r); - - final int x = (int) mX; - final int y = (int) mY; - bounds.union(x - r, y - r, x + r, y + r); } /** @@ -290,14 +279,11 @@ class Ripple { * Starts the enter animation. */ public void enter() { - mX = mStartingX - mBounds.exactCenterX(); - mY = mStartingY - mBounds.exactCenterY(); - final int radiusDuration = (int) (1000 * Math.sqrt(mOuterRadius / WAVE_TOUCH_DOWN_ACCELERATION * mDensity) + 0.5); final int outerDuration = (int) (1000 * 1.0f / WAVE_OUTER_OPACITY_VELOCITY); - final ObjectAnimator radius = ObjectAnimator.ofFloat(this, "radius", 0, mOuterRadius); + final ObjectAnimator radius = ObjectAnimator.ofFloat(this, "radiusGravity", 1); radius.setAutoCancel(true); radius.setDuration(radiusDuration); radius.setInterpolator(LINEAR_INTERPOLATOR); @@ -335,12 +321,10 @@ class Ripple { public void exit() { cancelSoftwareAnimations(); - mX = MathUtils.lerp(mStartingX - mBounds.exactCenterX(), mOuterX, mXGravity); - mY = MathUtils.lerp(mStartingY - mBounds.exactCenterY(), mOuterY, mYGravity); - + final float radius = MathUtils.lerp(0, mOuterRadius, mTweenRadius); final float remaining; if (mAnimRadius != null && mAnimRadius.isRunning()) { - remaining = mOuterRadius - mRadius; + remaining = mOuterRadius - radius; } else { remaining = mOuterRadius; } @@ -368,6 +352,8 @@ class Ripple { int inflectionOpacity) { mPendingAnimations.clear(); + final float startX = MathUtils.lerp(mStartingX - mBounds.exactCenterX(), mOuterX, mTweenX); + final float startY = MathUtils.lerp(mStartingY - mBounds.exactCenterY(), mOuterY, mTweenY); final Paint outerPaint = new Paint(); outerPaint.setAntiAlias(true); outerPaint.setColor(mColor); @@ -378,68 +364,69 @@ class Ripple { mPropOuterX = CanvasProperty.createFloat(mOuterX); mPropOuterY = CanvasProperty.createFloat(mOuterY); + final float startRadius = MathUtils.lerp(0, mOuterRadius, mTweenRadius); final Paint paint = new Paint(); paint.setAntiAlias(true); paint.setColor(mColor); paint.setAlpha((int) (255 * mOpacity + 0.5f)); paint.setStyle(Style.FILL); mPropPaint = CanvasProperty.createPaint(paint); - mPropRadius = CanvasProperty.createFloat(mRadius); - mPropX = CanvasProperty.createFloat(mX); - mPropY = CanvasProperty.createFloat(mY); + mPropRadius = CanvasProperty.createFloat(startRadius); + mPropX = CanvasProperty.createFloat(startX); + mPropY = CanvasProperty.createFloat(startY); - final RenderNodeAnimator radius = new RenderNodeAnimator(mPropRadius, mOuterRadius); - radius.setDuration(radiusDuration); - radius.setInterpolator(LINEAR_INTERPOLATOR); + final RenderNodeAnimator radiusAnim = new RenderNodeAnimator(mPropRadius, mOuterRadius); + radiusAnim.setDuration(radiusDuration); + radiusAnim.setInterpolator(LINEAR_INTERPOLATOR); - final RenderNodeAnimator x = new RenderNodeAnimator(mPropX, mOuterX); - x.setDuration(radiusDuration); - x.setInterpolator(LINEAR_INTERPOLATOR); + final RenderNodeAnimator xAnim = new RenderNodeAnimator(mPropX, mOuterX); + xAnim.setDuration(radiusDuration); + xAnim.setInterpolator(LINEAR_INTERPOLATOR); - final RenderNodeAnimator y = new RenderNodeAnimator(mPropY, mOuterY); - y.setDuration(radiusDuration); - y.setInterpolator(LINEAR_INTERPOLATOR); + final RenderNodeAnimator yAnim = new RenderNodeAnimator(mPropY, mOuterY); + yAnim.setDuration(radiusDuration); + yAnim.setInterpolator(LINEAR_INTERPOLATOR); - final RenderNodeAnimator opacity = new RenderNodeAnimator(mPropPaint, + final RenderNodeAnimator opacityAnim = new RenderNodeAnimator(mPropPaint, RenderNodeAnimator.PAINT_ALPHA, 0); - opacity.setDuration(opacityDuration); - opacity.setInterpolator(LINEAR_INTERPOLATOR); + opacityAnim.setDuration(opacityDuration); + opacityAnim.setInterpolator(LINEAR_INTERPOLATOR); - final RenderNodeAnimator outerOpacity; + final RenderNodeAnimator outerOpacityAnim; if (outerInflection > 0) { // Outer opacity continues to increase for a bit. - outerOpacity = new RenderNodeAnimator( + outerOpacityAnim = new RenderNodeAnimator( mPropOuterPaint, RenderNodeAnimator.PAINT_ALPHA, inflectionOpacity); - outerOpacity.setDuration(outerInflection); - outerOpacity.setInterpolator(LINEAR_INTERPOLATOR); + outerOpacityAnim.setDuration(outerInflection); + outerOpacityAnim.setInterpolator(LINEAR_INTERPOLATOR); // Chain the outer opacity exit animation. final int outerDuration = opacityDuration - outerInflection; if (outerDuration > 0) { - final RenderNodeAnimator outerFadeOut = new RenderNodeAnimator( + final RenderNodeAnimator outerFadeOutAnim = new RenderNodeAnimator( mPropOuterPaint, RenderNodeAnimator.PAINT_ALPHA, 0); - outerFadeOut.setDuration(outerDuration); - outerFadeOut.setInterpolator(LINEAR_INTERPOLATOR); - outerFadeOut.setStartDelay(outerInflection); - outerFadeOut.addListener(mAnimationListener); + outerFadeOutAnim.setDuration(outerDuration); + outerFadeOutAnim.setInterpolator(LINEAR_INTERPOLATOR); + outerFadeOutAnim.setStartDelay(outerInflection); + outerFadeOutAnim.addListener(mAnimationListener); - mPendingAnimations.add(outerFadeOut); + mPendingAnimations.add(outerFadeOutAnim); } else { - outerOpacity.addListener(mAnimationListener); + outerOpacityAnim.addListener(mAnimationListener); } } else { - outerOpacity = new RenderNodeAnimator( + outerOpacityAnim = new RenderNodeAnimator( mPropOuterPaint, RenderNodeAnimator.PAINT_ALPHA, 0); - outerOpacity.setInterpolator(LINEAR_INTERPOLATOR); - outerOpacity.setDuration(opacityDuration); - outerOpacity.addListener(mAnimationListener); + outerOpacityAnim.setInterpolator(LINEAR_INTERPOLATOR); + outerOpacityAnim.setDuration(opacityDuration); + outerOpacityAnim.addListener(mAnimationListener); } - mPendingAnimations.add(radius); - mPendingAnimations.add(opacity); - mPendingAnimations.add(outerOpacity); - mPendingAnimations.add(x); - mPendingAnimations.add(y); + mPendingAnimations.add(radiusAnim); + mPendingAnimations.add(opacityAnim); + mPendingAnimations.add(outerOpacityAnim); + mPendingAnimations.add(xAnim); + mPendingAnimations.add(yAnim); mHardwareAnimating = true; @@ -448,51 +435,51 @@ class Ripple { private void exitSoftware(int radiusDuration, int opacityDuration, int outerInflection, int inflectionOpacity) { - final ObjectAnimator radius = ObjectAnimator.ofFloat(this, "radius", mOuterRadius); - radius.setAutoCancel(true); - radius.setDuration(radiusDuration); - radius.setInterpolator(LINEAR_INTERPOLATOR); - - final ObjectAnimator x = ObjectAnimator.ofFloat(this, "x", mOuterX); - x.setAutoCancel(true); - x.setDuration(radiusDuration); - x.setInterpolator(LINEAR_INTERPOLATOR); - - final ObjectAnimator y = ObjectAnimator.ofFloat(this, "y", mOuterY); - y.setAutoCancel(true); - y.setDuration(radiusDuration); - y.setInterpolator(LINEAR_INTERPOLATOR); - - final ObjectAnimator opacity = ObjectAnimator.ofFloat(this, "opacity", 0); - opacity.setAutoCancel(true); - opacity.setDuration(opacityDuration); - opacity.setInterpolator(LINEAR_INTERPOLATOR); - - final ObjectAnimator outerOpacity; + final ObjectAnimator radiusAnim = ObjectAnimator.ofFloat(this, "radiusGravity", 1); + radiusAnim.setAutoCancel(true); + radiusAnim.setDuration(radiusDuration); + radiusAnim.setInterpolator(LINEAR_INTERPOLATOR); + + final ObjectAnimator xAnim = ObjectAnimator.ofFloat(this, "xGravity", 1); + xAnim.setAutoCancel(true); + xAnim.setDuration(radiusDuration); + xAnim.setInterpolator(LINEAR_INTERPOLATOR); + + final ObjectAnimator yAnim = ObjectAnimator.ofFloat(this, "yGravity", 1); + yAnim.setAutoCancel(true); + yAnim.setDuration(radiusDuration); + yAnim.setInterpolator(LINEAR_INTERPOLATOR); + + final ObjectAnimator opacityAnim = ObjectAnimator.ofFloat(this, "opacity", 0); + opacityAnim.setAutoCancel(true); + opacityAnim.setDuration(opacityDuration); + opacityAnim.setInterpolator(LINEAR_INTERPOLATOR); + + final ObjectAnimator outerOpacityAnim; if (outerInflection > 0) { // Outer opacity continues to increase for a bit. - outerOpacity = ObjectAnimator.ofFloat(this, + outerOpacityAnim = ObjectAnimator.ofFloat(this, "outerOpacity", inflectionOpacity / 255.0f); - outerOpacity.setAutoCancel(true); - outerOpacity.setDuration(outerInflection); - outerOpacity.setInterpolator(LINEAR_INTERPOLATOR); + outerOpacityAnim.setAutoCancel(true); + outerOpacityAnim.setDuration(outerInflection); + outerOpacityAnim.setInterpolator(LINEAR_INTERPOLATOR); // Chain the outer opacity exit animation. final int outerDuration = opacityDuration - outerInflection; if (outerDuration > 0) { - outerOpacity.addListener(new AnimatorListenerAdapter() { + outerOpacityAnim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - final ObjectAnimator outerFadeOut = ObjectAnimator.ofFloat(Ripple.this, + final ObjectAnimator outerFadeOutAnim = ObjectAnimator.ofFloat(Ripple.this, "outerOpacity", 0); - outerFadeOut.setAutoCancel(true); - outerFadeOut.setDuration(outerDuration); - outerFadeOut.setInterpolator(LINEAR_INTERPOLATOR); - outerFadeOut.addListener(mAnimationListener); + outerFadeOutAnim.setAutoCancel(true); + outerFadeOutAnim.setDuration(outerDuration); + outerFadeOutAnim.setInterpolator(LINEAR_INTERPOLATOR); + outerFadeOutAnim.addListener(mAnimationListener); - mAnimOuterOpacity = outerFadeOut; + mAnimOuterOpacity = outerFadeOutAnim; - outerFadeOut.start(); + outerFadeOutAnim.start(); } @Override @@ -501,26 +488,26 @@ class Ripple { } }); } else { - outerOpacity.addListener(mAnimationListener); + outerOpacityAnim.addListener(mAnimationListener); } } else { - outerOpacity = ObjectAnimator.ofFloat(this, "outerOpacity", 0); - outerOpacity.setAutoCancel(true); - outerOpacity.setDuration(opacityDuration); - outerOpacity.addListener(mAnimationListener); + outerOpacityAnim = ObjectAnimator.ofFloat(this, "outerOpacity", 0); + outerOpacityAnim.setAutoCancel(true); + outerOpacityAnim.setDuration(opacityDuration); + outerOpacityAnim.addListener(mAnimationListener); } - mAnimRadius = radius; - mAnimOpacity = opacity; - mAnimOuterOpacity = outerOpacity; - mAnimX = opacity; - mAnimY = opacity; - - radius.start(); - opacity.start(); - outerOpacity.start(); - x.start(); - y.start(); + mAnimRadius = radiusAnim; + mAnimOpacity = opacityAnim; + mAnimOuterOpacity = outerOpacityAnim; + mAnimX = opacityAnim; + mAnimY = opacityAnim; + + radiusAnim.start(); + opacityAnim.start(); + outerOpacityAnim.start(); + xAnim.start(); + yAnim.start(); } /** diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java index 4e786f0..9d7a8b6 100644 --- a/graphics/java/android/graphics/drawable/RippleDrawable.java +++ b/graphics/java/android/graphics/drawable/RippleDrawable.java @@ -213,6 +213,7 @@ public class RippleDrawable extends LayerDrawable { if (!mOverrideBounds) { mHotspotBounds.set(bounds); + onHotspotBoundsChanged(); } invalidateSelf(); @@ -452,6 +453,19 @@ public class RippleDrawable extends LayerDrawable { public void setHotspotBounds(int left, int top, int right, int bottom) { mOverrideBounds = true; mHotspotBounds.set(left, top, right, bottom); + + onHotspotBoundsChanged(); + } + + /** + * Notifies all the animating ripples that the hotspot bounds have changed. + */ + private void onHotspotBoundsChanged() { + final int count = mAnimatingRipplesCount; + final Ripple[] ripples = mAnimatingRipples; + for (int i = 0; i < count; i++) { + ripples[i].onHotspotBoundsChanged(); + } } @Override |