summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/ActionBar.java21
-rw-r--r--core/java/android/app/Activity.java23
-rw-r--r--core/java/android/widget/ActionMenuView.java7
-rw-r--r--core/java/android/widget/Toolbar.java17
-rw-r--r--core/java/com/android/internal/app/ToolbarActionBar.java179
-rw-r--r--core/java/com/android/internal/widget/ToolbarWidgetWrapper.java9
-rw-r--r--core/res/res/layout/alert_dialog_quantum.xml19
-rw-r--r--core/res/res/values/dimens_quantum.xml5
-rw-r--r--core/res/res/values/donottranslate_quantum.xml2
-rw-r--r--core/res/res/values/styles_quantum.xml2
-rw-r--r--core/res/res/values/themes_quantum.xml6
-rw-r--r--graphics/java/android/graphics/drawable/Ripple.java255
-rw-r--r--graphics/java/android/graphics/drawable/RippleDrawable.java14
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