diff options
260 files changed, 6200 insertions, 1303 deletions
diff --git a/api/current.txt b/api/current.txt index 852312b200e5..ae4be123da06 100644 --- a/api/current.txt +++ b/api/current.txt @@ -246,6 +246,7 @@ package android { field public static final int actionBarTabBarStyle = 16843508; // 0x10102f4 field public static final int actionBarTabStyle = 16843507; // 0x10102f3 field public static final int actionBarTabTextStyle = 16843509; // 0x10102f5 + field public static final int actionBarTheme = 16843836; // 0x101043c field public static final int actionBarWidgetTheme = 16843671; // 0x1010397 field public static final int actionButtonStyle = 16843480; // 0x10102d8 field public static final int actionDropDownStyle = 16843479; // 0x10102d7 @@ -1020,6 +1021,7 @@ package android { field public static final int shrinkColumns = 16843082; // 0x101014a field public static final deprecated int singleLine = 16843101; // 0x101015d field public static final int singleUser = 16843711; // 0x10103bf + field public static final int slideEdge = 16843835; // 0x101043b field public static final int smallIcon = 16843422; // 0x101029e field public static final int smallScreens = 16843396; // 0x1010284 field public static final int smoothScrollbar = 16843313; // 0x1010231 @@ -1075,6 +1077,7 @@ package android { field public static final int strokeOpacity = 16843812; // 0x1010424 field public static final int strokeWidth = 16843813; // 0x1010425 field public static final int subtitle = 16843473; // 0x10102d1 + field public static final int subtitleTextAppearance = 16843834; // 0x101043a field public static final int subtitleTextStyle = 16843513; // 0x10102f9 field public static final int subtypeExtraValue = 16843674; // 0x101039a field public static final int subtypeId = 16843713; // 0x10103c1 @@ -1190,6 +1193,7 @@ package android { field public static final int tintMode = 16843798; // 0x1010416 field public static final int title = 16843233; // 0x10101e1 field public static final int titleCondensed = 16843234; // 0x10101e2 + field public static final int titleTextAppearance = 16843833; // 0x1010439 field public static final int titleTextStyle = 16843512; // 0x10102f8 field public static final int toAlpha = 16843211; // 0x10101cb field public static final int toDegrees = 16843188; // 0x10101b4 @@ -3017,30 +3021,30 @@ package android.app { public abstract class ActionBar { ctor public ActionBar(); method public abstract void addOnMenuVisibilityListener(android.app.ActionBar.OnMenuVisibilityListener); - method public abstract void addTab(android.app.ActionBar.Tab); - method public abstract void addTab(android.app.ActionBar.Tab, boolean); - method public abstract void addTab(android.app.ActionBar.Tab, int); - method public abstract void addTab(android.app.ActionBar.Tab, int, boolean); + method public abstract deprecated void addTab(android.app.ActionBar.Tab); + method public abstract deprecated void addTab(android.app.ActionBar.Tab, boolean); + method public abstract deprecated void addTab(android.app.ActionBar.Tab, int); + method public abstract deprecated void addTab(android.app.ActionBar.Tab, int, boolean); method public abstract android.view.View getCustomView(); method public abstract int getDisplayOptions(); method public abstract int getHeight(); - method public abstract int getNavigationItemCount(); - method public abstract int getNavigationMode(); - method public abstract int getSelectedNavigationIndex(); - method public abstract android.app.ActionBar.Tab getSelectedTab(); + method public abstract deprecated int getNavigationItemCount(); + method public abstract deprecated int getNavigationMode(); + method public abstract deprecated int getSelectedNavigationIndex(); + method public abstract deprecated android.app.ActionBar.Tab getSelectedTab(); method public abstract java.lang.CharSequence getSubtitle(); - method public abstract android.app.ActionBar.Tab getTabAt(int); - method public abstract int getTabCount(); + method public abstract deprecated android.app.ActionBar.Tab getTabAt(int); + method public abstract deprecated int getTabCount(); method public android.content.Context getThemedContext(); method public abstract java.lang.CharSequence getTitle(); method public abstract void hide(); method public abstract boolean isShowing(); - method public abstract android.app.ActionBar.Tab newTab(); - method public abstract void removeAllTabs(); + method public abstract deprecated android.app.ActionBar.Tab newTab(); + method public abstract deprecated void removeAllTabs(); method public abstract void removeOnMenuVisibilityListener(android.app.ActionBar.OnMenuVisibilityListener); - method public abstract void removeTab(android.app.ActionBar.Tab); - method public abstract void removeTabAt(int); - method public abstract void selectTab(android.app.ActionBar.Tab); + method public abstract deprecated void removeTab(android.app.ActionBar.Tab); + method public abstract deprecated void removeTabAt(int); + method public abstract deprecated void selectTab(android.app.ActionBar.Tab); method public abstract void setBackgroundDrawable(android.graphics.drawable.Drawable); method public abstract void setCustomView(android.view.View); method public abstract void setCustomView(android.view.View, android.app.ActionBar.LayoutParams); @@ -3059,11 +3063,11 @@ package android.app { method public void setHomeButtonEnabled(boolean); method public abstract void setIcon(int); method public abstract void setIcon(android.graphics.drawable.Drawable); - method public abstract void setListNavigationCallbacks(android.widget.SpinnerAdapter, android.app.ActionBar.OnNavigationListener); + method public abstract deprecated void setListNavigationCallbacks(android.widget.SpinnerAdapter, android.app.ActionBar.OnNavigationListener); method public abstract void setLogo(int); method public abstract void setLogo(android.graphics.drawable.Drawable); - method public abstract void setNavigationMode(int); - method public abstract void setSelectedNavigationItem(int); + method public abstract deprecated void setNavigationMode(int); + method public abstract deprecated void setSelectedNavigationItem(int); method public void setSplitBackgroundDrawable(android.graphics.drawable.Drawable); method public void setStackedBackgroundDrawable(android.graphics.drawable.Drawable); method public abstract void setSubtitle(java.lang.CharSequence); @@ -3076,9 +3080,9 @@ package android.app { field public static final int DISPLAY_SHOW_HOME = 2; // 0x2 field public static final int DISPLAY_SHOW_TITLE = 8; // 0x8 field public static final int DISPLAY_USE_LOGO = 1; // 0x1 - field public static final int NAVIGATION_MODE_LIST = 1; // 0x1 - field public static final int NAVIGATION_MODE_STANDARD = 0; // 0x0 - field public static final int NAVIGATION_MODE_TABS = 2; // 0x2 + field public static final deprecated int NAVIGATION_MODE_LIST = 1; // 0x1 + field public static final deprecated int NAVIGATION_MODE_STANDARD = 0; // 0x0 + field public static final deprecated int NAVIGATION_MODE_TABS = 2; // 0x2 } public static class ActionBar.LayoutParams extends android.view.ViewGroup.MarginLayoutParams { @@ -3088,6 +3092,7 @@ package android.app { ctor public ActionBar.LayoutParams(int); ctor public ActionBar.LayoutParams(android.app.ActionBar.LayoutParams); ctor public ActionBar.LayoutParams(android.view.ViewGroup.LayoutParams); + ctor public ActionBar.LayoutParams(android.view.ViewGroup.MarginLayoutParams); field public int gravity; } @@ -3095,11 +3100,11 @@ package android.app { method public abstract void onMenuVisibilityChanged(boolean); } - public static abstract interface ActionBar.OnNavigationListener { + public static abstract deprecated interface ActionBar.OnNavigationListener { method public abstract boolean onNavigationItemSelected(int, long); } - public static abstract class ActionBar.Tab { + public static abstract deprecated class ActionBar.Tab { ctor public ActionBar.Tab(); method public abstract java.lang.CharSequence getContentDescription(); method public abstract android.view.View getCustomView(); @@ -3121,7 +3126,7 @@ package android.app { field public static final int INVALID_POSITION = -1; // 0xffffffff } - public static abstract interface ActionBar.TabListener { + public static abstract deprecated interface ActionBar.TabListener { method public abstract void onTabReselected(android.app.ActionBar.Tab, android.app.FragmentTransaction); method public abstract void onTabSelected(android.app.ActionBar.Tab, android.app.FragmentTransaction); method public abstract void onTabUnselected(android.app.ActionBar.Tab, android.app.FragmentTransaction); @@ -3147,6 +3152,7 @@ package android.app { method public void finishActivityFromChild(android.app.Activity, int); method public void finishAffinity(); method public void finishFromChild(android.app.Activity); + method public void finishWithTransition(); method public android.app.ActionBar getActionBar(); method public final android.app.Application getApplication(); method public android.content.ComponentName getCallingActivity(); @@ -3263,6 +3269,7 @@ package android.app { method public void reportFullyDrawn(); method public final boolean requestWindowFeature(int); method public final void runOnUiThread(java.lang.Runnable); + method public void setActionBar(android.widget.Toolbar); method public void setActivityLabelAndIcon(java.lang.CharSequence, android.graphics.Bitmap); method public void setContentTransitionManager(android.transition.TransitionManager); method public void setContentView(int); @@ -6836,6 +6843,8 @@ package android.content { field public static final java.lang.String ACTION_INSTALL_PACKAGE = "android.intent.action.INSTALL_PACKAGE"; field public static final java.lang.String ACTION_LOCALE_CHANGED = "android.intent.action.LOCALE_CHANGED"; field public static final java.lang.String ACTION_MAIN = "android.intent.action.MAIN"; + field public static final java.lang.String ACTION_MANAGED_PROFILE_ADDED = "android.intent.action.MANAGED_PROFILE_ADDED"; + field public static final java.lang.String ACTION_MANAGED_PROFILE_REMOVED = "android.intent.action.MANAGED_PROFILE_REMOVED"; field public static final java.lang.String ACTION_MANAGE_NETWORK_USAGE = "android.intent.action.MANAGE_NETWORK_USAGE"; field public static final java.lang.String ACTION_MANAGE_PACKAGE_STORAGE = "android.intent.action.MANAGE_PACKAGE_STORAGE"; field public static final java.lang.String ACTION_MEDIA_BAD_REMOVAL = "android.intent.action.MEDIA_BAD_REMOVAL"; @@ -6985,6 +6994,7 @@ package android.content { field public static final java.lang.String EXTRA_TEXT = "android.intent.extra.TEXT"; field public static final java.lang.String EXTRA_TITLE = "android.intent.extra.TITLE"; field public static final java.lang.String EXTRA_UID = "android.intent.extra.UID"; + field public static final java.lang.String EXTRA_USER = "android.intent.extra.user"; field public static final int FILL_IN_ACTION = 1; // 0x1 field public static final int FILL_IN_CATEGORIES = 4; // 0x4 field public static final int FILL_IN_CLIP_DATA = 128; // 0x80 @@ -8154,6 +8164,7 @@ package android.content.res { field public static final int UI_MODE_TYPE_NORMAL = 1; // 0x1 field public static final int UI_MODE_TYPE_TELEVISION = 4; // 0x4 field public static final int UI_MODE_TYPE_UNDEFINED = 0; // 0x0 + field public static final int UI_MODE_TYPE_WATCH = 6; // 0x6 field public int densityDpi; field public float fontScale; field public int hardKeyboardHidden; @@ -27398,6 +27409,16 @@ package android.transition { method public void setResizeClip(boolean); } + public class CircularPropagation extends android.transition.VisibilityPropagation { + ctor public CircularPropagation(); + method public long getStartDelay(android.view.ViewGroup, android.transition.Transition, android.transition.TransitionValues, android.transition.TransitionValues); + method public void setPropagationSpeed(float); + } + + public class Explode extends android.transition.Visibility { + ctor public Explode(); + } + public class Fade extends android.transition.Visibility { ctor public Fade(); ctor public Fade(int); @@ -27405,6 +27426,12 @@ package android.transition { field public static final int OUT = 2; // 0x2 } + public class MoveImage extends android.transition.Transition { + ctor public MoveImage(); + method public void captureEndValues(android.transition.TransitionValues); + method public void captureStartValues(android.transition.TransitionValues); + } + public final class Scene { ctor public Scene(android.view.ViewGroup); ctor public Scene(android.view.ViewGroup, android.view.View); @@ -27417,6 +27444,27 @@ package android.transition { method public void setExitAction(java.lang.Runnable); } + public class SidePropagation extends android.transition.VisibilityPropagation { + ctor public SidePropagation(); + method public long getStartDelay(android.view.ViewGroup, android.transition.Transition, android.transition.TransitionValues, android.transition.TransitionValues); + method public void setPropagationSpeed(float); + method public void setSide(int); + field public static final int BOTTOM = 3; // 0x3 + field public static final int LEFT = 0; // 0x0 + field public static final int RIGHT = 2; // 0x2 + field public static final int TOP = 1; // 0x1 + } + + public class Slide extends android.transition.Visibility { + ctor public Slide(); + ctor public Slide(int); + method public void setSlideEdge(int); + field public static final int BOTTOM = 3; // 0x3 + field public static final int LEFT = 0; // 0x0 + field public static final int RIGHT = 2; // 0x2 + field public static final int TOP = 1; // 0x1 + } + public abstract class Transition implements java.lang.Cloneable { ctor public Transition(); method public android.transition.Transition addListener(android.transition.Transition.TransitionListener); @@ -27434,8 +27482,11 @@ package android.transition { method public android.transition.Transition excludeTarget(android.view.View, boolean); method public android.transition.Transition excludeTarget(java.lang.Class, boolean); method public long getDuration(); + method public android.graphics.Rect getEpicenter(); + method public android.transition.Transition.EpicenterCallback getEpicenterCallback(); method public android.animation.TimeInterpolator getInterpolator(); method public java.lang.String getName(); + method public android.transition.TransitionPropagation getPropagation(); method public long getStartDelay(); method public java.util.List<java.lang.Integer> getTargetIds(); method public java.util.List<android.view.View> getTargets(); @@ -27445,10 +27496,17 @@ package android.transition { method public android.transition.Transition removeTarget(int); method public android.transition.Transition removeTarget(android.view.View); method public android.transition.Transition setDuration(long); + method public void setEpicenterCallback(android.transition.Transition.EpicenterCallback); method public android.transition.Transition setInterpolator(android.animation.TimeInterpolator); + method public void setPropagation(android.transition.TransitionPropagation); method public android.transition.Transition setStartDelay(long); } + public static abstract class Transition.EpicenterCallback { + ctor public Transition.EpicenterCallback(); + method public abstract android.graphics.Rect getEpicenter(android.transition.Transition); + } + public static abstract interface Transition.TransitionListener { method public abstract void onTransitionCancel(android.transition.Transition); method public abstract void onTransitionEnd(android.transition.Transition); @@ -27475,6 +27533,13 @@ package android.transition { method public void transitionTo(android.transition.Scene); } + public abstract class TransitionPropagation { + ctor public TransitionPropagation(); + method public abstract void captureValues(android.transition.TransitionValues); + method public abstract java.lang.String[] getPropagationProperties(); + method public abstract long getStartDelay(android.view.ViewGroup, android.transition.Transition, android.transition.TransitionValues, android.transition.TransitionValues); + } + public class TransitionSet extends android.transition.Transition { ctor public TransitionSet(); method public android.transition.TransitionSet addTransition(android.transition.Transition); @@ -27499,7 +27564,18 @@ package android.transition { method public void captureStartValues(android.transition.TransitionValues); method public boolean isVisible(android.transition.TransitionValues); method public android.animation.Animator onAppear(android.view.ViewGroup, android.transition.TransitionValues, int, android.transition.TransitionValues, int); + method public android.animation.Animator onAppear(android.view.ViewGroup, android.view.View, android.transition.TransitionValues, android.transition.TransitionValues); method public android.animation.Animator onDisappear(android.view.ViewGroup, android.transition.TransitionValues, int, android.transition.TransitionValues, int); + method public android.animation.Animator onDisappear(android.view.ViewGroup, android.view.View, android.transition.TransitionValues, android.transition.TransitionValues); + } + + public abstract class VisibilityPropagation extends android.transition.TransitionPropagation { + ctor public VisibilityPropagation(); + method public void captureValues(android.transition.TransitionValues); + method public java.lang.String[] getPropagationProperties(); + method public int getViewVisibility(android.transition.TransitionValues); + method public int getViewX(android.transition.TransitionValues); + method public int getViewY(android.transition.TransitionValues); } } @@ -30452,6 +30528,8 @@ package android.view { method public boolean requestFeature(int); method public abstract void restoreHierarchyState(android.os.Bundle); method public abstract android.os.Bundle saveHierarchyState(); + method public void setAllowOverlappingEnterTransition(boolean); + method public void setAllowOverlappingExitTransition(boolean); method public void setAttributes(android.view.WindowManager.LayoutParams); method public abstract void setBackgroundDrawable(android.graphics.drawable.Drawable); method public void setBackgroundDrawableResource(int); @@ -30480,7 +30558,6 @@ package android.view { method public abstract void setTitle(java.lang.CharSequence); method public abstract deprecated void setTitleColor(int); method public void setTransitionManager(android.transition.TransitionManager); - method public void setTriggerEarlyEnterTransition(boolean); method public void setType(int); method public void setUiOptions(int); method public void setUiOptions(int, int); @@ -32514,6 +32591,7 @@ package android.widget { method public android.view.Menu getMenu(); method public void onConfigurationChanged(android.content.res.Configuration); method public void onDetachedFromWindow(); + method public void setOnMenuItemClickListener(android.widget.ActionMenuView.OnMenuItemClickListener); } public static class ActionMenuView.LayoutParams extends android.widget.LinearLayout.LayoutParams { @@ -32523,6 +32601,10 @@ package android.widget { ctor public ActionMenuView.LayoutParams(int, int); } + public static abstract interface ActionMenuView.OnMenuItemClickListener { + method public abstract boolean onMenuItemClick(android.view.MenuItem); + } + public abstract interface Adapter { method public abstract int getCount(); method public abstract java.lang.Object getItem(int); @@ -34676,6 +34758,50 @@ package android.widget { method public void setTextOn(java.lang.CharSequence); } + public class Toolbar extends android.view.ViewGroup { + ctor public Toolbar(android.content.Context); + ctor public Toolbar(android.content.Context, android.util.AttributeSet); + ctor public Toolbar(android.content.Context, android.util.AttributeSet, int); + ctor public Toolbar(android.content.Context, android.util.AttributeSet, int, int); + method public android.graphics.drawable.Drawable getLogo(); + method public java.lang.CharSequence getLogoDescription(); + method public android.view.Menu getMenu(); + method public android.graphics.drawable.Drawable getNavigationIcon(); + method public java.lang.CharSequence getSubtitle(); + method public java.lang.CharSequence getTitle(); + method public void inflateMenu(int); + method protected void onLayout(boolean, int, int, int, int); + method public void setLogo(int); + method public void setLogo(android.graphics.drawable.Drawable); + method public void setLogoDescription(int); + method public void setLogoDescription(java.lang.CharSequence); + method public void setNavigationDescription(int); + method public void setNavigationDescription(java.lang.CharSequence); + method public void setNavigationIcon(int); + method public void setNavigationIcon(android.graphics.drawable.Drawable); + method public void setNavigationOnClickListener(android.view.View.OnClickListener); + method public void setOnMenuItemClickListener(android.widget.Toolbar.OnMenuItemClickListener); + method public void setSubtitle(int); + method public void setSubtitle(java.lang.CharSequence); + method public void setTitle(int); + method public void setTitle(java.lang.CharSequence); + } + + public static class Toolbar.LayoutParams extends android.view.ViewGroup.MarginLayoutParams { + ctor public Toolbar.LayoutParams(android.content.Context, android.util.AttributeSet); + ctor public Toolbar.LayoutParams(int, int); + ctor public Toolbar.LayoutParams(int, int, int); + ctor public Toolbar.LayoutParams(int); + ctor public Toolbar.LayoutParams(android.widget.Toolbar.LayoutParams); + ctor public Toolbar.LayoutParams(android.view.ViewGroup.MarginLayoutParams); + ctor public Toolbar.LayoutParams(android.view.ViewGroup.LayoutParams); + field public int gravity; + } + + public static abstract interface Toolbar.OnMenuItemClickListener { + method public abstract boolean onMenuItemClick(android.view.MenuItem); + } + public deprecated class TwoLineListItem extends android.widget.RelativeLayout { ctor public TwoLineListItem(android.content.Context); ctor public TwoLineListItem(android.content.Context, android.util.AttributeSet); diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java index fbe898771856..34b0f3a70332 100644 --- a/core/java/android/app/ActionBar.java +++ b/core/java/android/app/ActionBar.java @@ -20,9 +20,11 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import android.content.res.Configuration; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.util.AttributeSet; +import android.view.ActionMode; import android.view.Gravity; import android.view.View; import android.view.ViewDebug; @@ -30,31 +32,57 @@ import android.view.ViewGroup; import android.view.ViewGroup.MarginLayoutParams; import android.view.Window; import android.widget.SpinnerAdapter; +import android.widget.Toolbar; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Map; /** - * A window feature at the top of the activity that may display the activity title, navigation - * modes, and other interactive items. + * A primary toolbar within the activity that may display the activity title, application-level + * navigation affordances, and other interactive items. + * * <p>Beginning with Android 3.0 (API level 11), the action bar appears at the top of an * activity's window when the activity uses the system's {@link * android.R.style#Theme_Holo Holo} theme (or one of its descendant themes), which is the default. * You may otherwise add the action bar by calling {@link * android.view.Window#requestFeature requestFeature(FEATURE_ACTION_BAR)} or by declaring it in a * custom theme with the {@link android.R.styleable#Theme_windowActionBar windowActionBar} property. - * <p>By default, the action bar shows the application icon on + * </p> + * + * <p>Beginning with Android L (API level 21), the action bar may be represented by any + * Toolbar widget within the application layout. The application may signal to the Activity + * which Toolbar should be treated as the Activity's action bar. Activities that use this + * feature should use one of the supplied <code>.NoActionBar</code> themes, set the + * {@link android.R.styleable#Theme_windowActionBar windowActionBar} attribute to <code>false</code> + * or otherwise not request the window feature.</p> + * + * <p>By adjusting the window features requested by the theme and the layouts used for + * an Activity's content view, an app can use the standard system action bar on older platform + * releases and the newer inline toolbars on newer platform releases. The <code>ActionBar</code> + * object obtained from the Activity can be used to control either configuration transparently.</p> + * + * <p>When using the Holo themes the action bar shows the application icon on * the left, followed by the activity title. If your activity has an options menu, you can make * select items accessible directly from the action bar as "action items". You can also * modify various characteristics of the action bar or remove it completely.</p> + * + * <p>When using the Quantum themes (default in API 21 or newer) the navigation button + * (formerly "Home") takes over the space previously occupied by the application icon. + * Apps wishing to express a stronger branding should use their brand colors heavily + * in the action bar and other application chrome or use a {@link #setLogo(int) logo} + * in place of their standard title text.</p> + * * <p>From your activity, you can retrieve an instance of {@link ActionBar} by calling {@link * android.app.Activity#getActionBar getActionBar()}.</p> + * * <p>In some cases, the action bar may be overlayed by another bar that enables contextual actions, * using an {@link android.view.ActionMode}. For example, when the user selects one or more items in * your activity, you can enable an action mode that offers actions specific to the selected * items, with a UI that temporarily replaces the action bar. Although the UI may occupy the * same space, the {@link android.view.ActionMode} APIs are distinct and independent from those for - * {@link ActionBar}. + * {@link ActionBar}.</p> + * * <div class="special reference"> * <h3>Developer Guides</h3> * <p>For information about how to use the action bar, including how to add action items, navigation @@ -73,6 +101,11 @@ public abstract class ActionBar { * and title text with an optional subtitle. Clicking any of these elements * will dispatch onOptionsItemSelected to the host Activity with * a MenuItem with item ID android.R.id.home. + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public static final int NAVIGATION_MODE_STANDARD = 0; @@ -80,12 +113,22 @@ public abstract class ActionBar { * List navigation mode. Instead of static title text this mode * presents a list menu for navigation within the activity. * e.g. this might be presented to the user as a dropdown list. + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public static final int NAVIGATION_MODE_LIST = 1; /** * Tab navigation mode. Instead of static title text this mode * presents a series of tabs for navigation within the activity. + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public static final int NAVIGATION_MODE_TABS = 2; @@ -288,6 +331,11 @@ public abstract class ActionBar { * within the dropdown navigation menu. * @param callback An OnNavigationListener that will receive events when the user * selects a navigation item. + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public abstract void setListNavigationCallbacks(SpinnerAdapter adapter, OnNavigationListener callback); @@ -296,6 +344,11 @@ public abstract class ActionBar { * Set the selected navigation item in list or tabbed navigation modes. * * @param position Position of the item to select. + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public abstract void setSelectedNavigationItem(int position); @@ -303,6 +356,11 @@ public abstract class ActionBar { * Get the position of the selected navigation item in list or tabbed navigation modes. * * @return Position of the selected item. + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public abstract int getSelectedNavigationIndex(); @@ -310,6 +368,11 @@ public abstract class ActionBar { * Get the number of navigation items present in the current navigation mode. * * @return Number of navigation items. + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public abstract int getNavigationItemCount(); @@ -507,6 +570,11 @@ public abstract class ActionBar { * </ul> * * @return The current navigation mode. + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ @NavigationMode public abstract int getNavigationMode(); @@ -518,6 +586,11 @@ public abstract class ActionBar { * @see #NAVIGATION_MODE_STANDARD * @see #NAVIGATION_MODE_LIST * @see #NAVIGATION_MODE_TABS + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public abstract void setNavigationMode(@NavigationMode int mode); @@ -539,6 +612,11 @@ public abstract class ActionBar { * @return A new Tab * * @see #addTab(Tab) + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public abstract Tab newTab(); @@ -547,6 +625,11 @@ public abstract class ActionBar { * If this is the first tab to be added it will become the selected tab. * * @param tab Tab to add + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public abstract void addTab(Tab tab); @@ -555,6 +638,11 @@ public abstract class ActionBar { * * @param tab Tab to add * @param setSelected True if the added tab should become the selected tab. + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public abstract void addTab(Tab tab, boolean setSelected); @@ -565,6 +653,11 @@ public abstract class ActionBar { * * @param tab The tab to add * @param position The new position of the tab + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public abstract void addTab(Tab tab, int position); @@ -575,6 +668,11 @@ public abstract class ActionBar { * @param tab The tab to add * @param position The new position of the tab * @param setSelected True if the added tab should become the selected tab. + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public abstract void addTab(Tab tab, int position, boolean setSelected); @@ -583,6 +681,11 @@ public abstract class ActionBar { * and another tab will be selected if present. * * @param tab The tab to remove + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public abstract void removeTab(Tab tab); @@ -591,11 +694,21 @@ public abstract class ActionBar { * and another tab will be selected if present. * * @param position Position of the tab to remove + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public abstract void removeTabAt(int position); /** * Remove all tabs from the action bar and deselect the current tab. + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public abstract void removeAllTabs(); @@ -605,6 +718,11 @@ public abstract class ActionBar { * <p>Note: If you want to select by index, use {@link #setSelectedNavigationItem(int)}.</p> * * @param tab Tab to select + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public abstract void selectTab(Tab tab); @@ -613,6 +731,11 @@ public abstract class ActionBar { * one tab present. * * @return The currently selected tab or null + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public abstract Tab getSelectedTab(); @@ -621,12 +744,22 @@ public abstract class ActionBar { * * @param index Index value in the range 0-get * @return + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public abstract Tab getTabAt(int index); /** * Returns the number of tabs currently registered with the action bar. * @return Tab count + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public abstract int getTabCount(); @@ -799,8 +932,38 @@ public abstract class ActionBar { */ public void setHomeActionContentDescription(int resId) { } + /** @hide */ + public void setDefaultDisplayHomeAsUpEnabled(boolean enabled) { + } + + /** @hide */ + public void setShowHideAnimationEnabled(boolean enabled) { + } + + /** @hide */ + public void onConfigurationChanged(Configuration config) { + } + + /** @hide */ + public void dispatchMenuVisibilityChanged(boolean visible) { + } + + /** @hide */ + public void captureSharedElements(Map<String, View> sharedElements) { + } + + /** @hide */ + public ActionMode startActionMode(ActionMode.Callback callback) { + return null; + } + /** * Listener interface for ActionBar navigation events. + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public interface OnNavigationListener { /** @@ -833,6 +996,11 @@ public abstract class ActionBar { * A tab in the action bar. * * <p>Tabs manage the hiding and showing of {@link Fragment}s. + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public static abstract class Tab { /** @@ -984,6 +1152,11 @@ public abstract class ActionBar { /** * Callback interface invoked when a tab is focused, unfocused, added, or removed. + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public interface TabListener { /** @@ -1025,27 +1198,27 @@ public abstract class ActionBar { * * @attr ref android.R.styleable#ActionBar_LayoutParams_layout_gravity */ - public static class LayoutParams extends MarginLayoutParams { + public static class LayoutParams extends ViewGroup.MarginLayoutParams { /** * Gravity for the view associated with these LayoutParams. * * @see android.view.Gravity */ @ViewDebug.ExportedProperty(category = "layout", mapping = { - @ViewDebug.IntToString(from = -1, to = "NONE"), - @ViewDebug.IntToString(from = Gravity.NO_GRAVITY, to = "NONE"), - @ViewDebug.IntToString(from = Gravity.TOP, to = "TOP"), - @ViewDebug.IntToString(from = Gravity.BOTTOM, to = "BOTTOM"), - @ViewDebug.IntToString(from = Gravity.LEFT, to = "LEFT"), - @ViewDebug.IntToString(from = Gravity.RIGHT, to = "RIGHT"), - @ViewDebug.IntToString(from = Gravity.START, to = "START"), - @ViewDebug.IntToString(from = Gravity.END, to = "END"), - @ViewDebug.IntToString(from = Gravity.CENTER_VERTICAL, to = "CENTER_VERTICAL"), - @ViewDebug.IntToString(from = Gravity.FILL_VERTICAL, to = "FILL_VERTICAL"), - @ViewDebug.IntToString(from = Gravity.CENTER_HORIZONTAL, to = "CENTER_HORIZONTAL"), - @ViewDebug.IntToString(from = Gravity.FILL_HORIZONTAL, to = "FILL_HORIZONTAL"), - @ViewDebug.IntToString(from = Gravity.CENTER, to = "CENTER"), - @ViewDebug.IntToString(from = Gravity.FILL, to = "FILL") + @ViewDebug.IntToString(from = -1, to = "NONE"), + @ViewDebug.IntToString(from = Gravity.NO_GRAVITY, to = "NONE"), + @ViewDebug.IntToString(from = Gravity.TOP, to = "TOP"), + @ViewDebug.IntToString(from = Gravity.BOTTOM, to = "BOTTOM"), + @ViewDebug.IntToString(from = Gravity.LEFT, to = "LEFT"), + @ViewDebug.IntToString(from = Gravity.RIGHT, to = "RIGHT"), + @ViewDebug.IntToString(from = Gravity.START, to = "START"), + @ViewDebug.IntToString(from = Gravity.END, to = "END"), + @ViewDebug.IntToString(from = Gravity.CENTER_VERTICAL, to = "CENTER_VERTICAL"), + @ViewDebug.IntToString(from = Gravity.FILL_VERTICAL, to = "FILL_VERTICAL"), + @ViewDebug.IntToString(from = Gravity.CENTER_HORIZONTAL, to = "CENTER_HORIZONTAL"), + @ViewDebug.IntToString(from = Gravity.FILL_HORIZONTAL, to = "FILL_HORIZONTAL"), + @ViewDebug.IntToString(from = Gravity.CENTER, to = "CENTER"), + @ViewDebug.IntToString(from = Gravity.FILL, to = "FILL") }) public int gravity = Gravity.NO_GRAVITY; @@ -1062,12 +1235,10 @@ public abstract class ActionBar { public LayoutParams(int width, int height) { super(width, height); - this.gravity = Gravity.CENTER_VERTICAL | Gravity.START; } public LayoutParams(int width, int height, int gravity) { super(width, height); - this.gravity = gravity; } public LayoutParams(int gravity) { @@ -1076,12 +1247,14 @@ public abstract class ActionBar { public LayoutParams(LayoutParams source) { super(source); - - this.gravity = source.gravity; } public LayoutParams(ViewGroup.LayoutParams source) { super(source); } + + public LayoutParams(MarginLayoutParams source) { + super(source); + } } } diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 20e7311b94d5..b18eb983fcc2 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -21,9 +21,10 @@ import android.transition.Scene; import android.transition.Transition; import android.transition.TransitionManager; import android.util.ArrayMap; -import android.util.Pair; import android.util.SuperNotCalledException; -import com.android.internal.app.ActionBarImpl; +import android.widget.Toolbar; +import com.android.internal.app.WindowDecorActionBar; +import com.android.internal.app.ToolbarActionBar; import com.android.internal.policy.PolicyManager; import android.annotation.IntDef; @@ -723,7 +724,7 @@ public class Activity extends ContextThemeWrapper /*package*/ boolean mWindowAdded = false; /*package*/ boolean mVisibleFromServer = false; /*package*/ boolean mVisibleFromClient = true; - /*package*/ ActionBarImpl mActionBar = null; + /*package*/ ActionBar mActionBar = null; private boolean mEnableDefaultActionBarUp; private CharSequence mTitle; @@ -772,7 +773,6 @@ public class Activity extends ContextThemeWrapper private Thread mUiThread; final Handler mHandler = new Handler(); - private ActivityOptions mTransitionActivityOptions; /** Return the intent that started this activity. */ public Intent getIntent() { @@ -1398,6 +1398,9 @@ public class Activity extends ContextThemeWrapper protected void onStop() { if (DEBUG_LIFECYCLE) Slog.v(TAG, "onStop " + this); if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(false); + if (mWindow != null) { + mWindow.restoreViewVisibilityAfterTransitionToCallee(); + } getApplication().dispatchActivityStopped(this); mTranslucentCallback = null; mCalled = true; @@ -1906,15 +1909,39 @@ public class Activity extends ContextThemeWrapper */ @Nullable public ActionBar getActionBar() { - initActionBar(); + initWindowDecorActionBar(); return mActionBar; } + + /** + * Set a {@link android.widget.Toolbar Toolbar} to act as the {@link ActionBar} for this + * Activity window. + * + * <p>When set to a non-null value the {@link #getActionBar()} method will return + * an {@link ActionBar} object that can be used to control the given toolbar as if it were + * a traditional window decor action bar. The toolbar's menu will be populated with the + * Activity's options menu and the navigation button will be wired through the standard + * {@link android.R.id#home home} menu select action.</p> + * + * <p>In order to use a Toolbar within the Activity's window content the application + * must not request the window feature {@link Window#FEATURE_ACTION_BAR FEATURE_ACTION_BAR}.</p> + * + * @param actionBar Toolbar to set as the Activity's action bar + */ + public void setActionBar(@Nullable Toolbar actionBar) { + if (getActionBar() instanceof WindowDecorActionBar) { + throw new IllegalStateException("This Activity already has an action bar supplied " + + "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(actionBar); + } /** * Creates a new ActionBar, locates the inflated ActionBarView, * initializes the ActionBar with the view, and sets mActionBar. */ - private void initActionBar() { + private void initWindowDecorActionBar() { Window window = getWindow(); // Initializing the window decor can change window feature flags. @@ -1925,7 +1952,7 @@ public class Activity extends ContextThemeWrapper return; } - mActionBar = new ActionBarImpl(this); + mActionBar = new WindowDecorActionBar(this); mActionBar.setDefaultDisplayHomeAsUpEnabled(mEnableDefaultActionBarUp); mWindow.setDefaultIcon(mActivityInfo.getIconResource()); @@ -1943,7 +1970,7 @@ public class Activity extends ContextThemeWrapper */ public void setContentView(int layoutResID) { getWindow().setContentView(layoutResID); - initActionBar(); + initWindowDecorActionBar(); } /** @@ -1963,7 +1990,7 @@ public class Activity extends ContextThemeWrapper */ public void setContentView(View view) { getWindow().setContentView(view); - initActionBar(); + initWindowDecorActionBar(); } /** @@ -1979,7 +2006,7 @@ public class Activity extends ContextThemeWrapper */ public void setContentView(View view, ViewGroup.LayoutParams params) { getWindow().setContentView(view, params); - initActionBar(); + initWindowDecorActionBar(); } /** @@ -1991,7 +2018,7 @@ public class Activity extends ContextThemeWrapper */ public void addContentView(View view, ViewGroup.LayoutParams params) { getWindow().addContentView(view, params); - initActionBar(); + initWindowDecorActionBar(); } /** @@ -2274,7 +2301,7 @@ public class Activity extends ContextThemeWrapper */ public void onBackPressed() { if (!mFragments.popBackStackImmediate()) { - finish(); + finishWithTransition(); } } @@ -2636,7 +2663,7 @@ public class Activity extends ContextThemeWrapper */ public boolean onMenuOpened(int featureId, Menu menu) { if (featureId == Window.FEATURE_ACTION_BAR) { - initActionBar(); + initWindowDecorActionBar(); if (mActionBar != null) { mActionBar.dispatchMenuVisibilityChanged(true); } else { @@ -2717,7 +2744,7 @@ public class Activity extends ContextThemeWrapper break; case Window.FEATURE_ACTION_BAR: - initActionBar(); + initWindowDecorActionBar(); mActionBar.dispatchMenuVisibilityChanged(false); break; } @@ -3417,7 +3444,7 @@ public class Activity extends ContextThemeWrapper public MenuInflater getMenuInflater() { // Make sure that action views can get an appropriate theme. if (mMenuInflater == null) { - initActionBar(); + initWindowDecorActionBar(); if (mActionBar != null) { mMenuInflater = new MenuInflater(mActionBar.getThemedContext(), this); } else { @@ -3457,8 +3484,7 @@ public class Activity extends ContextThemeWrapper public void startActivityForResult(Intent intent, int requestCode) { Bundle options = null; if (mWindow.hasFeature(Window.FEATURE_CONTENT_TRANSITIONS)) { - final Pair<View, String>[] noSharedElements = null; - options = ActivityOptions.makeSceneTransitionAnimation(noSharedElements).toBundle(); + options = ActivityOptions.makeSceneTransitionAnimation().toBundle(); } startActivityForResult(intent, requestCode, options); } @@ -3506,7 +3532,7 @@ public class Activity extends ContextThemeWrapper mActionBar.captureSharedElements(sharedElementMap); activityOptions.addSharedElements(sharedElementMap); } - options = mWindow.startExitTransition(activityOptions); + options = mWindow.startExitTransitionToCallee(options); } } if (mParent == null) { @@ -4361,6 +4387,23 @@ public class Activity extends ContextThemeWrapper } /** + * Reverses the Activity Scene entry Transition and triggers the calling Activity + * to reverse its exit Transition. When the exit Transition completes, + * {@link #finish()} is called. If no entry Transition was used, finish() is called + * immediately and the Activity exit Transition is run. + * @see android.view.Window#setTriggerEarlySceneTransition(boolean, boolean) + * @see android.app.ActivityOptions#makeSceneTransitionAnimation(android.view.View, String) + */ + public void finishWithTransition() { + mWindow.startExitTransitionToCaller(new Runnable() { + @Override + public void run() { + finish(); + } + }); + } + + /** * Force finish another activity that you had previously started with * {@link #startActivityForResult}. * @@ -5139,7 +5182,7 @@ public class Activity extends ContextThemeWrapper @Nullable @Override public ActionMode onWindowStartingActionMode(ActionMode.Callback callback) { - initActionBar(); + initWindowDecorActionBar(); if (mActionBar != null) { return mActionBar.startActionMode(callback); } @@ -5370,43 +5413,34 @@ public class Activity extends ContextThemeWrapper } mWindowManager = mWindow.getWindowManager(); mCurrentConfig = config; - mTransitionActivityOptions = null; - Window.SceneTransitionListener sceneTransitionListener = null; - if (options != null) { - ActivityOptions activityOptions = new ActivityOptions(options); - if (activityOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) { - mTransitionActivityOptions = activityOptions; - sceneTransitionListener = new Window.SceneTransitionListener() { - @Override - public void nullPendingTransition() { - overridePendingTransition(0, 0); - } - - @Override - public void convertFromTranslucent() { - Activity.this.convertFromTranslucent(); - } - - @Override - public void convertToTranslucent() { - Activity.this.convertToTranslucent(null); - } + Window.SceneTransitionListener sceneTransitionListener + = new Window.SceneTransitionListener() { + @Override + public void nullPendingTransition() { + overridePendingTransition(0, 0); + } - @Override - public void sharedElementStart(Transition transition) { - Activity.this.onCaptureSharedElementStart(transition); - } + @Override + public void convertFromTranslucent() { + Activity.this.convertFromTranslucent(); + } - @Override - public void sharedElementEnd() { - Activity.this.onCaptureSharedElementEnd(); - } - }; + @Override + public void convertToTranslucent() { + Activity.this.convertToTranslucent(null); + } + @Override + public void sharedElementStart(Transition transition) { + Activity.this.onCaptureSharedElementStart(transition); } - } - mWindow.setTransitionOptions(mTransitionActivityOptions, sceneTransitionListener); + @Override + public void sharedElementEnd() { + Activity.this.onCaptureSharedElementEnd(); + } + }; + mWindow.setTransitionOptions(options, sceneTransitionListener); } /** @hide */ diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index 07247ffc3300..438458083a49 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -20,8 +20,10 @@ import android.content.Context; import android.graphics.Bitmap; import android.os.Bundle; import android.os.Handler; +import android.os.IBinder; import android.os.IRemoteCallback; import android.os.RemoteException; +import android.os.ResultReceiver; import android.transition.Transition; import android.util.Log; import android.util.Pair; @@ -136,6 +138,11 @@ public class ActivityOptions { /** @hide */ public static final int ANIM_SCENE_TRANSITION = 5; + private static final int MSG_SET_LISTENER = 100; + private static final int MSG_HIDE_SHARED_ELEMENTS = 101; + private static final int MSG_PREPARE_RESTORE = 102; + private static final int MSG_RESTORE = 103; + private String mPackageName; private int mAnimationType = ANIM_NONE; private int mCustomEnterResId; @@ -146,7 +153,7 @@ public class ActivityOptions { private int mStartWidth; private int mStartHeight; private IRemoteCallback mAnimationStartedListener; - private IRemoteCallback mTransitionCompleteListener; + private ResultReceiver mTransitionCompleteListener; private ArrayList<String> mSharedElementNames; private ArrayList<String> mLocalElementNames; @@ -428,8 +435,7 @@ public class ActivityOptions { break; case ANIM_SCENE_TRANSITION: - mTransitionCompleteListener = IRemoteCallback.Stub.asInterface( - opts.getBinder(KEY_TRANSITION_COMPLETE_LISTENER)); + mTransitionCompleteListener = opts.getParcelable(KEY_TRANSITION_COMPLETE_LISTENER); mSharedElementNames = opts.getStringArrayList(KEY_SHARED_ELEMENT_NAMES); mLocalElementNames = opts.getStringArrayList(KEY_LOCAL_ELEMENT_NAMES); break; @@ -495,7 +501,6 @@ public class ActivityOptions { /** @hide */ public void dispatchSceneTransitionStarted(final ActivityTransitionTarget target, ArrayList<String> sharedElementNames) { - boolean listenerSent = false; if (mTransitionCompleteListener != null) { IRemoteCallback callback = new IRemoteCallback.Stub() { @Override @@ -510,27 +515,28 @@ public class ActivityOptions { Bundle bundle = new Bundle(); bundle.putBinder(KEY_TRANSITION_TARGET_LISTENER, callback.asBinder()); bundle.putStringArrayList(KEY_SHARED_ELEMENT_NAMES, sharedElementNames); - try { - mTransitionCompleteListener.sendResult(bundle); - listenerSent = true; - } catch (RemoteException e) { - Log.w(TAG, "Couldn't retrieve transition notifications", e); - } - } - if (!listenerSent) { - target.sharedElementTransitionComplete(null); - target.exitTransitionComplete(); + mTransitionCompleteListener.send(MSG_SET_LISTENER, bundle); } } /** @hide */ public void dispatchSharedElementsReady() { if (mTransitionCompleteListener != null) { - try { - mTransitionCompleteListener.sendResult(null); - } catch (RemoteException e) { - Log.w(TAG, "Couldn't synchronize shared elements", e); - } + mTransitionCompleteListener.send(MSG_HIDE_SHARED_ELEMENTS, null); + } + } + + /** @hide */ + public void dispatchPrepareRestore() { + if (mTransitionCompleteListener != null) { + mTransitionCompleteListener.send(MSG_PREPARE_RESTORE, null); + } + } + + /** @hide */ + public void dispatchRestore(Bundle sharedElementsArgs) { + if (mTransitionCompleteListener != null) { + mTransitionCompleteListener.send(MSG_RESTORE, sharedElementsArgs); } } @@ -658,8 +664,7 @@ public class ActivityOptions { case ANIM_SCENE_TRANSITION: b.putInt(KEY_ANIM_TYPE, mAnimationType); if (mTransitionCompleteListener != null) { - b.putBinder(KEY_TRANSITION_COMPLETE_LISTENER, - mTransitionCompleteListener.asBinder()); + b.putParcelable(KEY_TRANSITION_COMPLETE_LISTENER, mTransitionCompleteListener); } b.putStringArrayList(KEY_SHARED_ELEMENT_NAMES, mSharedElementNames); b.putStringArrayList(KEY_LOCAL_ELEMENT_NAMES, mLocalElementNames); @@ -710,13 +715,13 @@ public class ActivityOptions { Bundle getSharedElementExitState(); void acceptedSharedElements(ArrayList<String> sharedElementNames); void hideSharedElements(); + void restore(Bundle sharedElementState); + void prepareForRestore(); } - private static class ExitTransitionListener extends IRemoteCallback.Stub + private static class ExitTransitionListener extends ResultReceiver implements Transition.TransitionListener { private boolean mSharedElementNotified; - private Transition mExitTransition; - private Transition mSharedElementTransition; private IRemoteCallback mTransitionCompleteCallback; private boolean mExitComplete; private boolean mSharedElementComplete; @@ -724,25 +729,40 @@ public class ActivityOptions { public ExitTransitionListener(Transition exitTransition, Transition sharedElementTransition, SharedElementSource sharedElementSource) { + super(null); mSharedElementSource = sharedElementSource; - mExitTransition = exitTransition; - mExitTransition.addListener(this); - mSharedElementTransition = sharedElementTransition; - mSharedElementTransition.addListener(this); + exitTransition.addListener(this); + sharedElementTransition.addListener(new Transition.TransitionListenerAdapter() { + @Override + public void onTransitionEnd(Transition transition) { + mSharedElementComplete = true; + notifySharedElement(); + transition.removeListener(this); + } + }); } @Override - public void sendResult(Bundle data) throws RemoteException { - if (data != null) { - mTransitionCompleteCallback = IRemoteCallback.Stub.asInterface( - data.getBinder(KEY_TRANSITION_TARGET_LISTENER)); - ArrayList<String> sharedElementNames - = data.getStringArrayList(KEY_SHARED_ELEMENT_NAMES); - mSharedElementSource.acceptedSharedElements(sharedElementNames); - notifySharedElement(); - notifyExit(); - } else { - mSharedElementSource.hideSharedElements(); + protected void onReceiveResult(int resultCode, Bundle resultData) { + switch (resultCode) { + case MSG_SET_LISTENER: + IBinder listener = resultData.getBinder(KEY_TRANSITION_TARGET_LISTENER); + mTransitionCompleteCallback = IRemoteCallback.Stub.asInterface(listener); + ArrayList<String> sharedElementNames + = resultData.getStringArrayList(KEY_SHARED_ELEMENT_NAMES); + mSharedElementSource.acceptedSharedElements(sharedElementNames); + notifySharedElement(); + notifyExit(); + break; + case MSG_HIDE_SHARED_ELEMENTS: + mSharedElementSource.hideSharedElements(); + break; + case MSG_PREPARE_RESTORE: + mSharedElementSource.prepareForRestore(); + break; + case MSG_RESTORE: + mSharedElementSource.restore(resultData); + break; } } @@ -752,15 +772,9 @@ public class ActivityOptions { @Override public void onTransitionEnd(Transition transition) { - if (transition == mExitTransition) { - mExitComplete = true; - notifyExit(); - mExitTransition.removeListener(this); - } else { - mSharedElementComplete = true; - notifySharedElement(); - mSharedElementTransition.removeListener(this); - } + mExitComplete = true; + notifyExit(); + transition.removeListener(this); } @Override diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index fb96d8d4d07d..07583fdd4974 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -17,7 +17,7 @@ package android.app; import android.content.pm.ApplicationInfo; -import com.android.internal.app.ActionBarImpl; +import com.android.internal.app.WindowDecorActionBar; import com.android.internal.policy.PolicyManager; import android.content.ComponentName; @@ -87,7 +87,7 @@ public class Dialog implements DialogInterface, Window.Callback, final WindowManager mWindowManager; Window mWindow; View mDecor; - private ActionBarImpl mActionBar; + private ActionBar mActionBar; /** * This field should be made private, so it is hidden from the SDK. * {@hide} @@ -280,7 +280,7 @@ public class Dialog implements DialogInterface, Window.Callback, final ApplicationInfo info = mContext.getApplicationInfo(); mWindow.setDefaultIcon(info.icon); mWindow.setDefaultLogo(info.logo); - mActionBar = new ActionBarImpl(this); + mActionBar = new WindowDecorActionBar(this); } WindowManager.LayoutParams l = mWindow.getAttributes(); diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java index 0c22740b6e00..c6731c9df423 100644 --- a/core/java/android/app/UiModeManager.java +++ b/core/java/android/app/UiModeManager.java @@ -166,9 +166,11 @@ public class UiModeManager { /** * Return the current running mode type. May be one of * {@link Configuration#UI_MODE_TYPE_NORMAL Configuration.UI_MODE_TYPE_NORMAL}, - * {@link Configuration#UI_MODE_TYPE_DESK Configuration.UI_MODE_TYPE_DESK}, or - * {@link Configuration#UI_MODE_TYPE_CAR Configuration.UI_MODE_TYPE_CAR}, or - * {@link Configuration#UI_MODE_TYPE_TELEVISION Configuration.UI_MODE_TYPE_APPLIANCE}. + * {@link Configuration#UI_MODE_TYPE_DESK Configuration.UI_MODE_TYPE_DESK}, + * {@link Configuration#UI_MODE_TYPE_CAR Configuration.UI_MODE_TYPE_CAR}, + * {@link Configuration#UI_MODE_TYPE_TELEVISION Configuration.UI_MODE_TYPE_TELEVISION}, + * {@link Configuration#UI_MODE_TYPE_APPLIANCE Configuration.UI_MODE_TYPE_APPLIANCE}, or + * {@link Configuration#UI_MODE_TYPE_WATCH Configuration.UI_MODE_TYPE_WATCH}. */ public int getCurrentModeType() { if (mService != null) { diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 3fdaef2c4d69..27e526b2a31d 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -573,6 +573,9 @@ public abstract class Context { * Open a private file associated with this Context's application package * for writing. Creates the file if it doesn't already exist. * + * <p>No permissions are required to invoke this method, since it uses internal + * storage. + * * @param name The name of the file to open; can not contain path * separators. * @param mode Operating mode. Use 0 or {@link #MODE_PRIVATE} for the @@ -630,6 +633,9 @@ public abstract class Context { * Returns the absolute path to the directory on the filesystem where * files created with {@link #openFileOutput} are stored. * + * <p>No permissions are required to read or write to the returned path, since this + * path is internal storage. + * * @return The path of the directory holding application files. * * @see #openFileOutput diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index d189a344fdbd..9881428865c4 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -2608,6 +2608,23 @@ public class Intent implements Parcelable, Cloneable { "android.intent.action.USER_INFO_CHANGED"; /** + * Broadcast sent to the primary user when an associated managed profile is added (the profile + * was created and is ready to be used). Carries an extra {@link #EXTRA_USER} that specifies + * the UserHandle of the profile that was added. This is only sent to registered receivers, + * not manifest receivers. + */ + public static final String ACTION_MANAGED_PROFILE_ADDED = + "android.intent.action.MANAGED_PROFILE_ADDED"; + + /** + * Broadcast sent to the primary user when an associated managed profile is removed. Carries an + * extra {@link #EXTRA_USER} that specifies the UserHandle of the profile that was removed. This + * is only sent to registered receivers, not manifest receivers. + */ + public static final String ACTION_MANAGED_PROFILE_REMOVED = + "android.intent.action.MANAGED_PROFILE_REMOVED"; + + /** * Sent when the user taps on the clock widget in the system's "quick settings" area. */ public static final String ACTION_QUICK_CLOCK = @@ -3301,15 +3318,23 @@ public class Intent implements Parcelable, Cloneable { "android.intent.extra.ALLOW_MULTIPLE"; /** - * The userHandle carried with broadcast intents related to addition, removal and switching of - * users - * - {@link #ACTION_USER_ADDED}, {@link #ACTION_USER_REMOVED} and {@link #ACTION_USER_SWITCHED}. + * The integer userHandle carried with broadcast intents related to addition, removal and + * switching of users and managed profiles - {@link #ACTION_USER_ADDED}, + * {@link #ACTION_USER_REMOVED} and {@link #ACTION_USER_SWITCHED}. + * * @hide */ public static final String EXTRA_USER_HANDLE = "android.intent.extra.user_handle"; /** + * The UserHandle carried with broadcasts intents related to addition and removal of managed + * profiles - {@link #ACTION_MANAGED_PROFILE_ADDED} and {@link #ACTION_MANAGED_PROFILE_REMOVED}. + */ + public static final String EXTRA_USER = + "android.intent.extra.user"; + + /** * Extra used in the response from a BroadcastReceiver that handles * {@link #ACTION_GET_RESTRICTION_ENTRIES}. The type of the extra is * <code>ArrayList<RestrictionEntry></code>. diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java index 48b6fca7b2d3..a07fc9708b7f 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -440,6 +440,11 @@ public final class Configuration implements Parcelable, Comparable<Configuration * <a href="{@docRoot}guide/topics/resources/providing-resources.html#UiModeQualifier">appliance</a> * resource qualifier. */ public static final int UI_MODE_TYPE_APPLIANCE = 0x05; + /** Constant for {@link #uiMode}: a {@link #UI_MODE_TYPE_MASK} + * value that corresponds to the + * <a href="{@docRoot}guide/topics/resources/providing-resources.html#UiModeQualifier">watch</a> + * resource qualifier. */ + public static final int UI_MODE_TYPE_WATCH = 0x06; /** Constant for {@link #uiMode}: bits that encode the night mode. */ public static final int UI_MODE_NIGHT_MASK = 0x30; @@ -462,8 +467,8 @@ public final class Configuration implements Parcelable, Comparable<Configuration * <p>The {@link #UI_MODE_TYPE_MASK} bits define the overall ui mode of the * device. They may be one of {@link #UI_MODE_TYPE_UNDEFINED}, * {@link #UI_MODE_TYPE_NORMAL}, {@link #UI_MODE_TYPE_DESK}, - * {@link #UI_MODE_TYPE_CAR}, {@link #UI_MODE_TYPE_TELEVISION}, or - * {@link #UI_MODE_TYPE_APPLIANCE}. + * {@link #UI_MODE_TYPE_CAR}, {@link #UI_MODE_TYPE_TELEVISION}, + * {@link #UI_MODE_TYPE_APPLIANCE}, or {@link #UI_MODE_TYPE_WATCH}. * * <p>The {@link #UI_MODE_NIGHT_MASK} defines whether the screen * is in a special mode. They may be one of {@link #UI_MODE_NIGHT_UNDEFINED}, @@ -700,6 +705,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration case UI_MODE_TYPE_CAR: sb.append(" car"); break; case UI_MODE_TYPE_TELEVISION: sb.append(" television"); break; case UI_MODE_TYPE_APPLIANCE: sb.append(" appliance"); break; + case UI_MODE_TYPE_WATCH: sb.append(" watch"); break; default: sb.append(" uimode="); sb.append(uiMode&UI_MODE_TYPE_MASK); break; } switch ((uiMode&UI_MODE_NIGHT_MASK)) { diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index d6eafc6e4018..4879c2382afd 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -1585,6 +1585,15 @@ public class Resources { /** Resource identifier for the theme. */ private int mThemeResId = 0; + + // Needed by layoutlib. + /*package*/ long getNativeTheme() { + return mTheme; + } + + /*package*/ int getAppliedStyleResId() { + return mThemeResId; + } } /** diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl index d03b0c5ba3e7..be3c0cc69883 100644 --- a/core/java/android/os/IPowerManager.aidl +++ b/core/java/android/os/IPowerManager.aidl @@ -23,7 +23,7 @@ import android.os.WorkSource; interface IPowerManager { - // WARNING: The first four methods must remain the first three methods because their + // WARNING: The first five methods must remain the first five methods because their // transaction numbers must not change unless IPowerManager.cpp is also updated. void acquireWakeLock(IBinder lock, int flags, String tag, String packageName, in WorkSource ws, String historyTag); @@ -31,6 +31,7 @@ interface IPowerManager int uidtoblame); void releaseWakeLock(IBinder lock, int flags); void updateWakeLockUids(IBinder lock, in int[] uids); + oneway void powerHint(int hintId, int data); void updateWakeLockWorkSource(IBinder lock, in WorkSource ws, String historyTag); boolean isWakeLockLevelSupported(int level); diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index 535eee1397ed..638ef22fe3e2 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -433,7 +433,7 @@ public class StaticLayout extends Layout { } if (mLineCount >= mMaximumVisibleLineCount) { - break; + return; } } } diff --git a/core/java/android/transition/CircularPropagation.java b/core/java/android/transition/CircularPropagation.java new file mode 100644 index 000000000000..18a3d229cc6c --- /dev/null +++ b/core/java/android/transition/CircularPropagation.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.transition; + +import android.graphics.Rect; +import android.util.FloatMath; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; + +/** + * A propagation that varies with the distance to the epicenter of the Transition + * or center of the scene if no epicenter exists. When a View is visible in the + * start of the transition, Views farther from the epicenter will transition + * sooner than Views closer to the epicenter. When a View is not in the start + * of the transition or is not visible at the start of the transition, it will + * transition sooner when closer to the epicenter and later when farther from + * the epicenter. This is the default TransitionPropagation used with + * {@link android.transition.Explode}. + */ +public class CircularPropagation extends VisibilityPropagation { + private static final String TAG = "CircularPropagation"; + + private float mPropagationSpeed = 4.0f; + + /** + * Sets the speed at which transition propagation happens, relative to the duration of the + * Transition. A <code>propagationSpeed</code> of 1 means that a View centered farthest from + * the epicenter and View centered at the epicenter will have a difference + * in start delay of approximately the duration of the Transition. A speed of 2 means the + * start delay difference will be approximately half of the duration of the transition. A + * value of 0 is illegal, but negative values will invert the propagation. + * + * @param propagationSpeed The speed at which propagation occurs, relative to the duration + * of the transition. A speed of 4 means it works 4 times as fast + * as the duration of the transition. May not be 0. + */ + public void setPropagationSpeed(float propagationSpeed) { + if (propagationSpeed == 0) { + throw new IllegalArgumentException("propagationSpeed may not be 0"); + } + mPropagationSpeed = propagationSpeed; + } + + @Override + public long getStartDelay(ViewGroup sceneRoot, Transition transition, + TransitionValues startValues, TransitionValues endValues) { + if (startValues == null && endValues == null) { + return 0; + } + int directionMultiplier = 1; + TransitionValues positionValues; + if (endValues == null || getViewVisibility(startValues) == View.VISIBLE) { + positionValues = startValues; + directionMultiplier = -1; + } else { + positionValues = endValues; + } + + int viewCenterX = getViewX(positionValues); + int viewCenterY = getViewY(positionValues); + + Rect epicenter = transition.getEpicenter(); + int epicenterX; + int epicenterY; + if (epicenter != null) { + epicenterX = epicenter.centerX(); + epicenterY = epicenter.centerY(); + } else { + int[] loc = new int[2]; + sceneRoot.getLocationOnScreen(loc); + epicenterX = Math.round(loc[0] + (sceneRoot.getWidth() / 2) + + sceneRoot.getTranslationX()); + epicenterY = Math.round(loc[1] + (sceneRoot.getHeight() / 2) + + sceneRoot.getTranslationY()); + } + float distance = distance(viewCenterX, viewCenterY, epicenterX, epicenterY); + float maxDistance = distance(0, 0, sceneRoot.getWidth(), sceneRoot.getHeight()); + float distanceFraction = distance/maxDistance; + + return Math.round(transition.getDuration() * directionMultiplier / mPropagationSpeed + * distanceFraction); + } + + private static float distance(float x1, float y1, float x2, float y2) { + float x = x2 - x1; + float y = y2 - y1; + return FloatMath.sqrt((x * x) + (y * y)); + } +} diff --git a/core/java/android/transition/Explode.java b/core/java/android/transition/Explode.java new file mode 100644 index 000000000000..fae527c26789 --- /dev/null +++ b/core/java/android/transition/Explode.java @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.transition; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.animation.TimeInterpolator; +import android.graphics.Path; +import android.graphics.Rect; +import android.util.FloatMath; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.DecelerateInterpolator; + +/** + * This transition tracks changes to the visibility of target views in the + * start and end scenes and moves views in or out from the edges of the + * scene. Visibility is determined by both the + * {@link View#setVisibility(int)} state of the view as well as whether it + * is parented in the current view hierarchy. Disappearing Views are + * limited as described in {@link Visibility#onDisappear(android.view.ViewGroup, + * TransitionValues, int, TransitionValues, int)}. + * <p>Views move away from the focal View or the center of the Scene if + * no epicenter was provided.</p> + */ +public class Explode extends Visibility { + private static final TimeInterpolator sDecelerate = new DecelerateInterpolator(); + private static final TimeInterpolator sAccelerate = new AccelerateInterpolator(); + private static final String TAG = "Explode"; + + private static final String PROPNAME_SCREEN_BOUNDS = "android:out:screenBounds"; + + private int[] mTempLoc = new int[2]; + + public Explode() { + setPropagation(new CircularPropagation()); + } + + private void captureValues(TransitionValues transitionValues) { + View view = transitionValues.view; + view.getLocationOnScreen(mTempLoc); + int left = mTempLoc[0] + Math.round(view.getTranslationX()); + int top = mTempLoc[1] + Math.round(view.getTranslationY()); + int right = left + view.getWidth(); + int bottom = top + view.getHeight(); + transitionValues.values.put(PROPNAME_SCREEN_BOUNDS, new Rect(left, top, right, bottom)); + } + + @Override + public void captureStartValues(TransitionValues transitionValues) { + super.captureStartValues(transitionValues); + captureValues(transitionValues); + } + + @Override + public void captureEndValues(TransitionValues transitionValues) { + super.captureEndValues(transitionValues); + captureValues(transitionValues); + } + + private Animator createAnimation(final View view, float startX, float startY, float endX, + float endY, float terminalX, float terminalY, TimeInterpolator interpolator) { + view.setTranslationX(startX); + view.setTranslationY(startY); + if (startY == endY && startX == endX) { + return null; + } + Path path = new Path(); + path.moveTo(startX, startY); + path.lineTo(endX, endY); + ObjectAnimator pathAnimator = ObjectAnimator.ofFloat(view, View.TRANSLATION_X, + View.TRANSLATION_Y, path); + pathAnimator.setInterpolator(interpolator); + OutAnimatorListener listener = new OutAnimatorListener(view, terminalX, terminalY, + endX, endY); + pathAnimator.addListener(listener); + pathAnimator.addPauseListener(listener); + + return pathAnimator; + } + + @Override + public Animator onAppear(ViewGroup sceneRoot, View view, + TransitionValues startValues, TransitionValues endValues) { + if (endValues == null) { + return null; + } + Rect bounds = (Rect) endValues.values.get(PROPNAME_SCREEN_BOUNDS); + calculateOut(sceneRoot, bounds, mTempLoc); + + final float endX = view.getTranslationX(); + final float startX = endX + mTempLoc[0]; + final float endY = view.getTranslationY(); + final float startY = endY + mTempLoc[1]; + + return createAnimation(view, startX, startY, endX, endY, endX, endY, sDecelerate); + } + + @Override + public Animator onDisappear(ViewGroup sceneRoot, View view, + TransitionValues startValues, TransitionValues endValues) { + Rect bounds = (Rect) startValues.values.get(PROPNAME_SCREEN_BOUNDS); + calculateOut(sceneRoot, bounds, mTempLoc); + + final float startX = view.getTranslationX(); + final float endX = startX + mTempLoc[0]; + final float startY = view.getTranslationY(); + final float endY = startY + mTempLoc[1]; + + return createAnimation(view, startX, startY, endX, endY, startX, startY, + sAccelerate); + } + + private void calculateOut(View sceneRoot, Rect bounds, int[] outVector) { + sceneRoot.getLocationOnScreen(mTempLoc); + int sceneRootX = mTempLoc[0]; + int sceneRootY = mTempLoc[1]; + int focalX; + int focalY; + + Rect epicenter = getEpicenter(); + if (epicenter == null) { + focalX = sceneRootX + (sceneRoot.getWidth() / 2) + + Math.round(sceneRoot.getTranslationX()); + focalY = sceneRootY + (sceneRoot.getHeight() / 2) + + Math.round(sceneRoot.getTranslationY()); + } else { + focalX = epicenter.centerX(); + focalY = epicenter.centerY(); + } + + int centerX = bounds.centerX(); + int centerY = bounds.centerY(); + float xVector = centerX - focalX; + float yVector = centerY - focalY; + + if (xVector == 0 && yVector == 0) { + // Random direction when View is centered on focal View. + xVector = (float)(Math.random() * 2) - 1; + yVector = (float)(Math.random() * 2) - 1; + } + float vectorSize = calculateDistance(xVector, yVector); + xVector /= vectorSize; + yVector /= vectorSize; + + float maxDistance = + calculateMaxDistance(sceneRoot, focalX - sceneRootX, focalY - sceneRootY); + + outVector[0] = Math.round(maxDistance * xVector); + outVector[1] = Math.round(maxDistance * yVector); + } + + private static float calculateMaxDistance(View sceneRoot, int focalX, int focalY) { + int maxX = Math.max(focalX, sceneRoot.getWidth() - focalX); + int maxY = Math.max(focalY, sceneRoot.getHeight() - focalY); + return calculateDistance(maxX, maxY); + } + + private static float calculateDistance(float x, float y) { + return FloatMath.sqrt((x * x) + (y * y)); + } + + private static class OutAnimatorListener extends AnimatorListenerAdapter { + private final View mView; + private boolean mCanceled = false; + private float mPausedX; + private float mPausedY; + private final float mTerminalX; + private final float mTerminalY; + private final float mEndX; + private final float mEndY; + + public OutAnimatorListener(View view, float terminalX, float terminalY, + float endX, float endY) { + mView = view; + mTerminalX = terminalX; + mTerminalY = terminalY; + mEndX = endX; + mEndY = endY; + } + + @Override + public void onAnimationCancel(Animator animator) { + mView.setTranslationX(mTerminalX); + mView.setTranslationY(mTerminalY); + mCanceled = true; + } + + @Override + public void onAnimationEnd(Animator animator) { + if (!mCanceled) { + mView.setTranslationX(mTerminalX); + mView.setTranslationY(mTerminalY); + } + } + + @Override + public void onAnimationPause(Animator animator) { + mPausedX = mView.getTranslationX(); + mPausedY = mView.getTranslationY(); + mView.setTranslationY(mEndX); + mView.setTranslationY(mEndY); + } + + @Override + public void onAnimationResume(Animator animator) { + mView.setTranslationX(mPausedX); + mView.setTranslationY(mPausedY); + } + } +} diff --git a/core/java/android/transition/Fade.java b/core/java/android/transition/Fade.java index 8edb1ff74405..08e27d33ff47 100644 --- a/core/java/android/transition/Fade.java +++ b/core/java/android/transition/Fade.java @@ -59,8 +59,6 @@ public class Fade extends Visibility { private static boolean DBG = Transition.DBG && false; private static final String LOG_TAG = "Fade"; - private static final String PROPNAME_SCREEN_X = "android:fade:screenX"; - private static final String PROPNAME_SCREEN_Y = "android:fade:screenY"; /** * Fading mode used in {@link #Fade(int)} to make the transition @@ -98,245 +96,81 @@ public class Fade extends Visibility { /** * Utility method to handle creating and running the Animator. */ - private Animator createAnimation(View view, float startAlpha, float endAlpha, - AnimatorListenerAdapter listener) { + private Animator createAnimation(View view, float startAlpha, float endAlpha) { if (startAlpha == endAlpha) { - // run listener if we're noop'ing the animation, to get the end-state results now - if (listener != null) { - listener.onAnimationEnd(null); - } return null; } - final ObjectAnimator anim = ObjectAnimator.ofFloat(view, "transitionAlpha", startAlpha, - endAlpha); + view.setTransitionAlpha(startAlpha); + final ObjectAnimator anim = ObjectAnimator.ofFloat(view, "transitionAlpha", endAlpha); if (DBG) { Log.d(LOG_TAG, "Created animator " + anim); } - if (listener != null) { - anim.addListener(listener); - anim.addPauseListener(listener); - } + FadeAnimatorListener listener = new FadeAnimatorListener(view, endAlpha); + anim.addListener(listener); + anim.addPauseListener(listener); return anim; } - private void captureValues(TransitionValues transitionValues) { - int[] loc = new int[2]; - transitionValues.view.getLocationOnScreen(loc); - transitionValues.values.put(PROPNAME_SCREEN_X, loc[0]); - transitionValues.values.put(PROPNAME_SCREEN_Y, loc[1]); - } - - @Override - public void captureStartValues(TransitionValues transitionValues) { - super.captureStartValues(transitionValues); - captureValues(transitionValues); - } - @Override - public Animator onAppear(ViewGroup sceneRoot, - TransitionValues startValues, int startVisibility, - TransitionValues endValues, int endVisibility) { + public Animator onAppear(ViewGroup sceneRoot, View view, + TransitionValues startValues, + TransitionValues endValues) { if ((mFadingMode & IN) != IN || endValues == null) { return null; } - final View endView = endValues.view; if (DBG) { View startView = (startValues != null) ? startValues.view : null; Log.d(LOG_TAG, "Fade.onAppear: startView, startVis, endView, endVis = " + - startView + ", " + startVisibility + ", " + endView + ", " + endVisibility); + startView + ", " + view); } - endView.setTransitionAlpha(0); - TransitionListener transitionListener = new TransitionListenerAdapter() { - boolean mCanceled = false; - float mPausedAlpha; - - @Override - public void onTransitionCancel(Transition transition) { - endView.setTransitionAlpha(1); - mCanceled = true; - } - - @Override - public void onTransitionEnd(Transition transition) { - if (!mCanceled) { - endView.setTransitionAlpha(1); - } - } - - @Override - public void onTransitionPause(Transition transition) { - mPausedAlpha = endView.getTransitionAlpha(); - endView.setTransitionAlpha(1); - } - - @Override - public void onTransitionResume(Transition transition) { - endView.setTransitionAlpha(mPausedAlpha); - } - }; - addListener(transitionListener); - return createAnimation(endView, 0, 1, null); + return createAnimation(view, 0, 1); } @Override - public Animator onDisappear(ViewGroup sceneRoot, - TransitionValues startValues, int startVisibility, - TransitionValues endValues, int endVisibility) { + public Animator onDisappear(ViewGroup sceneRoot, final View view, TransitionValues startValues, + TransitionValues endValues) { if ((mFadingMode & OUT) != OUT) { return null; } - View view = null; - View startView = (startValues != null) ? startValues.view : null; - View endView = (endValues != null) ? endValues.view : null; - if (DBG) { - Log.d(LOG_TAG, "Fade.onDisappear: startView, startVis, endView, endVis = " + - startView + ", " + startVisibility + ", " + endView + ", " + endVisibility); - } - View overlayView = null; - View viewToKeep = null; - if (endView == null || endView.getParent() == null) { - if (endView != null) { - // endView was removed from its parent - add it to the overlay - view = overlayView = endView; - } else if (startView != null) { - // endView does not exist. Use startView only under certain - // conditions, because placing a view in an overlay necessitates - // it being removed from its current parent - if (startView.getParent() == null) { - // no parent - safe to use - view = overlayView = startView; - } else if (startView.getParent() instanceof View && - startView.getParent().getParent() == null) { - View startParent = (View) startView.getParent(); - int id = startParent.getId(); - if (id != View.NO_ID && sceneRoot.findViewById(id) != null && mCanRemoveViews) { - // no parent, but its parent is unparented but the parent - // hierarchy has been replaced by a new hierarchy with the same id - // and it is safe to un-parent startView - view = overlayView = startView; - } - } - } - } else { - // visibility change - if (endVisibility == View.INVISIBLE) { - view = endView; - viewToKeep = view; - } else { - // Becoming GONE - if (startView == endView) { - view = endView; - viewToKeep = view; - } else { - view = startView; - overlayView = view; - } - } - } - final int finalVisibility = endVisibility; - // TODO: add automatic facility to Visibility superclass for keeping views around - if (overlayView != null) { - // TODO: Need to do this for general case of adding to overlay - int screenX = (Integer) startValues.values.get(PROPNAME_SCREEN_X); - int screenY = (Integer) startValues.values.get(PROPNAME_SCREEN_Y); - int[] loc = new int[2]; - sceneRoot.getLocationOnScreen(loc); - overlayView.offsetLeftAndRight((screenX - loc[0]) - overlayView.getLeft()); - overlayView.offsetTopAndBottom((screenY - loc[1]) - overlayView.getTop()); - sceneRoot.getOverlay().add(overlayView); - // TODO: add automatic facility to Visibility superclass for keeping views around - final float startAlpha = 1; - float endAlpha = 0; - final View finalView = view; - final View finalOverlayView = overlayView; - final View finalViewToKeep = viewToKeep; - final ViewGroup finalSceneRoot = sceneRoot; - final AnimatorListenerAdapter endListener = new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - finalView.setTransitionAlpha(startAlpha); - // TODO: restore view offset from overlay repositioning - if (finalViewToKeep != null) { - finalViewToKeep.setVisibility(finalVisibility); - } - if (finalOverlayView != null) { - finalSceneRoot.getOverlay().remove(finalOverlayView); - } - } - @Override - public void onAnimationPause(Animator animation) { - if (finalOverlayView != null) { - finalSceneRoot.getOverlay().remove(finalOverlayView); - } - } + return createAnimation(view, 1, 0); + } + + private static class FadeAnimatorListener extends AnimatorListenerAdapter { + private final View mView; + private final float mEndAlpha; + private boolean mCanceled = false; + private float mPausedAlpha; - @Override - public void onAnimationResume(Animator animation) { - if (finalOverlayView != null) { - finalSceneRoot.getOverlay().add(finalOverlayView); - } - } - }; - return createAnimation(view, startAlpha, endAlpha, endListener); + public FadeAnimatorListener(View view, float endAlpha) { + mView = view; + mEndAlpha = endAlpha; } - if (viewToKeep != null) { - // TODO: find a different way to do this, like just changing the view to be - // VISIBLE for the duration of the transition - viewToKeep.setVisibility((View.VISIBLE)); - // TODO: add automatic facility to Visibility superclass for keeping views around - final float startAlpha = 1; - float endAlpha = 0; - final View finalView = view; - final View finalOverlayView = overlayView; - final View finalViewToKeep = viewToKeep; - final ViewGroup finalSceneRoot = sceneRoot; - final AnimatorListenerAdapter endListener = new AnimatorListenerAdapter() { - boolean mCanceled = false; - float mPausedAlpha = -1; - @Override - public void onAnimationPause(Animator animation) { - if (finalViewToKeep != null && !mCanceled) { - finalViewToKeep.setVisibility(finalVisibility); - } - mPausedAlpha = finalView.getTransitionAlpha(); - finalView.setTransitionAlpha(startAlpha); - } + @Override + public void onAnimationCancel(Animator animator) { + mCanceled = true; + if (mPausedAlpha >= 0) { + mView.setTransitionAlpha(mPausedAlpha); + } + } - @Override - public void onAnimationResume(Animator animation) { - if (finalViewToKeep != null && !mCanceled) { - finalViewToKeep.setVisibility(View.VISIBLE); - } - finalView.setTransitionAlpha(mPausedAlpha); - } + @Override + public void onAnimationEnd(Animator animator) { + if (!mCanceled) { + mView.setTransitionAlpha(mEndAlpha); + } + } - @Override - public void onAnimationCancel(Animator animation) { - mCanceled = true; - if (mPausedAlpha >= 0) { - finalView.setTransitionAlpha(mPausedAlpha); - } - } + @Override + public void onAnimationPause(Animator animator) { + mPausedAlpha = mView.getTransitionAlpha(); + mView.setTransitionAlpha(mEndAlpha); + } - @Override - public void onAnimationEnd(Animator animation) { - if (!mCanceled) { - finalView.setTransitionAlpha(startAlpha); - } - // TODO: restore view offset from overlay repositioning - if (finalViewToKeep != null && !mCanceled) { - finalViewToKeep.setVisibility(finalVisibility); - } - if (finalOverlayView != null) { - finalSceneRoot.getOverlay().remove(finalOverlayView); - } - } - }; - return createAnimation(view, startAlpha, endAlpha, endListener); + @Override + public void onAnimationResume(Animator animator) { + mView.setTransitionAlpha(mPausedAlpha); } - return null; } - -}
\ No newline at end of file +} diff --git a/core/java/android/transition/MatrixClippedDrawable.java b/core/java/android/transition/MatrixClippedDrawable.java new file mode 100644 index 000000000000..ebaad59d134c --- /dev/null +++ b/core/java/android/transition/MatrixClippedDrawable.java @@ -0,0 +1,300 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.transition; + +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Matrix; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.util.Property; + +/** + * Used in MoveImage to mock an ImageView as a Drawable to be scaled in the scene root Overlay. + * @hide + */ +class MatrixClippedDrawable extends Drawable implements Drawable.Callback { + private static final String TAG = "MatrixClippedDrawable"; + + private ClippedMatrixState mClippedMatrixState; + + public static final Property<MatrixClippedDrawable, Rect> CLIP_PROPERTY + = new Property<MatrixClippedDrawable, Rect>(Rect.class, "clipRect") { + + @Override + public Rect get(MatrixClippedDrawable object) { + return object.getClipRect(); + } + + @Override + public void set(MatrixClippedDrawable object, Rect value) { + object.setClipRect(value); + } + }; + + public static final Property<MatrixClippedDrawable, Matrix> MATRIX_PROPERTY + = new Property<MatrixClippedDrawable, Matrix>(Matrix.class, "matrix") { + @Override + public void set(MatrixClippedDrawable object, Matrix value) { + object.setMatrix(value); + } + + @Override + public Matrix get(MatrixClippedDrawable object) { + return object.getMatrix(); + } + }; + + public MatrixClippedDrawable(Drawable drawable) { + this(null, null); + + mClippedMatrixState.mDrawable = drawable; + + if (drawable != null) { + drawable.setCallback(this); + } + } + + public void setMatrix(Matrix matrix) { + if (matrix == null) { + mClippedMatrixState.mMatrix = null; + } else { + if (mClippedMatrixState.mMatrix == null) { + mClippedMatrixState.mMatrix = new Matrix(); + } + mClippedMatrixState.mMatrix.set(matrix); + } + invalidateSelf(); + } + + public Matrix getMatrix() { + return mClippedMatrixState.mMatrix; + } + + public Rect getClipRect() { + return mClippedMatrixState.mClipRect; + } + + public void setClipRect(Rect clipRect) { + if (clipRect == null) { + if (mClippedMatrixState.mClipRect != null) { + mClippedMatrixState.mClipRect = null; + invalidateSelf(); + } + } else { + if (mClippedMatrixState.mClipRect == null) { + mClippedMatrixState.mClipRect = new Rect(clipRect); + } else { + mClippedMatrixState.mClipRect.set(clipRect); + } + invalidateSelf(); + } + } + + // overrides from Drawable.Callback + + public void invalidateDrawable(Drawable who) { + final Drawable.Callback callback = getCallback(); + if (callback != null) { + callback.invalidateDrawable(this); + } + } + + public void scheduleDrawable(Drawable who, Runnable what, long when) { + final Drawable.Callback callback = getCallback(); + if (callback != null) { + callback.scheduleDrawable(this, what, when); + } + } + + public void unscheduleDrawable(Drawable who, Runnable what) { + final Drawable.Callback callback = getCallback(); + if (callback != null) { + callback.unscheduleDrawable(this, what); + } + } + + // overrides from Drawable + + @Override + public int getChangingConfigurations() { + return super.getChangingConfigurations() + | mClippedMatrixState.mChangingConfigurations + | mClippedMatrixState.mDrawable.getChangingConfigurations(); + } + + @Override + public boolean getPadding(Rect padding) { + // XXX need to adjust padding! + return mClippedMatrixState.mDrawable.getPadding(padding); + } + + @Override + public boolean setVisible(boolean visible, boolean restart) { + mClippedMatrixState.mDrawable.setVisible(visible, restart); + return super.setVisible(visible, restart); + } + + @Override + public void setAlpha(int alpha) { + mClippedMatrixState.mDrawable.setAlpha(alpha); + } + + @Override + public int getAlpha() { + return mClippedMatrixState.mDrawable.getAlpha(); + } + + @Override + public void setColorFilter(ColorFilter cf) { + mClippedMatrixState.mDrawable.setColorFilter(cf); + } + + @Override + public int getOpacity() { + return mClippedMatrixState.mDrawable.getOpacity(); + } + + @Override + public boolean isStateful() { + return mClippedMatrixState.mDrawable.isStateful(); + } + + @Override + protected boolean onStateChange(int[] state) { + return mClippedMatrixState.mDrawable.setState(state); + } + + @Override + protected boolean onLevelChange(int level) { + mClippedMatrixState.mDrawable.setLevel(level); + invalidateSelf(); + return true; + } + + @Override + protected void onBoundsChange(Rect bounds) { + super.setBounds(bounds); + if (mClippedMatrixState.mMatrix == null) { + mClippedMatrixState.mDrawable.setBounds(bounds); + } else { + int drawableWidth = mClippedMatrixState.mDrawable.getIntrinsicWidth(); + int drawableHeight = mClippedMatrixState.mDrawable.getIntrinsicHeight(); + mClippedMatrixState.mDrawable.setBounds(bounds.left, bounds.top, + drawableWidth + bounds.left, drawableHeight + bounds.top); + } + invalidateSelf(); + } + + @Override + public void draw(Canvas canvas) { + Rect bounds = getBounds(); + int left = bounds.left; + int top = bounds.top; + int saveCount = canvas.getSaveCount(); + canvas.save(); + if (mClippedMatrixState.mClipRect != null) { + canvas.clipRect(mClippedMatrixState.mClipRect); + } else { + canvas.clipRect(bounds); + } + + if (mClippedMatrixState != null && !mClippedMatrixState.mMatrix.isIdentity()) { + canvas.translate(left, top); + canvas.concat(mClippedMatrixState.mMatrix); + canvas.translate(-left, -top); + } + mClippedMatrixState.mDrawable.draw(canvas); + canvas.restoreToCount(saveCount); + } + + @Override + public int getIntrinsicWidth() { + return mClippedMatrixState.mDrawable.getIntrinsicWidth(); + } + + @Override + public int getIntrinsicHeight() { + return mClippedMatrixState.mDrawable.getIntrinsicHeight(); + } + + @Override + public Drawable.ConstantState getConstantState() { + if (mClippedMatrixState.canConstantState()) { + mClippedMatrixState.mChangingConfigurations = getChangingConfigurations(); + return mClippedMatrixState; + } + return null; + } + + final static class ClippedMatrixState extends Drawable.ConstantState { + Drawable mDrawable; + Matrix mMatrix; + Rect mClipRect; + + private boolean mCheckedConstantState; + private boolean mCanConstantState; + int mChangingConfigurations; + + ClippedMatrixState(ClippedMatrixState orig, MatrixClippedDrawable owner, Resources res) { + if (orig != null) { + if (res != null) { + mDrawable = orig.mDrawable.getConstantState().newDrawable(res); + } else { + mDrawable = orig.mDrawable.getConstantState().newDrawable(); + } + mDrawable.setCallback(owner); + mCheckedConstantState = mCanConstantState = true; + if (orig.mMatrix != null) { + mMatrix = new Matrix(orig.mMatrix); + } + if (orig.mClipRect != null) { + mClipRect = new Rect(orig.mClipRect); + } + } + } + + @Override + public Drawable newDrawable() { + return new MatrixClippedDrawable(this, null); + } + + @Override + public Drawable newDrawable(Resources res) { + return new MatrixClippedDrawable(this, res); + } + + @Override + public int getChangingConfigurations() { + return mChangingConfigurations; + } + + boolean canConstantState() { + if (!mCheckedConstantState) { + mCanConstantState = mDrawable.getConstantState() != null; + mCheckedConstantState = true; + } + + return mCanConstantState; + } + } + + private MatrixClippedDrawable(ClippedMatrixState state, Resources res) { + mClippedMatrixState = new ClippedMatrixState(state, this, res); + } + +} diff --git a/core/java/android/transition/MoveImage.java b/core/java/android/transition/MoveImage.java new file mode 100644 index 000000000000..d68e97190a36 --- /dev/null +++ b/core/java/android/transition/MoveImage.java @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.transition; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; +import android.animation.PropertyValuesHolder; +import android.animation.RectEvaluator; +import android.animation.TypeEvaluator; +import android.animation.ValueAnimator; +import android.graphics.Matrix; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.drawable.Drawable; +import android.util.FloatMath; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewGroupOverlay; +import android.view.ViewParent; +import android.widget.ImageView; + +import java.util.ArrayList; +import java.util.Map; + +/** + * Transitions ImageViews, including size, scaleType, and matrix. The ImageView drawable + * must remain the same between both start and end states, but the + * {@link ImageView#setScaleType(android.widget.ImageView.ScaleType)} may + * differ. + */ +public class MoveImage extends Transition { + private static final String TAG = "MoveImage"; + private static final String PROPNAME_MATRIX = "android:moveImage:matrix"; + private static final String PROPNAME_BOUNDS = "android:moveImage:bounds"; + private static final String PROPNAME_CLIP = "android:moveImage:clip"; + private static final String PROPNAME_DRAWABLE = "android:moveImage:drawable"; + + private int[] mTempLoc = new int[2]; + + private static final String[] sTransitionProperties = { + PROPNAME_MATRIX, + PROPNAME_BOUNDS, + PROPNAME_CLIP, + PROPNAME_DRAWABLE, + }; + + private void captureValues(TransitionValues transitionValues) { + View view = transitionValues.view; + if (!(view instanceof ImageView) || view.getVisibility() != View.VISIBLE) { + return; + } + Map<String, Object> values = transitionValues.values; + + ViewGroup parent = (ViewGroup) view.getParent(); + parent.getLocationInWindow(mTempLoc); + int paddingLeft = view.getPaddingLeft(); + int paddingTop = view.getPaddingTop(); + int paddingRight = view.getPaddingRight(); + int paddingBottom = view.getPaddingBottom(); + int left = mTempLoc[0] + paddingLeft + view.getLeft() + Math.round(view.getTranslationX()); + int top = mTempLoc[1] + paddingTop + view.getTop() + Math.round(view.getTranslationY()); + int right = left + view.getWidth() - paddingRight - paddingLeft; + int bottom = top + view.getHeight() - paddingTop - paddingBottom; + + Rect bounds = new Rect(left, top, right, bottom); + values.put(PROPNAME_BOUNDS, bounds); + ImageView imageView = (ImageView) view; + Matrix matrix = getMatrix(imageView); + values.put(PROPNAME_MATRIX, matrix); + values.put(PROPNAME_CLIP, findClip(imageView)); + values.put(PROPNAME_DRAWABLE, imageView.getDrawable()); + } + + @Override + public void captureStartValues(TransitionValues transitionValues) { + captureValues(transitionValues); + } + + @Override + public void captureEndValues(TransitionValues transitionValues) { + captureValues(transitionValues); + } + + @Override + public String[] getTransitionProperties() { + return sTransitionProperties; + } + + /** + * Creates an Animator for ImageViews moving, changing dimensions, and/or changing + * {@link android.widget.ImageView.ScaleType}. + * @param sceneRoot The root of the transition hierarchy. + * @param startValues The values for a specific target in the start scene. + * @param endValues The values for the target in the end scene. + * @return An Animator to move an ImageView or null if the View is not an ImageView, + * the Drawable changed, the View is not VISIBLE, or there was no change. + */ + @Override + public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, + TransitionValues endValues) { + if (startValues == null || endValues == null + || startValues.values.get(PROPNAME_BOUNDS) == null + || endValues.values.get(PROPNAME_BOUNDS) == null + || startValues.values.get(PROPNAME_DRAWABLE) + != endValues.values.get(PROPNAME_DRAWABLE)) { + return null; + } + ArrayList<PropertyValuesHolder> changes = new ArrayList<PropertyValuesHolder>(); + + Matrix startMatrix = (Matrix) startValues.values.get(PROPNAME_MATRIX); + Matrix endMatrix = (Matrix) endValues.values.get(PROPNAME_MATRIX); + + if (!startMatrix.equals(endMatrix)) { + changes.add(PropertyValuesHolder.ofObject(MatrixClippedDrawable.MATRIX_PROPERTY, + new MatrixEvaluator(), startMatrix, endMatrix)); + } + + sceneRoot.getLocationInWindow(mTempLoc); + int rootX = mTempLoc[0]; + int rootY = mTempLoc[1]; + final ImageView imageView = (ImageView) endValues.view; + + Drawable drawable = imageView.getDrawable(); + + Rect startBounds = new Rect((Rect) startValues.values.get(PROPNAME_BOUNDS)); + Rect endBounds = new Rect((Rect) endValues.values.get(PROPNAME_BOUNDS)); + startBounds.offset(-rootX, -rootY); + endBounds.offset(-rootX, -rootY); + + if (!startBounds.equals(endBounds)) { + changes.add(PropertyValuesHolder.ofObject("bounds", new RectEvaluator(new Rect()), + startBounds, endBounds)); + } + + Rect startClip = (Rect) startValues.values.get(PROPNAME_CLIP); + Rect endClip = (Rect) endValues.values.get(PROPNAME_CLIP); + if (startClip != null || endClip != null) { + startClip = nonNullClip(startClip, sceneRoot, rootX, rootY); + endClip = nonNullClip(endClip, sceneRoot, rootX, rootY); + + expandClip(startBounds, startMatrix, startClip, endClip); + expandClip(endBounds, endMatrix, endClip, startClip); + boolean clipped = !startClip.contains(startBounds) || !endClip.contains(endBounds); + if (!clipped) { + startClip = null; + } else if (!startClip.equals(endClip)) { + changes.add(PropertyValuesHolder.ofObject(MatrixClippedDrawable.CLIP_PROPERTY, + new RectEvaluator(), startClip, endClip)); + } + } + + if (changes.isEmpty()) { + return null; + } + + drawable = drawable.getConstantState().newDrawable(); + final MatrixClippedDrawable matrixClippedDrawable = new MatrixClippedDrawable(drawable); + matrixClippedDrawable.setMatrix(startMatrix); + matrixClippedDrawable.setBounds(startBounds); + matrixClippedDrawable.setClipRect(startClip); + + imageView.setVisibility(View.INVISIBLE); + final ViewGroupOverlay overlay = sceneRoot.getOverlay(); + overlay.add(matrixClippedDrawable); + ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(matrixClippedDrawable, + changes.toArray(new PropertyValuesHolder[changes.size()])); + + AnimatorListenerAdapter listener = new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + imageView.setVisibility(View.VISIBLE); + overlay.remove(matrixClippedDrawable); + } + + @Override + public void onAnimationPause(Animator animation) { + imageView.setVisibility(View.VISIBLE); + overlay.remove(matrixClippedDrawable); + } + + @Override + public void onAnimationResume(Animator animation) { + imageView.setVisibility(View.INVISIBLE); + overlay.add(matrixClippedDrawable); + } + }; + + animator.addListener(listener); + animator.addPauseListener(listener); + + return animator; + } + + private static Rect nonNullClip(Rect clip, ViewGroup sceneRoot, int rootX, int rootY) { + if (clip != null) { + clip = new Rect(clip); + clip.offset(-rootX, -rootY); + } else { + clip = new Rect(0, 0, sceneRoot.getWidth(), sceneRoot.getHeight()); + } + return clip; + } + + private static void expandClip(Rect bounds, Matrix matrix, Rect clip, Rect otherClip) { + RectF boundsF = new RectF(bounds); + matrix.mapRect(boundsF); + clip.left = expandMinDimension(boundsF.left, clip.left, otherClip.left); + clip.top = expandMinDimension(boundsF.top, clip.top, otherClip.top); + clip.right = expandMaxDimension(boundsF.right, clip.right, otherClip.right); + clip.bottom = expandMaxDimension(boundsF.bottom, clip.bottom, otherClip.bottom); + } + + private static int expandMinDimension(float boundsDimension, int clipDimension, + int otherClipDimension) { + if (clipDimension > boundsDimension) { + // Already clipped in that dimension, return the clipped value + return clipDimension; + } + return Math.min(clipDimension, otherClipDimension); + } + + private static int expandMaxDimension(float boundsDimension, int clipDimension, + int otherClipDimension) { + return -expandMinDimension(-boundsDimension, -clipDimension, -otherClipDimension); + } + + private static Matrix getMatrix(ImageView imageView) { + Drawable drawable = imageView.getDrawable(); + int drawableWidth = drawable.getIntrinsicWidth(); + int drawableHeight = drawable.getIntrinsicHeight(); + ImageView.ScaleType scaleType = imageView.getScaleType(); + if (drawableWidth <= 0 || drawableHeight <= 0 || scaleType == ImageView.ScaleType.FIT_XY) { + return null; + } + return new Matrix(imageView.getImageMatrix()); + } + + private Rect findClip(ImageView imageView) { + if (imageView.getCropToPadding()) { + Rect clip = getClip(imageView); + clip.left += imageView.getPaddingLeft(); + clip.right -= imageView.getPaddingRight(); + clip.top += imageView.getPaddingTop(); + clip.bottom -= imageView.getPaddingBottom(); + return clip; + } else { + View view = imageView; + ViewParent viewParent; + while ((viewParent = view.getParent()) instanceof ViewGroup) { + ViewGroup viewGroup = (ViewGroup) viewParent; + if (viewGroup.getClipChildren()) { + Rect clip = getClip(view); + return clip; + } + view = viewGroup; + } + } + return null; + } + + private Rect getClip(View clipView) { + Rect clipBounds = clipView.getClipBounds(); + if (clipBounds == null) { + clipBounds = new Rect(clipView.getLeft(), clipView.getTop(), + clipView.getRight(), clipView.getBottom()); + } + + ViewParent parent = clipView.getParent(); + if (parent instanceof ViewGroup) { + ViewGroup parentViewGroup = (ViewGroup) parent; + parentViewGroup.getLocationInWindow(mTempLoc); + clipBounds.offset(mTempLoc[0], mTempLoc[1]); + } + + return clipBounds; + } + + @Override + public Transition clone() { + MoveImage clone = (MoveImage) super.clone(); + clone.mTempLoc = new int[2]; + return clone; + } + + private static class MatrixEvaluator implements TypeEvaluator<Matrix> { + static final Matrix sIdentity = new Matrix(); + float[] mTempStartValues = new float[9]; + float[] mTempEndValues = new float[9]; + Matrix mTempMatrix = new Matrix(); + + @Override + public Matrix evaluate(float fraction, Matrix startValue, Matrix endValue) { + if (startValue == null && endValue == null) { + return null; + } + if (startValue == null) { + startValue = sIdentity; + } else if (endValue == null) { + endValue = sIdentity; + } + startValue.getValues(mTempStartValues); + endValue.getValues(mTempEndValues); + for (int i = 0; i < 9; i++) { + float diff = mTempEndValues[i] - mTempStartValues[i]; + mTempEndValues[i] = mTempStartValues[i] + (fraction * diff); + } + mTempMatrix.setValues(mTempEndValues); + return mTempMatrix; + } + } +} diff --git a/core/java/android/transition/SidePropagation.java b/core/java/android/transition/SidePropagation.java new file mode 100644 index 000000000000..c331945b27ca --- /dev/null +++ b/core/java/android/transition/SidePropagation.java @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.transition; + +import android.graphics.Rect; +import android.util.FloatMath; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; + +/** + * A <code>TransitionPropagation</code> that propagates based on the distance to the side + * and, orthogonally, the distance to epicenter. If the transitioning View is visible in + * the start of the transition, then it will transition sooner when closer to the side and + * later when farther. If the view is not visible in the start of the transition, then + * it will transition later when closer to the side and sooner when farther from the edge. + * This is the default TransitionPropagation used with {@link android.transition.Slide}. + */ +public class SidePropagation extends VisibilityPropagation { + private static final String TAG = "SlidePropagation"; + + /** + * Transition propagates relative to the distance of the left side of the scene. + */ + public static final int LEFT = Slide.LEFT; + + /** + * Transition propagates relative to the distance of the top of the scene. + */ + public static final int TOP = Slide.TOP; + + /** + * Transition propagates relative to the distance of the right side of the scene. + */ + public static final int RIGHT = Slide.RIGHT; + + /** + * Transition propagates relative to the distance of the bottom of the scene. + */ + public static final int BOTTOM = Slide.BOTTOM; + + private float mPropagationSpeed = 4.0f; + private int mSide = BOTTOM; + + /** + * Sets the side that is used to calculate the transition propagation. If the transitioning + * View is visible in the start of the transition, then it will transition sooner when + * closer to the side and later when farther. If the view is not visible in the start of + * the transition, then it will transition later when closer to the side and sooner when + * farther from the edge. The default is {@link #BOTTOM}. + * + * @param side The side that is used to calculate the transition propagation. Must be one of + * {@link #LEFT}, {@link #TOP}, {@link #RIGHT}, or {@link #BOTTOM}. + */ + public void setSide(int side) { + mSide = side; + } + + /** + * Sets the speed at which transition propagation happens, relative to the duration of the + * Transition. A <code>propagationSpeed</code> of 1 means that a View centered at the side + * set in {@link #setSide(int)} and View centered at the opposite edge will have a difference + * in start delay of approximately the duration of the Transition. A speed of 2 means the + * start delay difference will be approximately half of the duration of the transition. A + * value of 0 is illegal, but negative values will invert the propagation. + * + * @param propagationSpeed The speed at which propagation occurs, relative to the duration + * of the transition. A speed of 4 means it works 4 times as fast + * as the duration of the transition. May not be 0. + */ + public void setPropagationSpeed(float propagationSpeed) { + if (propagationSpeed == 0) { + throw new IllegalArgumentException("propagationSpeed may not be 0"); + } + mPropagationSpeed = propagationSpeed; + } + + @Override + public long getStartDelay(ViewGroup sceneRoot, Transition transition, + TransitionValues startValues, TransitionValues endValues) { + if (startValues == null && endValues == null) { + return 0; + } + int directionMultiplier = 1; + Rect epicenter = transition.getEpicenter(); + TransitionValues positionValues; + if (endValues == null || getViewVisibility(startValues) == View.VISIBLE) { + positionValues = startValues; + directionMultiplier = -1; + } else { + positionValues = endValues; + } + + int viewCenterX = getViewX(positionValues); + int viewCenterY = getViewY(positionValues); + + int[] loc = new int[2]; + sceneRoot.getLocationOnScreen(loc); + int left = loc[0] + Math.round(sceneRoot.getTranslationX()); + int top = loc[1] + Math.round(sceneRoot.getTranslationY()); + int right = left + sceneRoot.getWidth(); + int bottom = top + sceneRoot.getHeight(); + + int epicenterX; + int epicenterY; + if (epicenter != null) { + epicenterX = epicenter.centerX(); + epicenterY = epicenter.centerY(); + } else { + epicenterX = (left + right) / 2; + epicenterY = (top + bottom) / 2; + } + + float distance = distance(viewCenterX, viewCenterY, epicenterX, epicenterY, + left, top, right, bottom); + float maxDistance = getMaxDistance(sceneRoot); + float distanceFraction = distance/maxDistance; + + return Math.round(transition.getDuration() * directionMultiplier / mPropagationSpeed + * distanceFraction); + } + + private int distance(int viewX, int viewY, int epicenterX, int epicenterY, + int left, int top, int right, int bottom) { + int distance = 0; + switch (mSide) { + case LEFT: + distance = right - viewX + Math.abs(epicenterY - viewY); + break; + case TOP: + distance = bottom - viewY + Math.abs(epicenterX - viewX); + break; + case RIGHT: + distance = viewX - left + Math.abs(epicenterY - viewY); + break; + case BOTTOM: + distance = viewY - top + Math.abs(epicenterX - viewX); + break; + } + return distance; + } + + private int getMaxDistance(ViewGroup sceneRoot) { + switch (mSide) { + case LEFT: + case RIGHT: + return sceneRoot.getWidth(); + default: + return sceneRoot.getHeight(); + } + } +} diff --git a/core/java/android/transition/Slide.java b/core/java/android/transition/Slide.java index b38973ce122d..0ff8ddd4d1f9 100644 --- a/core/java/android/transition/Slide.java +++ b/core/java/android/transition/Slide.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 The Android Open Source Project + * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,53 +13,240 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package android.transition; import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.TimeInterpolator; +import android.animation.ValueAnimator; +import android.graphics.Rect; +import android.util.Log; +import android.util.Property; import android.view.View; import android.view.ViewGroup; import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; /** - * This transition captures the visibility of target objects before and - * after a scene change and animates any changes by sliding the target - * objects into or out of place. - * - * @hide + * This transition tracks changes to the visibility of target views in the + * start and end scenes and moves views in or out from one of the edges of the + * scene. Visibility is determined by both the + * {@link View#setVisibility(int)} state of the view as well as whether it + * is parented in the current view hierarchy. Disappearing Views are + * limited as described in {@link Visibility#onDisappear(android.view.ViewGroup, + * TransitionValues, int, TransitionValues, int)}. */ public class Slide extends Visibility { + private static final String TAG = "Slide"; - // TODO: Add parameter for sliding factor - it's hard-coded below + /** + * Move Views in or out of the left edge of the scene. + * @see #setSlideEdge(int) + */ + public static final int LEFT = 0; - private static final TimeInterpolator sAccelerator = new AccelerateInterpolator(); - private static final TimeInterpolator sDecelerator = new DecelerateInterpolator(); + /** + * Move Views in or out of the top edge of the scene. + * @see #setSlideEdge(int) + */ + public static final int TOP = 1; - @Override - public Animator onAppear(ViewGroup sceneRoot, - TransitionValues startValues, int startVisibility, - TransitionValues endValues, int endVisibility) { - View endView = (endValues != null) ? endValues.view : null; - endView.setTranslationY(-2 * endView.getHeight()); - ObjectAnimator anim = ObjectAnimator.ofFloat(endView, View.TRANSLATION_Y, - -2 * endView.getHeight(), 0); - anim.setInterpolator(sDecelerator); + /** + * Move Views in or out of the right edge of the scene. + * @see #setSlideEdge(int) + */ + public static final int RIGHT = 2; + + /** + * Move Views in or out of the bottom edge of the scene. This is the + * default slide direction. + * @see #setSlideEdge(int) + */ + public static final int BOTTOM = 3; + + private static final TimeInterpolator sDecelerate = new DecelerateInterpolator(); + private static final TimeInterpolator sAccelerate = new AccelerateInterpolator(); + + private int[] mTempLoc = new int[2]; + private CalculateSlide mSlideCalculator = sCalculateBottom; + + private interface CalculateSlide { + /** Returns the translation value for view when it out of the scene */ + float getGone(ViewGroup sceneRoot, View view); + + /** Returns the translation value for view when it is in the scene */ + float getHere(View view); + + /** Returns the property to animate translation */ + Property<View, Float> getProperty(); + } + + private static abstract class CalculateSlideHorizontal implements CalculateSlide { + @Override + public float getHere(View view) { + return view.getTranslationX(); + } + + @Override + public Property<View, Float> getProperty() { + return View.TRANSLATION_X; + } + } + + private static abstract class CalculateSlideVertical implements CalculateSlide { + @Override + public float getHere(View view) { + return view.getTranslationY(); + } + + @Override + public Property<View, Float> getProperty() { + return View.TRANSLATION_Y; + } + } + + private static final CalculateSlide sCalculateLeft = new CalculateSlideHorizontal() { + @Override + public float getGone(ViewGroup sceneRoot, View view) { + return view.getTranslationX() - sceneRoot.getWidth(); + } + }; + + private static final CalculateSlide sCalculateTop = new CalculateSlideVertical() { + @Override + public float getGone(ViewGroup sceneRoot, View view) { + return view.getTranslationY() - sceneRoot.getHeight(); + } + }; + + private static final CalculateSlide sCalculateRight = new CalculateSlideHorizontal() { + @Override + public float getGone(ViewGroup sceneRoot, View view) { + return view.getTranslationX() + sceneRoot.getWidth(); + } + }; + + private static final CalculateSlide sCalculateBottom = new CalculateSlideVertical() { + @Override + public float getGone(ViewGroup sceneRoot, View view) { + return view.getTranslationY() + sceneRoot.getHeight(); + } + }; + + /** + * Constructor using the default {@link android.transition.Slide#BOTTOM} + * slide edge direction. + */ + public Slide() { + setSlideEdge(BOTTOM); + } + + /** + * Constructor using the provided slide edge direction. + */ + public Slide(int slideEdge) { + setSlideEdge(slideEdge); + } + + /** + * Change the edge that Views appear and disappear from. + * @param slideEdge The edge of the scene to use for Views appearing and disappearing. + */ + public void setSlideEdge(int slideEdge) { + switch (slideEdge) { + case LEFT: + mSlideCalculator = sCalculateLeft; + break; + case TOP: + mSlideCalculator = sCalculateTop; + break; + case RIGHT: + mSlideCalculator = sCalculateRight; + break; + case BOTTOM: + mSlideCalculator = sCalculateBottom; + break; + default: + throw new IllegalArgumentException("Invalid slide direction"); + } + SidePropagation propagation = new SidePropagation(); + propagation.setSide(slideEdge); + setPropagation(propagation); + } + + private Animator createAnimation(final View view, Property<View, Float> property, + float start, float end, float terminalValue, TimeInterpolator interpolator) { + view.setTranslationY(start); + if (start == end) { + return null; + } + final ObjectAnimator anim = ObjectAnimator.ofFloat(view, property, start, end); + + SlideAnimatorListener listener = new SlideAnimatorListener(view, terminalValue, end); + anim.addListener(listener); + anim.addPauseListener(listener); + anim.setInterpolator(interpolator); return anim; } @Override - public Animator onDisappear(ViewGroup sceneRoot, - TransitionValues startValues, int startVisibility, - TransitionValues endValues, int endVisibility) { - View startView = (startValues != null) ? startValues.view : null; - startView.setTranslationY(0); - ObjectAnimator anim = ObjectAnimator.ofFloat(startView, View.TRANSLATION_Y, 0, - -2 * startView.getHeight()); - anim.setInterpolator(sAccelerator); - return anim; + public Animator onAppear(ViewGroup sceneRoot, View view, + TransitionValues startValues, TransitionValues endValues) { + if (endValues == null) { + return null; + } + float end = mSlideCalculator.getHere(view); + float start = mSlideCalculator.getGone(sceneRoot, view); + return createAnimation(view, mSlideCalculator.getProperty(), start, end, end, sDecelerate); } + @Override + public Animator onDisappear(ViewGroup sceneRoot, View view, + TransitionValues startValues, TransitionValues endValues) { + float start = mSlideCalculator.getHere(view); + float end = mSlideCalculator.getGone(sceneRoot, view); + + return createAnimation(view, mSlideCalculator.getProperty(), start, end, start, + sAccelerate); + } + + private static class SlideAnimatorListener extends AnimatorListenerAdapter { + private boolean mCanceled = false; + private float mPausedY; + private final View mView; + private final float mEndY; + private final float mTerminalY; + + public SlideAnimatorListener(View view, float terminalY, float endY) { + mView = view; + mTerminalY = terminalY; + mEndY = endY; + } + + @Override + public void onAnimationCancel(Animator animator) { + mView.setTranslationY(mTerminalY); + mCanceled = true; + } + + @Override + public void onAnimationEnd(Animator animator) { + if (!mCanceled) { + mView.setTranslationY(mTerminalY); + } + } + + @Override + public void onAnimationPause(Animator animator) { + mPausedY = mView.getTranslationY(); + mView.setTranslationY(mEndY); + } + + @Override + public void onAnimationResume(Animator animator) { + mView.setTranslationY(mPausedY); + } + } } diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java index c88b4c03c6e8..b7ae31e4e5fb 100644 --- a/core/java/android/transition/Transition.java +++ b/core/java/android/transition/Transition.java @@ -19,10 +19,12 @@ package android.transition; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.TimeInterpolator; +import android.graphics.Rect; import android.util.ArrayMap; import android.util.Log; import android.util.LongSparseArray; import android.util.SparseArray; +import android.util.SparseLongArray; import android.view.SurfaceView; import android.view.TextureView; import android.view.View; @@ -60,10 +62,18 @@ import java.util.List; * <p>Transitions can be declared in XML resource files inside the <code>res/transition</code> * directory. Transition resources consist of a tag name for one of the Transition * subclasses along with attributes to define some of the attributes of that transition. - * For example, here is a minimal resource file that declares a {@link ChangeBounds} transition:</p> + * For example, here is a minimal resource file that declares a {@link ChangeBounds} transition: * * {@sample development/samples/ApiDemos/res/transition/changebounds.xml ChangeBounds} * + * <p>{@link android.transition.Explode} transition:</p> + * + * {@sample development/samples/ApiDemos/res/transition/explode.xml Explode} + * + * <p>{@link android.transition.MoveImage} transition:</p> + * + * {@sample development/samples/ApiDemos/res/transition/move_image.xml MoveImage} + * * <p>Note that attributes for the transition are not required, just as they are * optional when declared in code; Transitions created from XML resources will use * the same defaults as their code-created equivalents. Here is a slightly more @@ -87,7 +97,8 @@ import java.util.List; * * Further information on XML resource descriptions for transitions can be found for * {@link android.R.styleable#Transition}, {@link android.R.styleable#TransitionSet}, - * {@link android.R.styleable#TransitionTarget}, and {@link android.R.styleable#Fade}. + * {@link android.R.styleable#TransitionTarget}, {@link android.R.styleable#Fade}, and + * {@link android.R.styleable#Slide}. * */ public abstract class Transition implements Cloneable { @@ -149,6 +160,13 @@ public abstract class Transition implements Cloneable { // to be run in runAnimators() ArrayList<Animator> mAnimators = new ArrayList<Animator>(); + // The function for calculating the Animation start delay. + TransitionPropagation mPropagation; + + // The rectangular region for Transitions like Explode and TransitionPropagations + // like CircularPropagation + EpicenterCallback mEpicenterCallback; + /** * Constructs a Transition object with no target objects. A transition with * no targets defaults to running on all target objects in the scene hierarchy @@ -435,6 +453,9 @@ public abstract class Transition implements Cloneable { endValuesList.add(end); } ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators(); + long minStartDelay = Long.MAX_VALUE; + int minAnimator = mAnimators.size(); + SparseLongArray startDelays = new SparseLongArray(); for (int i = 0; i < startValuesList.size(); ++i) { TransitionValues start = startValuesList.get(i); TransitionValues end = endValuesList.get(i); @@ -497,6 +518,12 @@ public abstract class Transition implements Cloneable { view = (start != null) ? start.view : null; } if (animator != null) { + if (mPropagation != null) { + long delay = mPropagation + .getStartDelay(sceneRoot, this, start, end); + startDelays.put(mAnimators.size(), delay); + minStartDelay = Math.min(delay, minStartDelay); + } AnimationInfo info = new AnimationInfo(view, getName(), sceneRoot.getWindowId(), infoValues); runningAnimators.put(animator, info); @@ -506,6 +533,14 @@ public abstract class Transition implements Cloneable { } } } + if (minStartDelay != 0) { + for (int i = 0; i < startDelays.size(); i++) { + int index = startDelays.keyAt(i); + Animator animator = mAnimators.get(index); + long delay = startDelays.valueAt(i) - minStartDelay + animator.getStartDelay(); + animator.setStartDelay(delay); + } + } } /** @@ -565,7 +600,7 @@ public abstract class Transition implements Cloneable { /** * This is called internally once all animations have been set up by the - * transition hierarchy. \ + * transition hierarchy. * * @hide */ @@ -1010,6 +1045,7 @@ public abstract class Transition implements Cloneable { } else { captureEndValues(values); } + capturePropagationValues(values); if (start) { mStartValues.viewValues.put(view, values); if (id >= 0) { @@ -1035,6 +1071,7 @@ public abstract class Transition implements Cloneable { } else { captureEndValues(values); } + capturePropagationValues(values); if (start) { mStartValues.viewValues.put(view, values); } else { @@ -1122,6 +1159,7 @@ public abstract class Transition implements Cloneable { } else { captureEndValues(values); } + capturePropagationValues(values); if (start) { if (!isListViewItem) { mStartValues.viewValues.put(view, values); @@ -1340,7 +1378,7 @@ public abstract class Transition implements Cloneable { animator.setDuration(getDuration()); } if (getStartDelay() >= 0) { - animator.setStartDelay(getStartDelay()); + animator.setStartDelay(getStartDelay() + animator.getStartDelay()); } if (getInterpolator() != null) { animator.setInterpolator(getInterpolator()); @@ -1473,6 +1511,98 @@ public abstract class Transition implements Cloneable { return this; } + /** + * Sets the callback to use to find the epicenter of a Transition. A null value indicates + * that there is no epicenter in the Transition and getEpicenter() will return null. + * Transitions like {@link android.transition.Explode} use a point or Rect to orient + * the direction of travel. This is called the epicenter of the Transition and is + * typically centered on a touched View. The + * {@link android.transition.Transition.EpicenterCallback} allows a Transition to + * dynamically retrieve the epicenter during a Transition. + * @param epicenterCallback The callback to use to find the epicenter of the Transition. + */ + public void setEpicenterCallback(EpicenterCallback epicenterCallback) { + mEpicenterCallback = epicenterCallback; + } + + /** + * Returns the callback used to find the epicenter of the Transition. + * Transitions like {@link android.transition.Explode} use a point or Rect to orient + * the direction of travel. This is called the epicenter of the Transition and is + * typically centered on a touched View. The + * {@link android.transition.Transition.EpicenterCallback} allows a Transition to + * dynamically retrieve the epicenter during a Transition. + * @return the callback used to find the epicenter of the Transition. + */ + public EpicenterCallback getEpicenterCallback() { + return mEpicenterCallback; + } + + /** + * Returns the epicenter as specified by the + * {@link android.transition.Transition.EpicenterCallback} or null if no callback exists. + * @return the epicenter as specified by the + * {@link android.transition.Transition.EpicenterCallback} or null if no callback exists. + * @see #setEpicenterCallback(android.transition.Transition.EpicenterCallback) + */ + public Rect getEpicenter() { + if (mEpicenterCallback == null) { + return null; + } + return mEpicenterCallback.getEpicenter(this); + } + + /** + * Sets the method for determining Animator start delays. + * When a Transition affects several Views like {@link android.transition.Explode} or + * {@link android.transition.Slide}, there may be a desire to have a "wave-front" effect + * such that the Animator start delay depends on position of the View. The + * TransitionPropagation specifies how the start delays are calculated. + * @param transitionPropagation The class used to determine the start delay of + * Animators created by this Transition. A null value + * indicates that no delay should be used. + */ + public void setPropagation(TransitionPropagation transitionPropagation) { + mPropagation = transitionPropagation; + } + + /** + * Returns the {@link android.transition.TransitionPropagation} used to calculate Animator start + * delays. + * When a Transition affects several Views like {@link android.transition.Explode} or + * {@link android.transition.Slide}, there may be a desire to have a "wave-front" effect + * such that the Animator start delay depends on position of the View. The + * TransitionPropagation specifies how the start delays are calculated. + * @return the {@link android.transition.TransitionPropagation} used to calculate Animator start + * delays. This is null by default. + */ + public TransitionPropagation getPropagation() { + return mPropagation; + } + + /** + * Captures TransitionPropagation values for the given view and the + * hierarchy underneath it. + */ + void capturePropagationValues(TransitionValues transitionValues) { + if (mPropagation != null) { + String[] propertyNames = mPropagation.getPropagationProperties(); + if (propertyNames == null) { + return; + } + boolean containsAll = true; + for (int i = 0; i < propertyNames.length; i++) { + if (!transitionValues.values.containsKey(propertyNames[i])) { + containsAll = false; + break; + } + } + if (!containsAll) { + mPropagation.captureValues(transitionValues); + } + } + } + Transition setSceneRoot(ViewGroup sceneRoot) { mSceneRoot = sceneRoot; return this; @@ -1710,4 +1840,28 @@ public abstract class Transition implements Cloneable { } } + /** + * Class to get the epicenter of Transition. Use + * {@link #setEpicenterCallback(android.transition.Transition.EpicenterCallback)} to + * set the callback used to calculate the epicenter of the Transition. Override + * {@link #getEpicenter()} to return the rectangular region in screen coordinates of + * the epicenter of the transition. + * @see #setEpicenterCallback(android.transition.Transition.EpicenterCallback) + */ + public static abstract class EpicenterCallback { + + /** + * Implementers must override to return the epicenter of the Transition in screen + * coordinates. Transitions like {@link android.transition.Explode} depend upon + * an epicenter for the Transition. In Explode, Views move toward or away from the + * center of the epicenter Rect along the vector between the epicenter and the center + * of the View appearing and disappearing. Some Transitions, such as + * {@link android.transition.Fade} pay no attention to the epicenter. + * + * @param transition The transition for which the epicenter applies. + * @return The Rect region of the epicenter of <code>transition</code> or null if + * there is no epicenter. + */ + public abstract Rect getEpicenter(Transition transition); + } } diff --git a/core/java/android/transition/TransitionInflater.java b/core/java/android/transition/TransitionInflater.java index 912f2ed88962..f675c6a3f1ae 100644 --- a/core/java/android/transition/TransitionInflater.java +++ b/core/java/android/transition/TransitionInflater.java @@ -20,7 +20,6 @@ import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; -import android.text.TextUtils; import android.util.AttributeSet; import android.util.Xml; import android.view.InflateException; @@ -146,7 +145,13 @@ public class TransitionInflater { transition = new ChangeBounds(); newTransition = true; } else if ("slide".equals(name)) { - transition = new Slide(); + transition = createSlideTransition(attrs); + newTransition = true; + } else if ("explode".equals(name)) { + transition = new Explode(); + newTransition = true; + } else if ("moveImage".equals(name)) { + transition = new MoveImage(); newTransition = true; } else if ("autoTransition".equals(name)) { transition = new AutoTransition(); @@ -189,6 +194,15 @@ public class TransitionInflater { return transition; } + private Slide createSlideTransition(AttributeSet attrs) { + TypedArray a = mContext.obtainStyledAttributes(attrs, + com.android.internal.R.styleable.Slide); + int edge = a.getInt(com.android.internal.R.styleable.Slide_slideEdge, Slide.BOTTOM); + Slide slide = new Slide(edge); + a.recycle(); + return slide; + } + private void getTargetIds(XmlPullParser parser, AttributeSet attrs, Transition transition) throws XmlPullParserException, IOException { diff --git a/core/java/android/transition/TransitionPropagation.java b/core/java/android/transition/TransitionPropagation.java new file mode 100644 index 000000000000..9a481c2dca92 --- /dev/null +++ b/core/java/android/transition/TransitionPropagation.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.transition; + +import android.graphics.Rect; +import android.view.ViewGroup; + +/** + * Extend <code>TransitionPropagation</code> to customize start delays for Animators created + * in {@link android.transition.Transition#createAnimator(ViewGroup, + * TransitionValues, TransitionValues)}. A Transition such as {@link android.transition.Explode} + * defaults to using {@link android.transition.CircularPropagation} and Views closer to the + * epicenter will move out of the scene later and into the scene sooner than Views farther + * from the epicenter, giving the appearance of inertia. With no TransitionPropagation, all + * Views will react simultaneously to the start of the transition. + * + * @see Transition#setPropagation(TransitionPropagation) + * @see Transition#getEpicenter() + */ +public abstract class TransitionPropagation { + /** + * Called by Transition to alter the Animator start delay. All start delays will be adjusted + * such that the minimum becomes zero. + * @param sceneRoot The root of the View hierarchy running the transition. + * @param transition The transition that created the Animator + * @param startValues The values for a specific target in the start scene. + * @param endValues The values for the target in the end scene. + * @return A start delay to use with the Animator created by <code>transition</code>. The + * delay will be offset by the minimum delay of all <code>TransitionPropagation</code>s + * used in the Transition so that the smallest delay will be 0. Returned values may be + * negative. + */ + public abstract long getStartDelay(ViewGroup sceneRoot, Transition transition, + TransitionValues startValues, TransitionValues endValues); + + /** + * Captures the values in the start or end scene for the properties that this + * transition propagation monitors. These values are then passed as the startValues + * or endValues structure in a later call to + * {@link #getStartDelay(ViewGroup, Transition, TransitionValues, TransitionValues)}. + * The main concern for an implementation is what the + * properties are that the transition cares about and what the values are + * for all of those properties. The start and end values will be compared + * later during the + * {@link #getStartDelay(ViewGroup, Transition, TransitionValues, TransitionValues)}. + * method to determine the start delay. + * + * <p>Subclasses must implement this method. The method should only be called by the + * transition system; it is not intended to be called from external classes.</p> + * + * @param transitionValues The holder for any values that the Transition + * wishes to store. Values are stored in the <code>values</code> field + * of this TransitionValues object and are keyed from + * a String value. For example, to store a view's rotation value, + * a transition might call + * <code>transitionValues.values.put("appname:transitionname:rotation", + * view.getRotation())</code>. The target view will already be stored in + * the transitionValues structure when this method is called. + */ + public abstract void captureValues(TransitionValues transitionValues); + + /** + * Returns the set of property names stored in the {@link TransitionValues} + * object passed into {@link #captureValues(TransitionValues)} that + * this transition propagation cares about for the purposes of preventing + * duplicate capturing of property values. + + * <p>A <code>TransitionPropagation</code> must override this method to prevent + * duplicate capturing of values and must contain at least one </p> + * + * @return An array of property names as described in the class documentation for + * {@link TransitionValues}. + */ + public abstract String[] getPropagationProperties() ; +} diff --git a/core/java/android/transition/TransitionSet.java b/core/java/android/transition/TransitionSet.java index 19d6b3d3c393..966b24dc033a 100644 --- a/core/java/android/transition/TransitionSet.java +++ b/core/java/android/transition/TransitionSet.java @@ -17,6 +17,7 @@ package android.transition; import android.animation.TimeInterpolator; +import android.graphics.Rect; import android.util.AndroidRuntimeException; import android.view.View; import android.view.ViewGroup; @@ -315,6 +316,15 @@ public class TransitionSet extends Transition { } } + @Override + void capturePropagationValues(TransitionValues transitionValues) { + super.capturePropagationValues(transitionValues); + int numTransitions = mTransitions.size(); + for (int i = 0; i < numTransitions; ++i) { + mTransitions.get(i).capturePropagationValues(transitionValues); + } + } + /** @hide */ @Override public void pause(View sceneRoot) { @@ -365,6 +375,24 @@ public class TransitionSet extends Transition { } @Override + public void setPropagation(TransitionPropagation propagation) { + super.setPropagation(propagation); + int numTransitions = mTransitions.size(); + for (int i = 0; i < numTransitions; ++i) { + mTransitions.get(i).setPropagation(propagation); + } + } + + @Override + public void setEpicenterCallback(EpicenterCallback epicenterCallback) { + super.setEpicenterCallback(epicenterCallback); + int numTransitions = mTransitions.size(); + for (int i = 0; i < numTransitions; ++i) { + mTransitions.get(i).setEpicenterCallback(epicenterCallback); + } + } + + @Override String toString(String indent) { String result = super.toString(indent); for (int i = 0; i < mTransitions.size(); ++i) { @@ -383,5 +411,4 @@ public class TransitionSet extends Transition { } return clone; } - } diff --git a/core/java/android/transition/Visibility.java b/core/java/android/transition/Visibility.java index 44f92cd7ed4a..7783b6f0ff1c 100644 --- a/core/java/android/transition/Visibility.java +++ b/core/java/android/transition/Visibility.java @@ -17,6 +17,7 @@ package android.transition; import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.view.View; import android.view.ViewGroup; @@ -29,15 +30,20 @@ import android.view.ViewGroup; * information to determine the specific animations to run when visibility * changes occur. Subclasses should implement one or both of the methods * {@link #onAppear(ViewGroup, TransitionValues, int, TransitionValues, int)}, - * {@link #onDisappear(ViewGroup, TransitionValues, int, TransitionValues, int)}, + * {@link #onDisappear(ViewGroup, TransitionValues, int, TransitionValues, int)} or + * {@link #onAppear(ViewGroup, View, TransitionValues, TransitionValues)}, + * {@link #onDisappear(ViewGroup, View, TransitionValues, TransitionValues)}. */ public abstract class Visibility extends Transition { private static final String PROPNAME_VISIBILITY = "android:visibility:visibility"; private static final String PROPNAME_PARENT = "android:visibility:parent"; + private static final String PROPNAME_SCREEN_LOCATION = "android:visibility:screenLocation"; + private static final String[] sTransitionProperties = { PROPNAME_VISIBILITY, PROPNAME_PARENT, + PROPNAME_SCREEN_LOCATION, }; private static class VisibilityInfo { @@ -58,6 +64,9 @@ public abstract class Visibility extends Transition { int visibility = transitionValues.view.getVisibility(); transitionValues.values.put(PROPNAME_VISIBILITY, visibility); transitionValues.values.put(PROPNAME_PARENT, transitionValues.view.getParent()); + int[] loc = new int[2]; + transitionValues.view.getLocationOnScreen(loc); + transitionValues.values.put(PROPNAME_SCREEN_LOCATION, loc); } @Override @@ -179,8 +188,11 @@ public abstract class Visibility extends Transition { } /** - * The default implementation of this method does nothing. Subclasses - * should override if they need to create an Animator when targets appear. + * The default implementation of this method calls + * {@link #onAppear(ViewGroup, View, TransitionValues, TransitionValues)}. + * Subclasses should override this method or + * {@link #onAppear(ViewGroup, View, TransitionValues, TransitionValues)}. + * if they need to create an Animator when targets appear. * The method should only be called by the Visibility class; it is * not intended to be called from external classes. * @@ -196,15 +208,53 @@ public abstract class Visibility extends Transition { public Animator onAppear(ViewGroup sceneRoot, TransitionValues startValues, int startVisibility, TransitionValues endValues, int endVisibility) { + return onAppear(sceneRoot, endValues.view, startValues, endValues); + } + + /** + * The default implementation of this method returns a null Animator. Subclasses should + * override this method to make targets appear with the desired transition. The + * method should only be called from + * {@link #onAppear(ViewGroup, TransitionValues, int, TransitionValues, int)}. + * + * @param sceneRoot The root of the transition hierarchy + * @param view The View to make appear. This will be in the target scene's View hierarchy and + * will be VISIBLE. + * @param startValues The target values in the start scene + * @param endValues The target values in the end scene + * @return An Animator to be started at the appropriate time in the + * overall transition for this scene change. A null value means no animation + * should be run. + */ + public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues, + TransitionValues endValues) { return null; } /** - * The default implementation of this method does nothing. Subclasses - * should override if they need to create an Animator when targets disappear. + * Subclasses should override this method or + * {@link #onDisappear(ViewGroup, View, TransitionValues, TransitionValues)} + * if they need to create an Animator when targets disappear. * The method should only be called by the Visibility class; it is * not intended to be called from external classes. - * + * <p> + * The default implementation of this method attempts to find a View to use to call + * {@link #onDisappear(ViewGroup, View, TransitionValues, TransitionValues)}, + * based on the situation of the View in the View hierarchy. For example, + * if a View was simply removed from its parent, then the View will be added + * into a {@link android.view.ViewGroupOverlay} and passed as the <code>view</code> + * parameter in {@link #onDisappear(ViewGroup, View, TransitionValues, TransitionValues)}. + * If a visible View is changed to be {@link View#GONE} or {@link View#INVISIBLE}, + * then it can be used as the <code>view</code> and the visibility will be changed + * to {@link View#VISIBLE} for the duration of the animation. However, if a View + * is in a hierarchy which is also altering its visibility, the situation can be + * more complicated. In general, if a view that is no longer in the hierarchy in + * the end scene still has a parent (so its parent hierarchy was removed, but it + * was not removed from its parent), then it will be left alone to avoid side-effects from + * improperly removing it from its parent. The only exception to this is if + * the previous {@link Scene} was {@link Scene#getSceneForLayout(ViewGroup, int, + * android.content.Context) created from a layout resource file}, then it is considered + * safe to un-parent the starting scene view in order to make it disappear.</p> * * @param sceneRoot The root of the transition hierarchy * @param startValues The target values in the start scene @@ -218,6 +268,144 @@ public abstract class Visibility extends Transition { public Animator onDisappear(ViewGroup sceneRoot, TransitionValues startValues, int startVisibility, TransitionValues endValues, int endVisibility) { + View startView = (startValues != null) ? startValues.view : null; + View endView = (endValues != null) ? endValues.view : null; + View overlayView = null; + View viewToKeep = null; + if (endView == null || endView.getParent() == null) { + if (endView != null) { + // endView was removed from its parent - add it to the overlay + overlayView = endView; + } else if (startView != null) { + // endView does not exist. Use startView only under certain + // conditions, because placing a view in an overlay necessitates + // it being removed from its current parent + if (startView.getParent() == null) { + // no parent - safe to use + overlayView = startView; + } else if (startView.getParent() instanceof View && + startView.getParent().getParent() == null) { + View startParent = (View) startView.getParent(); + int id = startParent.getId(); + if (id != View.NO_ID && sceneRoot.findViewById(id) != null && mCanRemoveViews) { + // no parent, but its parent is unparented but the parent + // hierarchy has been replaced by a new hierarchy with the same id + // and it is safe to un-parent startView + overlayView = startView; + } + } + } + } else { + // visibility change + if (endVisibility == View.INVISIBLE) { + viewToKeep = endView; + } else { + // Becoming GONE + if (startView == endView) { + viewToKeep = endView; + } else { + overlayView = startView; + } + } + } + final int finalVisibility = endVisibility; + final ViewGroup finalSceneRoot = sceneRoot; + + if (overlayView != null) { + // TODO: Need to do this for general case of adding to overlay + int[] screenLoc = (int[]) startValues.values.get(PROPNAME_SCREEN_LOCATION); + int screenX = screenLoc[0]; + int screenY = screenLoc[1]; + int[] loc = new int[2]; + sceneRoot.getLocationOnScreen(loc); + overlayView.offsetLeftAndRight((screenX - loc[0]) - overlayView.getLeft()); + overlayView.offsetTopAndBottom((screenY - loc[1]) - overlayView.getTop()); + sceneRoot.getOverlay().add(overlayView); + Animator animator = onDisappear(sceneRoot, overlayView, startValues, endValues); + if (animator == null) { + sceneRoot.getOverlay().remove(overlayView); + } else { + final View finalOverlayView = overlayView; + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + finalSceneRoot.getOverlay().remove(finalOverlayView); + } + + @Override + public void onAnimationPause(Animator animation) { + finalSceneRoot.getOverlay().remove(finalOverlayView); + } + + @Override + public void onAnimationResume(Animator animation) { + finalSceneRoot.getOverlay().add(finalOverlayView); + } + }); + } + return animator; + } + + if (viewToKeep != null) { + viewToKeep.setVisibility(View.VISIBLE); + Animator animator = onDisappear(sceneRoot, viewToKeep, startValues, endValues); + if (animator == null) { + viewToKeep.setVisibility(finalVisibility); + } else { + final View finalViewToKeep = viewToKeep; + animator.addListener(new AnimatorListenerAdapter() { + boolean mCanceled = false; + + @Override + public void onAnimationPause(Animator animation) { + if (!mCanceled) { + finalViewToKeep.setVisibility(finalVisibility); + } + } + + @Override + public void onAnimationResume(Animator animation) { + if (!mCanceled) { + finalViewToKeep.setVisibility(View.VISIBLE); + } + } + + @Override + public void onAnimationCancel(Animator animation) { + mCanceled = true; + } + + @Override + public void onAnimationEnd(Animator animation) { + if (!mCanceled) { + finalViewToKeep.setVisibility(finalVisibility); + } + } + }); + } + return animator; + } + return null; + } + + /** + * The default implementation of this method returns a null Animator. Subclasses should + * override this method to make targets disappear with the desired transition. The + * method should only be called from + * {@link #onDisappear(ViewGroup, TransitionValues, int, TransitionValues, int)}. + * + * @param sceneRoot The root of the transition hierarchy + * @param view The View to make disappear. This will be in the target scene's View + * hierarchy or in an {@link android.view.ViewGroupOverlay} and will be + * VISIBLE. + * @param startValues The target values in the start scene + * @param endValues The target values in the end scene + * @return An Animator to be started at the appropriate time in the + * overall transition for this scene change. A null value means no animation + * should be run. + */ + public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues, + TransitionValues endValues) { return null; } } diff --git a/core/java/android/transition/VisibilityPropagation.java b/core/java/android/transition/VisibilityPropagation.java new file mode 100644 index 000000000000..0326d4725ecd --- /dev/null +++ b/core/java/android/transition/VisibilityPropagation.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.transition; + +import android.view.View; + +/** + * Base class for <code>TransitionPropagation</code>s that care about + * View Visibility and the center position of the View. + */ +public abstract class VisibilityPropagation extends TransitionPropagation { + + /** + * The property key used for {@link android.view.View#getVisibility()}. + */ + private static final String PROPNAME_VISIBILITY = "android:visibilityPropagation:visibility"; + + /** + * The property key used for the center of the View in screen coordinates. This is an + * int[2] with the index 0 taking the x coordinate and index 1 taking the y coordinate. + */ + private static final String PROPNAME_VIEW_CENTER = "android:visibilityPropagation:center"; + + private static final String[] VISIBILITY_PROPAGATION_VALUES = { + PROPNAME_VISIBILITY, + PROPNAME_VIEW_CENTER, + }; + + @Override + public void captureValues(TransitionValues values) { + View view = values.view; + values.values.put(PROPNAME_VISIBILITY, view.getVisibility()); + int[] loc = new int[2]; + view.getLocationOnScreen(loc); + loc[0] += Math.round(view.getTranslationX()); + loc[0] += view.getWidth() / 2; + loc[1] += Math.round(view.getTranslationY()); + loc[1] += view.getHeight() / 2; + values.values.put(PROPNAME_VIEW_CENTER, loc); + } + + @Override + public String[] getPropagationProperties() { + return VISIBILITY_PROPAGATION_VALUES; + } + + /** + * Returns {@link android.view.View#getVisibility()} for the View at the time the values + * were captured. + * @param values The TransitionValues captured at the start or end of the Transition. + * @return {@link android.view.View#getVisibility()} for the View at the time the values + * were captured. + */ + public int getViewVisibility(TransitionValues values) { + if (values == null) { + return View.GONE; + } + Integer visibility = (Integer) values.values.get(PROPNAME_VISIBILITY); + if (visibility == null) { + return View.GONE; + } + return visibility; + } + + /** + * Returns the View's center x coordinate, relative to the screen, at the time the values + * were captured. + * @param values The TransitionValues captured at the start or end of the Transition. + * @return the View's center x coordinate, relative to the screen, at the time the values + * were captured. + */ + public int getViewX(TransitionValues values) { + return getViewCoordinate(values, 0); + } + + /** + * Returns the View's center y coordinate, relative to the screen, at the time the values + * were captured. + * @param values The TransitionValues captured at the start or end of the Transition. + * @return the View's center y coordinate, relative to the screen, at the time the values + * were captured. + */ + public int getViewY(TransitionValues values) { + return getViewCoordinate(values, 1); + } + + private static int getViewCoordinate(TransitionValues values, int coordinateIndex) { + if (values == null) { + return -1; + } + + int[] coordinates = (int[]) values.values.get(PROPNAME_VIEW_CENTER); + if (coordinates == null) { + return -1; + } + + return coordinates[coordinateIndex]; + } +} diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 0cd6325231f6..7bd1f56cd6c2 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -18,7 +18,6 @@ package android.view; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.ActivityOptions; import android.content.Context; import android.content.res.Configuration; import android.content.res.TypedArray; @@ -1390,11 +1389,11 @@ public abstract class Window { * @param options Options to set or null for none * @hide */ - public void setTransitionOptions(ActivityOptions options, SceneTransitionListener listener) { + public void setTransitionOptions(Bundle options, SceneTransitionListener listener) { } /** - * A callback for Activity transitions to be told when the shared element is ready to be shown + * A callback for Window transitions to be told when the shared element is ready to be shown * and start the transition to its target location. * @hide */ @@ -1407,27 +1406,57 @@ public abstract class Window { } /** - * Controls when the Activity enter scene is triggered and the background is faded in. If - * triggerEarly is true, the enter scene will begin as soon as possible and the background - * will fade in when all shared elements are ready to begin transitioning. If triggerEarly is - * false, the Activity enter scene and background fade will be triggered when the calling - * Activity's exit transition completes. - * - * @param triggerEarly Set to true to have the Activity enter scene transition in as early as - * possible or set to false to wait for the calling Activity to exit first. + * Controls how the Activity's start Scene is faded in and when the enter scene + * is triggered to start. + * <p>When allow is true, the enter Scene will begin as soon as possible + * and the background will fade in when all shared elements are ready to begin + * transitioning. If allow is false, the Activity enter Scene and + * background fade will be triggered when the calling Activity's exit transition + * completes.</p> + * @param allow Set to true to have the Activity enter scene transition in + * as early as possible or set to false to wait for the calling + * Activity to exit first. The default value is true. */ - public void setTriggerEarlyEnterTransition(boolean triggerEarly) { + public void setAllowOverlappingEnterTransition(boolean allow) { + } + + /** + * Controls how the Activity's Scene fades out and when the calling Activity's + * enter scene is triggered when finishing to return to a calling Activity. + * <p>When allow is true, the Scene will fade out quickly + * and inform the calling Activity to transition in when the fade completes. + * When allow is false, the calling Activity will transition in after + * the Activity's Scene has exited. + * </p> + * @param allow Set to true to have the Activity fade out as soon as possible + * and transition in the calling Activity. The default value is + * true. + */ + public void setAllowOverlappingExitTransition(boolean allow) { } /** * Start the exit transition. * @hide */ - public Bundle startExitTransition(ActivityOptions options) { + public Bundle startExitTransitionToCallee(Bundle options) { return null; } /** + * Starts the transition back to the calling Activity. + * onTransitionEnd will be called on the current thread if there is no exit transition. + * @hide + */ + public void startExitTransitionToCaller(Runnable onTransitionEnd) { + onTransitionEnd.run(); + } + + /** @hide */ + public void restoreViewVisibilityAfterTransitionToCallee() { + } + + /** * On entering Activity Scene transitions, shared element names may be mapped from a * source Activity's specified name to a unique shared element name in the View hierarchy. * Under most circumstances, mapping is not necessary - a single View will have the diff --git a/core/java/android/widget/ActionMenuPresenter.java b/core/java/android/widget/ActionMenuPresenter.java index 1f405a7c3563..e4575e51651d 100644 --- a/core/java/android/widget/ActionMenuPresenter.java +++ b/core/java/android/widget/ActionMenuPresenter.java @@ -640,16 +640,6 @@ public class ActionMenuPresenter extends BaseMenuPresenter } @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) { - // Fill available height - heightMeasureSpec = MeasureSpec.makeMeasureSpec( - MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.EXACTLY); - } - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - } - - @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); info.setCanOpenPopup(true); diff --git a/core/java/android/widget/ActionMenuView.java b/core/java/android/widget/ActionMenuView.java index 32c7086b70e8..3975edf002b4 100644 --- a/core/java/android/widget/ActionMenuView.java +++ b/core/java/android/widget/ActionMenuView.java @@ -20,6 +20,7 @@ import android.content.res.Configuration; import android.util.AttributeSet; import android.view.Gravity; import android.view.Menu; +import android.view.MenuItem; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; @@ -27,6 +28,7 @@ import android.view.accessibility.AccessibilityEvent; import com.android.internal.view.menu.ActionMenuItemView; import com.android.internal.view.menu.MenuBuilder; import com.android.internal.view.menu.MenuItemImpl; +import com.android.internal.view.menu.MenuPresenter; import com.android.internal.view.menu.MenuView; /** @@ -50,6 +52,8 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo private int mMinCellSize; private int mGeneratedItemPadding; + private OnMenuItemClickListener mOnMenuItemClickListener; + public ActionMenuView(Context context) { this(context, null); } @@ -78,6 +82,10 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo } } + public void setOnMenuItemClickListener(OnMenuItemClickListener listener) { + mOnMenuItemClickListener = listener; + } + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // If we've been given an exact size to match, apply special formatting during layout. @@ -96,11 +104,11 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo mMenu.onItemsChanged(true); } - if (mFormatItems) { + final int childCount = getChildCount(); + if (mFormatItems && childCount > 0) { onMeasureExactFormat(widthMeasureSpec, heightMeasureSpec); } else { // Previous measurement at exact format may have set margins - reset them. - final int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { final View child = getChildAt(i); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); @@ -559,9 +567,11 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo if (mMenu == null) { final Context context = getContext(); mMenu = new MenuBuilder(context); + mMenu.setCallback(new MenuBuilderCallback()); mPresenter = new ActionMenuPresenter(context); - mPresenter.initForMenu(context, mMenu); mPresenter.setMenuView(this); + mPresenter.setCallback(new ActionMenuPresenterCallback()); + mMenu.addMenuPresenter(mPresenter); } return mMenu; @@ -591,6 +601,44 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo return false; } + /** + * Interface responsible for receiving menu item click events if the items themselves + * do not have individual item click listeners. + */ + public interface OnMenuItemClickListener { + /** + * This method will be invoked when a menu item is clicked if the item itself did + * not already handle the event. + * + * @param item {@link MenuItem} that was clicked + * @return <code>true</code> if the event was handled, <code>false</code> otherwise. + */ + public boolean onMenuItemClick(MenuItem item); + } + + private class MenuBuilderCallback implements MenuBuilder.Callback { + @Override + public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) { + return mOnMenuItemClickListener != null && + mOnMenuItemClickListener.onMenuItemClick(item); + } + + @Override + public void onMenuModeChange(MenuBuilder menu) { + } + } + + private class ActionMenuPresenterCallback implements ActionMenuPresenter.Callback { + @Override + public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { + } + + @Override + public boolean onOpenSubMenu(MenuBuilder subMenu) { + return false; + } + } + /** @hide */ public interface ActionMenuChildView { public boolean needsDividerBefore(); diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java new file mode 100644 index 000000000000..075feba22ef7 --- /dev/null +++ b/core/java/android/widget/Toolbar.java @@ -0,0 +1,1048 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package android.widget; + +import android.annotation.NonNull; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.view.Gravity; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewDebug; +import android.view.ViewGroup; +import com.android.internal.R; + +import java.util.ArrayList; +import java.util.List; + +/** + * A standard toolbar for use within application content. + * + * <p>A Toolbar is a generalization of {@link android.app.ActionBar action bars} for use + * within application layouts. While an action bar is traditionally part of an + * {@link android.app.Activity Activity's} opaque window decor controlled by the framework, + * a Toolbar may be placed at any arbitrary level of nesting within a view hierarchy. + * An application may choose to designate a Toolbar as the action bar for an Activity + * using the {@link android.app.Activity#setActionBar(Toolbar) setActionBar()} method.</p> + * + * <p>Toolbar supports a more focused feature set than ActionBar. From start to end, a toolbar + * may contain a combination of the following optional elements: + * + * <ul> + * <li><em>A navigation button.</em> This may be an Up arrow, navigation menu toggle, close, + * collapse, done or another glyph of the app's choosing. This button should always be used + * to access other navigational destinations within the container of the Toolbar and + * its signified content or otherwise leave the current context signified by the Toolbar.</li> + * <li><em>A branded logo image.</em> This may extend to the height of the bar and can be + * arbitrarily wide.</li> + * <li><em>A title and subtitle.</em> The title should be a signpost for the Toolbar's current + * position in the navigation hierarchy and the content contained there. The subtitle, + * if present should indicate any extended information about the current content. + * If an app uses a logo image it should strongly consider omitting a title and subtitle.</li> + * <li><em>One or more custom views.</em> The application may add arbitrary child views + * to the Toolbar. They will appear at this position within the layout. If a child view's + * {@link LayoutParams} indicates a {@link Gravity} value of + * {@link Gravity#CENTER_HORIZONTAL CENTER_HORIZONTAL} the view will attempt to center + * within the available space remaining in the Toolbar after all other elements have been + * measured.</li> + * <li><em>An {@link ActionMenuView action menu}.</em> The menu of actions will pin to the + * end of the Toolbar offering a few + * <a href="http://developer.android.com/design/patterns/actionbar.html#ActionButtons"> + * frequent, important or typical</a> actions along with an optional overflow menu for + * additional actions.</li> + * </ul> + * </p> + * + * <p>In modern Android UIs developers should lean more on a visually distinct color scheme for + * toolbars than on their application icon. The use of application icon plus title as a standard + * layout is discouraged on API 21 devices and newer.</p> + */ +public class Toolbar extends ViewGroup { + private ActionMenuView mMenuView; + private TextView mTitleTextView; + private TextView mSubtitleTextView; + private ImageButton mNavButtonView; + private ImageView mLogoView; + + private int mTitleTextAppearance; + private int mSubtitleTextAppearance; + private int mTitleMarginStart; + private int mTitleMarginEnd; + private int mTitleMarginTop; + private int mTitleMarginBottom; + + private int mGravity = Gravity.START | Gravity.CENTER_VERTICAL; + + private CharSequence mTitleText; + private CharSequence mSubtitleText; + + // Clear me after use. + private final ArrayList<View> mTempViews = new ArrayList<View>(); + + private OnMenuItemClickListener mOnMenuItemClickListener; + + private final ActionMenuView.OnMenuItemClickListener mMenuViewItemClickListener = + new ActionMenuView.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + if (mOnMenuItemClickListener != null) { + return mOnMenuItemClickListener.onMenuItemClick(item); + } + return false; + } + }; + + public Toolbar(Context context) { + this(context, null); + } + + public Toolbar(Context context, AttributeSet attrs) { + this(context, attrs, com.android.internal.R.attr.toolbarStyle); + } + + public Toolbar(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public Toolbar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + + final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Toolbar, + defStyleAttr, defStyleRes); + + mTitleTextAppearance = a.getResourceId(R.styleable.Toolbar_titleTextAppearance, 0); + mSubtitleTextAppearance = a.getResourceId(R.styleable.Toolbar_subtitleTextAppearance, 0); + mGravity = a.getInteger(R.styleable.Toolbar_gravity, mGravity); + mTitleMarginStart = mTitleMarginEnd = Math.max(0, a.getDimensionPixelOffset( + R.styleable.Toolbar_titleMargins, -1)); + + final int marginStart = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMarginStart, -1); + if (marginStart >= 0) { + mTitleMarginStart = marginStart; + } + + final int marginEnd = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMarginEnd, -1); + if (marginEnd >= 0) { + mTitleMarginEnd = marginEnd; + } + + final int marginTop = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMarginTop, -1); + if (marginTop >= 0) { + mTitleMarginTop = marginTop; + } + + final int marginBottom = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMarginBottom, + -1); + if (marginBottom >= 0) { + mTitleMarginBottom = marginBottom; + } + + final CharSequence title = a.getText(R.styleable.Toolbar_title); + if (!TextUtils.isEmpty(title)) { + setTitle(title); + } + + final CharSequence subtitle = a.getText(R.styleable.Toolbar_subtitle); + if (!TextUtils.isEmpty(subtitle)) { + setSubtitle(title); + } + a.recycle(); + } + + /** + * Set a logo drawable from a resource id. + * + * <p>This drawable should generally take the place of title text. The logo cannot be + * clicked. Apps using a logo should also supply a description using + * {@link #setLogoDescription(int)}.</p> + * + * @param resId ID of a drawable resource + */ + public void setLogo(int resId) { + setLogo(getContext().getDrawable(resId)); + } + + /** + * Set a logo drawable. + * + * <p>This drawable should generally take the place of title text. The logo cannot be + * clicked. Apps using a logo should also supply a description using + * {@link #setLogoDescription(int)}.</p> + * + * @param drawable Drawable to use as a logo + */ + public void setLogo(Drawable drawable) { + if (drawable != null) { + if (mLogoView == null) { + mLogoView = new ImageView(getContext()); + } + if (mLogoView.getParent() == null) { + addSystemView(mLogoView); + } + } else if (mLogoView != null && mLogoView.getParent() != null) { + removeView(mLogoView); + } + if (mLogoView != null) { + mLogoView.setImageDrawable(drawable); + } + } + + /** + * Return the current logo drawable. + * + * @return The current logo drawable + * @see #setLogo(int) + * @see #setLogo(android.graphics.drawable.Drawable) + */ + public Drawable getLogo() { + return mLogoView != null ? mLogoView.getDrawable() : null; + } + + /** + * Set a description of the toolbar's logo. + * + * <p>This description will be used for accessibility or other similar descriptions + * of the UI.</p> + * + * @param resId String resource id + */ + public void setLogoDescription(int resId) { + setLogoDescription(getContext().getText(resId)); + } + + /** + * Set a description of the toolbar's logo. + * + * <p>This description will be used for accessibility or other similar descriptions + * of the UI.</p> + * + * @param description Description to set + */ + public void setLogoDescription(CharSequence description) { + if (!TextUtils.isEmpty(description) && mLogoView == null) { + mLogoView = new ImageView(getContext()); + } + if (mLogoView != null) { + mLogoView.setContentDescription(description); + } + } + + /** + * Return the description of the toolbar's logo. + * + * @return A description of the logo + */ + public CharSequence getLogoDescription() { + return mLogoView != null ? mLogoView.getContentDescription() : null; + } + + /** + * Return the current title displayed in the toolbar. + * + * @return The current title + */ + public CharSequence getTitle() { + return mTitleText; + } + + /** + * Set the title of this toolbar. + * + * <p>A title should be used as the anchor for a section of content. It should + * describe or name the content being viewed.</p> + * + * @param resId Resource ID of a string to set as the title + */ + public void setTitle(int resId) { + setTitle(getContext().getText(resId)); + } + + /** + * Set the title of this toolbar. + * + * <p>A title should be used as the anchor for a section of content. It should + * describe or name the content being viewed.</p> + * + * @param title Title to set + */ + public void setTitle(CharSequence title) { + if (!TextUtils.isEmpty(title)) { + if (mTitleTextView == null) { + final Context context = getContext(); + mTitleTextView = new TextView(context); + mTitleTextView.setTextAppearance(context, mTitleTextAppearance); + } + if (mTitleTextView.getParent() == null) { + addSystemView(mTitleTextView); + } + } else if (mTitleTextView != null && mTitleTextView.getParent() != null) { + removeView(mTitleTextView); + } + if (mTitleTextView != null) { + mTitleTextView.setText(title); + } + mTitleText = title; + } + + /** + * Return the subtitle of this toolbar. + * + * @return The current subtitle + */ + public CharSequence getSubtitle() { + return mSubtitleText; + } + + /** + * Set the subtitle of this toolbar. + * + * <p>Subtitles should express extended information about the current content.</p> + * + * @param resId String resource ID + */ + public void setSubtitle(int resId) { + setSubtitle(getContext().getText(resId)); + } + + /** + * Set the subtitle of this toolbar. + * + * <p>Subtitles should express extended information about the current content.</p> + * + * @param subtitle Subtitle to set + */ + public void setSubtitle(CharSequence subtitle) { + if (!TextUtils.isEmpty(subtitle)) { + if (mSubtitleTextView == null) { + final Context context = getContext(); + mSubtitleTextView = new TextView(context); + mSubtitleTextView.setTextAppearance(context, mSubtitleTextAppearance); + } + if (mSubtitleTextView.getParent() == null) { + addSystemView(mSubtitleTextView); + } + } else if (mSubtitleTextView != null && mSubtitleTextView.getParent() != null) { + removeView(mSubtitleTextView); + } + if (mSubtitleTextView != null) { + mSubtitleTextView.setText(subtitle); + } + mSubtitleText = subtitle; + } + + /** + * Set the icon to use for the toolbar's navigation button. + * + * <p>The navigation button appears at the start of the toolbar if present. Setting an icon + * will make the navigation button visible.</p> + * + * <p>If you use a navigation icon you should also set a description for its action using + * {@link #setNavigationDescription(int)}. This is used for accessibility and tooltips.</p> + * + * @param resId Resource ID of a drawable to set + */ + public void setNavigationIcon(int resId) { + setNavigationIcon(getContext().getDrawable(resId)); + } + + /** + * Set the icon to use for the toolbar's navigation button. + * + * <p>The navigation button appears at the start of the toolbar if present. Setting an icon + * will make the navigation button visible.</p> + * + * <p>If you use a navigation icon you should also set a description for its action using + * {@link #setNavigationDescription(int)}. This is used for accessibility and tooltips.</p> + * + * @param icon Drawable to set + */ + public void setNavigationIcon(Drawable icon) { + if (icon != null) { + ensureNavButtonView(); + if (mNavButtonView.getParent() == null) { + addSystemView(mNavButtonView); + } + } else if (mNavButtonView != null && mNavButtonView.getParent() != null) { + removeView(mNavButtonView); + } + if (mNavButtonView != null) { + mNavButtonView.setImageDrawable(icon); + } + } + + /** + * Return the current drawable used as the navigation icon. + * + * @return The navigation icon drawable + */ + public Drawable getNavigationIcon() { + return mNavButtonView != null ? mNavButtonView.getDrawable() : null; + } + + /** + * Set a description for the navigation button. + * + * <p>This description string is used for accessibility, tooltips and other facilities + * to improve discoverability.</p> + * + * @param resId Resource ID of a string to set + */ + public void setNavigationDescription(int resId) { + setNavigationDescription(getContext().getText(resId)); + } + + /** + * Set a description for the navigation button. + * + * <p>This description string is used for accessibility, tooltips and other facilities + * to improve discoverability.</p> + * + * @param description String to set as the description + */ + public void setNavigationDescription(CharSequence description) { + if (!TextUtils.isEmpty(description)) { + ensureNavButtonView(); + } + if (mNavButtonView != null) { + mNavButtonView.setContentDescription(description); + } + } + + /** + * Set a listener to respond to navigation events. + * + * <p>This listener will be called whenever the user clicks the navigation button + * at the start of the toolbar. An icon must be set for the navigation button to appear.</p> + * + * @param listener Listener to set + * @see #setNavigationIcon(android.graphics.drawable.Drawable) + */ + public void setNavigationOnClickListener(OnClickListener listener) { + ensureNavButtonView(); + mNavButtonView.setOnClickListener(listener); + } + + /** + * Return the Menu shown in the toolbar. + * + * <p>Applications that wish to populate the toolbar's menu can do so from here. To use + * an XML menu resource, use {@link #inflateMenu(int)}.</p> + * + * @return The toolbar's Menu + */ + public Menu getMenu() { + if (mMenuView == null) { + mMenuView = new ActionMenuView(getContext()); + mMenuView.setOnMenuItemClickListener(mMenuViewItemClickListener); + addSystemView(mMenuView); + } + return mMenuView.getMenu(); + } + + private MenuInflater getMenuInflater() { + return new MenuInflater(getContext()); + } + + /** + * Inflate a menu resource into this toolbar. + * + * <p>Inflate an XML menu resource into this toolbar. Existing items in the menu will not + * be modified or removed.</p> + * + * @param resId ID of a menu resource to inflate + */ + public void inflateMenu(int resId) { + getMenuInflater().inflate(resId, getMenu()); + } + + /** + * Set a listener to respond to menu item click events. + * + * <p>This listener will be invoked whenever a user selects a menu item from + * the action buttons presented at the end of the toolbar or the associated overflow.</p> + * + * @param listener Listener to set + */ + public void setOnMenuItemClickListener(OnMenuItemClickListener listener) { + mOnMenuItemClickListener = listener; + } + + private void ensureNavButtonView() { + if (mNavButtonView == null) { + mNavButtonView = new ImageButton(getContext(), null, R.attr.borderlessButtonStyle); + } + } + + private void addSystemView(View v) { + final LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT); + lp.mViewType = LayoutParams.SYSTEM; + addView(v, lp); + } + + @Override + protected Parcelable onSaveInstanceState() { + SavedState state = new SavedState(super.onSaveInstanceState()); + return state; + } + + @Override + protected void onRestoreInstanceState(Parcelable state) { + final SavedState ss = (SavedState) state; + super.onRestoreInstanceState(ss.getSuperState()); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int width = 0; + int height = 0; + int childState = 0; + + // System views measure first. + + if (shouldLayout(mNavButtonView)) { + measureChildWithMargins(mNavButtonView, widthMeasureSpec, width, heightMeasureSpec, 0); + width += mNavButtonView.getMeasuredWidth() + getHorizontalMargins(mNavButtonView); + height = Math.max(height, mNavButtonView.getMeasuredHeight() + + getVerticalMargins(mNavButtonView)); + childState = combineMeasuredStates(childState, mNavButtonView.getMeasuredState()); + } + + if (shouldLayout(mMenuView)) { + measureChildWithMargins(mMenuView, widthMeasureSpec, width, heightMeasureSpec, 0); + width += mMenuView.getMeasuredWidth() + getHorizontalMargins(mMenuView); + height = Math.max(height, mMenuView.getMeasuredHeight() + + getVerticalMargins(mMenuView)); + childState = combineMeasuredStates(childState, mMenuView.getMeasuredState()); + } + + if (shouldLayout(mLogoView)) { + measureChildWithMargins(mLogoView, widthMeasureSpec, width, heightMeasureSpec, 0); + width += mLogoView.getMeasuredWidth() + getHorizontalMargins(mLogoView); + height = Math.max(height, mLogoView.getMeasuredHeight() + + getVerticalMargins(mLogoView)); + childState = combineMeasuredStates(childState, mLogoView.getMeasuredState()); + } + + int titleWidth = 0; + int titleHeight = 0; + final int titleVertMargins = mTitleMarginTop + mTitleMarginBottom; + final int titleHorizMargins = mTitleMarginStart + mTitleMarginEnd; + if (shouldLayout(mTitleTextView)) { + measureChildWithMargins(mTitleTextView, widthMeasureSpec, width + titleHorizMargins, + heightMeasureSpec, titleVertMargins); + titleWidth = mTitleTextView.getMeasuredWidth() + getHorizontalMargins(mTitleTextView); + titleHeight = mTitleTextView.getMeasuredHeight() + getVerticalMargins(mTitleTextView); + childState = combineMeasuredStates(childState, mTitleTextView.getMeasuredState()); + } + if (shouldLayout(mSubtitleTextView)) { + measureChildWithMargins(mSubtitleTextView, widthMeasureSpec, width + titleHorizMargins, + heightMeasureSpec, titleHeight + titleVertMargins); + titleWidth = Math.max(titleWidth, mSubtitleTextView.getMeasuredWidth() + + getHorizontalMargins(mSubtitleTextView)); + titleHeight += mSubtitleTextView.getMeasuredHeight() + + getVerticalMargins(mSubtitleTextView); + childState = combineMeasuredStates(childState, mSubtitleTextView.getMeasuredState()); + } + + width += titleWidth; + height = Math.max(height, titleHeight); + + final int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + final View child = getChildAt(i); + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + if (lp.mViewType == LayoutParams.SYSTEM || !shouldLayout(child)) { + // We already got all system views above. Skip them and GONE views. + continue; + } + + measureChildWithMargins(child, widthMeasureSpec, width, heightMeasureSpec, 0); + width += child.getMeasuredWidth() + getHorizontalMargins(child); + height = Math.max(height, child.getMeasuredHeight() + getVerticalMargins(child)); + childState = combineMeasuredStates(childState, child.getMeasuredState()); + } + + // Measurement already took padding into account for available space for the children, + // add it in for the final size. + width += getPaddingLeft() + getPaddingRight(); + height += getPaddingTop() + getPaddingBottom(); + + final int measuredWidth = resolveSizeAndState( + Math.max(width, getSuggestedMinimumWidth()), + widthMeasureSpec, childState & MEASURED_STATE_MASK); + final int measuredHeight = resolveSizeAndState( + Math.max(height, getSuggestedMinimumHeight()), + heightMeasureSpec, childState << MEASURED_HEIGHT_STATE_SHIFT); + setMeasuredDimension(measuredWidth, measuredHeight); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + final boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL; + final int width = getWidth(); + final int height = getHeight(); + final int paddingLeft = getPaddingLeft(); + final int paddingRight = getPaddingRight(); + final int paddingTop = getPaddingTop(); + final int paddingBottom = getPaddingBottom(); + int left = paddingLeft; + int right = width - paddingRight; + + if (shouldLayout(mNavButtonView)) { + if (isRtl) { + right = layoutChildRight(mNavButtonView, right); + } else { + left = layoutChildLeft(mNavButtonView, left); + } + } + + if (shouldLayout(mMenuView)) { + if (isRtl) { + left = layoutChildLeft(mMenuView, left); + } else { + right = layoutChildRight(mMenuView, right); + } + } + + if (shouldLayout(mLogoView)) { + if (isRtl) { + right = layoutChildRight(mLogoView, right); + } else { + left = layoutChildLeft(mLogoView, left); + } + } + + final boolean layoutTitle = shouldLayout(mTitleTextView); + final boolean layoutSubtitle = shouldLayout(mSubtitleTextView); + int titleHeight = 0; + if (layoutTitle) { + final LayoutParams lp = (LayoutParams) mTitleTextView.getLayoutParams(); + titleHeight += lp.topMargin + mTitleTextView.getMeasuredHeight() + lp.bottomMargin; + } + if (layoutSubtitle) { + final LayoutParams lp = (LayoutParams) mSubtitleTextView.getLayoutParams(); + titleHeight += lp.bottomMargin + mTitleTextView.getMeasuredHeight() + lp.bottomMargin; + } + + if (layoutTitle || layoutSubtitle) { + int titleTop; + switch (mGravity & Gravity.VERTICAL_GRAVITY_MASK) { + case Gravity.TOP: + titleTop = getPaddingTop(); + break; + default: + case Gravity.CENTER_VERTICAL: + final View child = layoutTitle ? mTitleTextView : mSubtitleTextView; + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + final int space = height - paddingTop - paddingBottom; + int spaceAbove = (space - titleHeight) / 2; + if (spaceAbove < lp.topMargin + mTitleMarginTop) { + spaceAbove = lp.topMargin + mTitleMarginTop; + } else { + final int spaceBelow = height - paddingBottom - titleHeight - + spaceAbove - paddingTop; + if (spaceBelow < lp.bottomMargin + mTitleMarginBottom) { + spaceAbove = Math.max(0, spaceAbove - + (lp.bottomMargin + mTitleMarginBottom - spaceBelow)); + } + } + titleTop = paddingTop + spaceAbove; + break; + case Gravity.BOTTOM: + titleTop = height - paddingBottom - titleHeight; + break; + } + if (isRtl) { + int titleRight = right; + int subtitleRight = right; + titleTop += mTitleMarginTop; + if (layoutTitle) { + final LayoutParams lp = (LayoutParams) mTitleTextView.getLayoutParams(); + titleRight -= lp.rightMargin + mTitleMarginStart; + titleTop += lp.topMargin; + final int titleLeft = titleRight - mTitleTextView.getMeasuredWidth(); + final int titleBottom = titleTop + mTitleTextView.getMeasuredHeight(); + mTitleTextView.layout(titleLeft, titleTop, titleRight, titleBottom); + titleRight = titleLeft - lp.leftMargin - mTitleMarginEnd; + titleTop = titleBottom + lp.bottomMargin; + } + if (layoutSubtitle) { + final LayoutParams lp = (LayoutParams) mSubtitleTextView.getLayoutParams(); + subtitleRight -= lp.rightMargin + mTitleMarginStart; + titleTop += lp.topMargin; + final int subtitleLeft = subtitleRight - mSubtitleTextView.getMeasuredWidth(); + final int subtitleBottom = titleTop + mSubtitleTextView.getMeasuredHeight(); + mSubtitleTextView.layout(subtitleLeft, titleTop, subtitleRight, subtitleBottom); + subtitleRight = subtitleRight - lp.leftMargin - mTitleMarginEnd; + titleTop = subtitleBottom + lp.bottomMargin; + } + right = Math.max(titleRight, subtitleRight); + } else { + int titleLeft = left; + int subtitleLeft = left; + titleTop += mTitleMarginTop; + if (layoutTitle) { + final LayoutParams lp = (LayoutParams) mTitleTextView.getLayoutParams(); + titleLeft += lp.leftMargin + mTitleMarginStart; + titleTop += lp.topMargin; + final int titleRight = titleLeft + mTitleTextView.getMeasuredWidth(); + final int titleBottom = titleTop + mTitleTextView.getMeasuredHeight(); + mTitleTextView.layout(titleLeft, titleTop, titleRight, titleBottom); + titleLeft = titleRight + lp.rightMargin + mTitleMarginEnd; + titleTop = titleBottom + lp.bottomMargin; + } + if (layoutSubtitle) { + final LayoutParams lp = (LayoutParams) mSubtitleTextView.getLayoutParams(); + subtitleLeft += lp.leftMargin + mTitleMarginStart; + titleTop += lp.topMargin; + final int subtitleRight = subtitleLeft + mSubtitleTextView.getMeasuredWidth(); + final int subtitleBottom = titleTop + mSubtitleTextView.getMeasuredHeight(); + mSubtitleTextView.layout(subtitleLeft, titleTop, subtitleRight, subtitleBottom); + subtitleLeft = subtitleRight + lp.rightMargin + mTitleMarginEnd; + titleTop = subtitleBottom + lp.bottomMargin; + } + left = Math.max(titleLeft, subtitleLeft); + } + } + + // Get all remaining children sorted for layout. This is all prepared + // such that absolute layout direction can be used below. + + addCustomViewsWithGravity(mTempViews, Gravity.LEFT); + final int leftViewsCount = mTempViews.size(); + for (int i = 0; i < leftViewsCount; i++) { + left = layoutChildLeft(getChildAt(i), left); + } + + addCustomViewsWithGravity(mTempViews, Gravity.RIGHT); + final int rightViewsCount = mTempViews.size(); + for (int i = 0; i < rightViewsCount; i++) { + right = layoutChildRight(getChildAt(i), right); + } + + // Centered views try to center with respect to the whole bar, but views pinned + // to the left or right can push the mass of centered views to one side or the other. + addCustomViewsWithGravity(mTempViews, Gravity.CENTER); + final int centerViewsWidth = getViewListMeasuredWidth(mTempViews); + final int parentCenter = paddingLeft + (width - paddingLeft - paddingRight) / 2; + final int halfCenterViewsWidth = centerViewsWidth / 2; + int centerLeft = parentCenter - halfCenterViewsWidth; + final int centerRight = centerLeft + centerViewsWidth; + if (centerLeft < left) { + centerLeft = left; + } else if (centerRight > right) { + centerLeft -= centerRight - right; + } + + final int centerViewsCount = mTempViews.size(); + for (int i = 0; i < centerViewsCount; i++) { + centerLeft = layoutChildLeft(getChildAt(i), centerLeft); + } + mTempViews.clear(); + } + + private int getViewListMeasuredWidth(List<View> views) { + int width = 0; + final int count = views.size(); + for (int i = 0; i < count; i++) { + final View v = views.get(i); + final LayoutParams lp = (LayoutParams) v.getLayoutParams(); + width += lp.leftMargin + v.getMeasuredWidth() + lp.rightMargin; + } + return width; + } + + private int layoutChildLeft(View child, int left) { + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + left += lp.leftMargin; + int top = getChildTop(child); + child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight()); + left += lp.rightMargin; + return left; + } + + private int layoutChildRight(View child, int right) { + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + right -= lp.rightMargin; + int top = getChildTop(child); + child.layout(right - child.getMeasuredWidth(), top, right, top + child.getMeasuredHeight()); + right -= lp.leftMargin; + return right; + } + + private int getChildTop(View child) { + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + switch (getChildVerticalGravity(lp.gravity)) { + case Gravity.TOP: + return getPaddingTop(); + + case Gravity.BOTTOM: + return getPaddingBottom() - child.getMeasuredHeight() - lp.bottomMargin; + + default: + case Gravity.CENTER_VERTICAL: + final int paddingTop = getPaddingTop(); + final int paddingBottom = getPaddingBottom(); + final int height = getHeight(); + final int childHeight = child.getMeasuredHeight(); + final int space = height - paddingTop - paddingBottom; + int spaceAbove = (space - childHeight) / 2; + if (spaceAbove < lp.topMargin) { + spaceAbove = lp.topMargin; + } else { + final int spaceBelow = height - paddingBottom - childHeight - + spaceAbove - paddingTop; + if (spaceBelow < lp.bottomMargin) { + spaceAbove = Math.max(0, spaceAbove - (lp.bottomMargin - spaceBelow)); + } + } + return paddingTop + spaceAbove; + } + } + + private int getChildVerticalGravity(int gravity) { + final int vgrav = gravity & Gravity.VERTICAL_GRAVITY_MASK; + switch (vgrav) { + case Gravity.TOP: + case Gravity.BOTTOM: + case Gravity.CENTER_VERTICAL: + return vgrav; + default: + return mGravity & Gravity.VERTICAL_GRAVITY_MASK; + } + } + + /** + * Prepare a list of non-SYSTEM child views. If the layout direction is RTL + * this will be in reverse child order. + * + * @param views List to populate. It will be cleared before use. + * @param gravity Horizontal gravity to match against + */ + private void addCustomViewsWithGravity(List<View> views, int gravity) { + final boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL; + final int childCount = getChildCount(); + final int absGrav = Gravity.getAbsoluteGravity(gravity, getLayoutDirection()); + + views.clear(); + + if (isRtl) { + for (int i = childCount - 1; i >= 0; i--) { + final View child = getChildAt(i); + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + if (lp.mViewType != LayoutParams.SYSTEM && shouldLayout(child) && + getChildHorizontalGravity(lp.gravity) == absGrav) { + views.add(child); + } + + } + } else { + for (int i = 0; i < childCount; i++) { + final View child = getChildAt(i); + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + if (lp.mViewType != LayoutParams.SYSTEM && shouldLayout(child) && + getChildHorizontalGravity(lp.gravity) == absGrav) { + views.add(child); + } + } + } + } + + private int getChildHorizontalGravity(int gravity) { + final int ld = getLayoutDirection(); + final int absGrav = Gravity.getAbsoluteGravity(gravity, ld); + final int hGrav = absGrav & Gravity.HORIZONTAL_GRAVITY_MASK; + switch (hGrav) { + case Gravity.LEFT: + case Gravity.RIGHT: + case Gravity.CENTER_HORIZONTAL: + return hGrav; + default: + return ld == LAYOUT_DIRECTION_RTL ? Gravity.RIGHT : Gravity.LEFT; + } + } + + private boolean shouldLayout(View view) { + return view != null && view.getParent() == this && view.getVisibility() != GONE; + } + + private int getHorizontalMargins(View v) { + final MarginLayoutParams mlp = (MarginLayoutParams) v.getLayoutParams(); + return mlp.getMarginStart() + mlp.getMarginEnd(); + } + + private int getVerticalMargins(View v) { + final MarginLayoutParams mlp = (MarginLayoutParams) v.getLayoutParams(); + return mlp.topMargin + mlp.bottomMargin; + } + + @Override + public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { + return super.generateLayoutParams(attrs); + } + + @Override + protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { + if (p instanceof LayoutParams) { + return new LayoutParams((LayoutParams) p); + } else if (p instanceof MarginLayoutParams) { + return new LayoutParams((MarginLayoutParams) p); + } else { + return new LayoutParams(p); + } + } + + @Override + protected ViewGroup.LayoutParams generateDefaultLayoutParams() { + return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + } + + @Override + protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { + return super.checkLayoutParams(p) && p instanceof LayoutParams; + } + + private static boolean isCustomView(View child) { + return ((LayoutParams) child.getLayoutParams()).mViewType == LayoutParams.CUSTOM; + } + + /** + * Interface responsible for receiving menu item click events if the items themselves + * do not have individual item click listeners. + */ + public interface OnMenuItemClickListener { + /** + * This method will be invoked when a menu item is clicked if the item itself did + * not already handle the event. + * + * @param item {@link MenuItem} that was clicked + * @return <code>true</code> if the event was handled, <code>false</code> otherwise. + */ + public boolean onMenuItemClick(MenuItem item); + } + + /** + * Layout information for child views of Toolbars. + * + * @attr ref android.R.styleable#Toolbar_LayoutParams_layout_gravity + */ + public static class LayoutParams extends MarginLayoutParams { + /** + * Gravity for the view associated with these LayoutParams. + * + * @see android.view.Gravity + */ + @ViewDebug.ExportedProperty(category = "layout", mapping = { + @ViewDebug.IntToString(from = -1, to = "NONE"), + @ViewDebug.IntToString(from = Gravity.NO_GRAVITY, to = "NONE"), + @ViewDebug.IntToString(from = Gravity.TOP, to = "TOP"), + @ViewDebug.IntToString(from = Gravity.BOTTOM, to = "BOTTOM"), + @ViewDebug.IntToString(from = Gravity.LEFT, to = "LEFT"), + @ViewDebug.IntToString(from = Gravity.RIGHT, to = "RIGHT"), + @ViewDebug.IntToString(from = Gravity.START, to = "START"), + @ViewDebug.IntToString(from = Gravity.END, to = "END"), + @ViewDebug.IntToString(from = Gravity.CENTER_VERTICAL, to = "CENTER_VERTICAL"), + @ViewDebug.IntToString(from = Gravity.FILL_VERTICAL, to = "FILL_VERTICAL"), + @ViewDebug.IntToString(from = Gravity.CENTER_HORIZONTAL, to = "CENTER_HORIZONTAL"), + @ViewDebug.IntToString(from = Gravity.FILL_HORIZONTAL, to = "FILL_HORIZONTAL"), + @ViewDebug.IntToString(from = Gravity.CENTER, to = "CENTER"), + @ViewDebug.IntToString(from = Gravity.FILL, to = "FILL") + }) + public int gravity = Gravity.NO_GRAVITY; + + static final int CUSTOM = 0; + static final int SYSTEM = 1; + + int mViewType = CUSTOM; + + public LayoutParams(@NonNull Context c, AttributeSet attrs) { + super(c, attrs); + + TypedArray a = c.obtainStyledAttributes(attrs, + com.android.internal.R.styleable.Toolbar_LayoutParams); + gravity = a.getInt( + com.android.internal.R.styleable.Toolbar_LayoutParams_layout_gravity, + Gravity.NO_GRAVITY); + a.recycle(); + } + + public LayoutParams(int width, int height) { + super(width, height); + this.gravity = Gravity.CENTER_VERTICAL | Gravity.START; + } + + public LayoutParams(int width, int height, int gravity) { + super(width, height); + this.gravity = gravity; + } + + public LayoutParams(int gravity) { + this(WRAP_CONTENT, MATCH_PARENT, gravity); + } + + public LayoutParams(LayoutParams source) { + super(source); + + this.gravity = source.gravity; + } + + public LayoutParams(MarginLayoutParams source) { + super(source); + } + + public LayoutParams(ViewGroup.LayoutParams source) { + super(source); + } + } + + static class SavedState extends BaseSavedState { + public SavedState(Parcel source) { + super(source); + } + + public SavedState(Parcelable superState) { + super(superState); + } + + @Override + public void writeToParcel(Parcel out, int flags) { + super.writeToParcel(out, flags); + } + + public static final Creator<SavedState> CREATOR = new Creator<SavedState>() { + + @Override + public SavedState createFromParcel(Parcel source) { + return new SavedState(source); + } + + @Override + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; + } +} diff --git a/core/java/com/android/internal/app/ToolbarActionBar.java b/core/java/com/android/internal/app/ToolbarActionBar.java new file mode 100644 index 000000000000..34156e5ec027 --- /dev/null +++ b/core/java/com/android/internal/app/ToolbarActionBar.java @@ -0,0 +1,452 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package com.android.internal.app; + +import android.annotation.Nullable; +import android.app.ActionBar; +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.drawable.Drawable; +import android.view.ActionMode; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.SpinnerAdapter; +import android.widget.Toolbar; + +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 boolean mLastMenuVisibility; + private ArrayList<OnMenuVisibilityListener> mMenuVisibilityListeners = + new ArrayList<OnMenuVisibilityListener>(); + + public ToolbarActionBar(Toolbar toolbar) { + mToolbar = toolbar; + } + + @Override + public void setCustomView(View view) { + setCustomView(view, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); + } + + @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; + } + + @Override + public void setCustomView(int resId) { + final LayoutInflater inflater = LayoutInflater.from(mToolbar.getContext()); + setCustomView(inflater.inflate(resId, mToolbar, false)); + } + + @Override + public void setIcon(int resId) { + mIconResId = resId; + mIconDrawable = null; + updateToolbarLogo(); + } + + @Override + public void setIcon(Drawable icon) { + mIconResId = 0; + mIconDrawable = icon; + updateToolbarLogo(); + } + + @Override + public void setLogo(int resId) { + mLogoResId = resId; + mLogoDrawable = null; + updateToolbarLogo(); + } + + @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); + } + + @Override + public void setStackedBackgroundDrawable(Drawable d) { + // This space for rent (do nothing) + } + + @Override + public void setSplitBackgroundDrawable(Drawable d) { + // This space for rent (do nothing) + } + + @Override + public void setHomeButtonEnabled(boolean enabled) { + // If the nav button on a Toolbar is present, it's enabled. No-op. + } + + @Override + public Context getThemedContext() { + return mToolbar.getContext(); + } + + @Override + public boolean isTitleTruncated() { + return super.isTitleTruncated(); + } + + @Override + public void setHomeAsUpIndicator(Drawable indicator) { + mToolbar.setNavigationIcon(indicator); + } + + @Override + public void setHomeAsUpIndicator(int resId) { + mToolbar.setNavigationIcon(resId); + } + + @Override + public void setHomeActionContentDescription(CharSequence description) { + mToolbar.setNavigationDescription(description); + } + + @Override + public void setDefaultDisplayHomeAsUpEnabled(boolean enabled) { + // Do nothing + } + + @Override + public void setHomeActionContentDescription(int resId) { + mToolbar.setNavigationDescription(resId); + } + + @Override + public void setShowHideAnimationEnabled(boolean enabled) { + // This space for rent; no-op. + } + + @Override + public void onConfigurationChanged(Configuration config) { + super.onConfigurationChanged(config); + } + + @Override + public ActionMode startActionMode(ActionMode.Callback callback) { + return mToolbar.startActionMode(callback); + } + + @Override + public void setListNavigationCallbacks(SpinnerAdapter adapter, OnNavigationListener callback) { + throw new UnsupportedOperationException( + "Navigation modes are not supported in toolbar action bars"); + } + + @Override + public void setSelectedNavigationItem(int position) { + throw new UnsupportedOperationException( + "Navigation modes are not supported in toolbar action bars"); + } + + @Override + public int getSelectedNavigationIndex() { + return -1; + } + + @Override + public int getNavigationItemCount() { + return 0; + } + + @Override + public void setTitle(CharSequence title) { + mTitle = title; + mTitleResId = 0; + updateToolbarTitle(); + } + + @Override + public void setTitle(int resId) { + mTitleResId = resId; + mTitle = null; + updateToolbarTitle(); + } + + @Override + public void setSubtitle(CharSequence subtitle) { + mSubtitle = subtitle; + mSubtitleResId = 0; + updateToolbarTitle(); + } + + @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); + } + + @Override + public void setDisplayOptions(@DisplayOptions int options) { + setDisplayOptions(options, 0xffffffff); + } + + @Override + public void setDisplayOptions(@DisplayOptions int options, @DisplayOptions int mask) { + final int oldOptions = mDisplayOptions; + mDisplayOptions = (options & mask) | (mDisplayOptions & ~mask); + final int optionsChanged = oldOptions ^ mDisplayOptions; + } + + @Override + public void setDisplayUseLogoEnabled(boolean useLogo) { + setDisplayOptions(useLogo ? DISPLAY_USE_LOGO : 0, DISPLAY_USE_LOGO); + } + + @Override + public void setDisplayShowHomeEnabled(boolean showHome) { + setDisplayOptions(showHome ? DISPLAY_SHOW_HOME : 0, DISPLAY_SHOW_HOME); + } + + @Override + public void setDisplayHomeAsUpEnabled(boolean showHomeAsUp) { + setDisplayOptions(showHomeAsUp ? DISPLAY_HOME_AS_UP : 0, DISPLAY_HOME_AS_UP); + } + + @Override + public void setDisplayShowTitleEnabled(boolean showTitle) { + setDisplayOptions(showTitle ? DISPLAY_SHOW_TITLE : 0, DISPLAY_SHOW_TITLE); + } + + @Override + public void setDisplayShowCustomEnabled(boolean showCustom) { + setDisplayOptions(showCustom ? DISPLAY_SHOW_CUSTOM : 0, DISPLAY_SHOW_CUSTOM); + } + + @Override + public void setBackgroundDrawable(@Nullable Drawable d) { + mToolbar.setBackground(d); + } + + @Override + public View getCustomView() { + return mCustomView; + } + + @Override + public CharSequence getTitle() { + return mToolbar.getTitle(); + } + + @Override + public CharSequence getSubtitle() { + return mToolbar.getSubtitle(); + } + + @Override + public int getNavigationMode() { + return NAVIGATION_MODE_STANDARD; + } + + @Override + public void setNavigationMode(@NavigationMode int mode) { + throw new UnsupportedOperationException( + "Navigation modes are not supported in toolbar action bars"); + } + + @Override + public int getDisplayOptions() { + return mDisplayOptions; + } + + @Override + public Tab newTab() { + throw new UnsupportedOperationException( + "Navigation modes are not supported in toolbar action bars"); + } + + @Override + public void addTab(Tab tab) { + throw new UnsupportedOperationException( + "Navigation modes are not supported in toolbar action bars"); + } + + @Override + public void addTab(Tab tab, boolean setSelected) { + throw new UnsupportedOperationException( + "Navigation modes are not supported in toolbar action bars"); + } + + @Override + public void addTab(Tab tab, int position) { + throw new UnsupportedOperationException( + "Navigation modes are not supported in toolbar action bars"); + } + + @Override + public void addTab(Tab tab, int position, boolean setSelected) { + throw new UnsupportedOperationException( + "Navigation modes are not supported in toolbar action bars"); + } + + @Override + public void removeTab(Tab tab) { + throw new UnsupportedOperationException( + "Navigation modes are not supported in toolbar action bars"); + } + + @Override + public void removeTabAt(int position) { + throw new UnsupportedOperationException( + "Navigation modes are not supported in toolbar action bars"); + } + + @Override + public void removeAllTabs() { + throw new UnsupportedOperationException( + "Navigation modes are not supported in toolbar action bars"); + } + + @Override + public void selectTab(Tab tab) { + throw new UnsupportedOperationException( + "Navigation modes are not supported in toolbar action bars"); + } + + @Override + public Tab getSelectedTab() { + throw new UnsupportedOperationException( + "Navigation modes are not supported in toolbar action bars"); + } + + @Override + public Tab getTabAt(int index) { + throw new UnsupportedOperationException( + "Navigation modes are not supported in toolbar action bars"); + } + + @Override + public int getTabCount() { + return 0; + } + + @Override + public int getHeight() { + return mToolbar.getHeight(); + } + + @Override + public void show() { + // TODO: Consider a better transition for this. + // Right now use no automatic transition so that the app can supply one if desired. + mToolbar.setVisibility(View.VISIBLE); + } + + @Override + public void hide() { + // TODO: Consider a better transition for this. + // Right now use no automatic transition so that the app can supply one if desired. + mToolbar.setVisibility(View.GONE); + } + + @Override + public boolean isShowing() { + return mToolbar.getVisibility() == View.VISIBLE; + } + + public void addOnMenuVisibilityListener(OnMenuVisibilityListener listener) { + mMenuVisibilityListeners.add(listener); + } + + public void removeOnMenuVisibilityListener(OnMenuVisibilityListener listener) { + mMenuVisibilityListeners.remove(listener); + } + + public void dispatchMenuVisibilityChanged(boolean isVisible) { + if (isVisible == mLastMenuVisibility) { + return; + } + mLastMenuVisibility = isVisible; + + final int count = mMenuVisibilityListeners.size(); + for (int i = 0; i < count; i++) { + mMenuVisibilityListeners.get(i).onMenuVisibilityChanged(isVisible); + } + } + + @Override + public void captureSharedElements(Map<String, View> sharedElements) { + mToolbar.findSharedElements(sharedElements); + } +} diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/WindowDecorActionBar.java index 80e1caaf26d8..c6afae07ab5a 100644 --- a/core/java/com/android/internal/app/ActionBarImpl.java +++ b/core/java/com/android/internal/app/WindowDecorActionBar.java @@ -60,14 +60,14 @@ import java.util.ArrayList; import java.util.Map; /** - * ActionBarImpl is the ActionBar implementation used - * by devices of all screen sizes. If it detects a compatible decor, - * it will split contextual modes across both the ActionBarView at - * the top of the screen and a horizontal LinearLayout at the bottom - * which is normally hidden. + * WindowDecorActionBar is the ActionBar implementation used + * by devices of all screen sizes as part of the window decor layout. + * If it detects a compatible decor, it will split contextual modes + * across both the ActionBarView at the top of the screen and + * a horizontal LinearLayout at the bottom which is normally hidden. */ -public class ActionBarImpl extends ActionBar { - private static final String TAG = "ActionBarImpl"; +public class WindowDecorActionBar extends ActionBar { + private static final String TAG = "WindowDecorActionBar"; private Context mContext; private Context mThemedContext; @@ -105,9 +105,6 @@ public class ActionBarImpl extends ActionBar { private int mContextDisplayMode; private boolean mHasEmbeddedTabs; - final Handler mHandler = new Handler(); - Runnable mTabSelector; - private int mCurWindowVisibility = View.VISIBLE; private boolean mContentAnimations = true; @@ -157,7 +154,7 @@ public class ActionBarImpl extends ActionBar { } }; - public ActionBarImpl(Activity activity) { + public WindowDecorActionBar(Activity activity) { mActivity = activity; Window window = activity.getWindow(); View decor = window.getDecorView(); @@ -168,7 +165,7 @@ public class ActionBarImpl extends ActionBar { } } - public ActionBarImpl(Dialog dialog) { + public WindowDecorActionBar(Dialog dialog) { mDialog = dialog; init(dialog.getWindow().getDecorView()); } diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index 47347128604b..dfdb9aead2fd 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -41,6 +41,7 @@ interface IStatusBarService out List<IBinder> notificationKeys, out List<StatusBarNotification> notifications, out int[] switches, out List<IBinder> binders); void onPanelRevealed(); + void onPanelHidden(); void onNotificationClick(String pkg, String tag, int id, int userId); void onNotificationError(String pkg, String tag, int id, int uid, int initialPid, String message, int userId); diff --git a/core/java/com/android/internal/view/menu/ActionMenuItemView.java b/core/java/com/android/internal/view/menu/ActionMenuItemView.java index 3cceebeab443..891baea6ea8e 100644 --- a/core/java/com/android/internal/view/menu/ActionMenuItemView.java +++ b/core/java/com/android/internal/view/menu/ActionMenuItemView.java @@ -282,11 +282,6 @@ public class ActionMenuItemView extends TextView @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) { - // Fill all available height. - heightMeasureSpec = MeasureSpec.makeMeasureSpec( - MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.EXACTLY); - } final boolean textVisible = hasText(); if (textVisible && mSavedPaddingLeft >= 0) { super.setPadding(mSavedPaddingLeft, getPaddingTop(), diff --git a/core/java/com/android/internal/widget/ActionBarContainer.java b/core/java/com/android/internal/widget/ActionBarContainer.java index c2d22dd43609..ed075142b355 100644 --- a/core/java/com/android/internal/widget/ActionBarContainer.java +++ b/core/java/com/android/internal/widget/ActionBarContainer.java @@ -43,6 +43,7 @@ public class ActionBarContainer extends FrameLayout { private Drawable mSplitBackground; private boolean mIsSplit; private boolean mIsStacked; + private int mHeight; public ActionBarContainer(Context context) { this(context, null); @@ -59,6 +60,7 @@ public class ActionBarContainer extends FrameLayout { mBackground = a.getDrawable(com.android.internal.R.styleable.ActionBar_background); mStackedBackground = a.getDrawable( com.android.internal.R.styleable.ActionBar_backgroundStacked); + mHeight = a.getDimensionPixelSize(com.android.internal.R.styleable.ActionBar_height, -1); if (getId() == com.android.internal.R.id.split_action_bar) { mIsSplit = true; @@ -251,6 +253,11 @@ public class ActionBarContainer extends FrameLayout { @Override public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + if (mActionBarView == null && + MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST && mHeight >= 0) { + heightMeasureSpec = MeasureSpec.makeMeasureSpec( + Math.min(mHeight, MeasureSpec.getSize(heightMeasureSpec)), MeasureSpec.AT_MOST); + } super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (mActionBarView == null) return; diff --git a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java index adb9bf8ff015..c9dff1a2f1ab 100644 --- a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java +++ b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java @@ -21,7 +21,7 @@ import android.graphics.drawable.Drawable; import android.os.Build; import android.view.ViewGroup; import android.view.WindowInsets; -import com.android.internal.app.ActionBarImpl; +import com.android.internal.app.WindowDecorActionBar; import android.content.Context; import android.content.res.TypedArray; @@ -38,7 +38,7 @@ public class ActionBarOverlayLayout extends ViewGroup { private static final String TAG = "ActionBarOverlayLayout"; private int mActionBarHeight; - private ActionBarImpl mActionBar; + private WindowDecorActionBar mActionBar; private int mWindowVisibility = View.VISIBLE; // The main UI elements that we handle the layout of. @@ -88,7 +88,7 @@ public class ActionBarOverlayLayout extends ViewGroup { Build.VERSION_CODES.KITKAT; } - public void setActionBar(ActionBarImpl impl) { + public void setActionBar(WindowDecorActionBar impl) { mActionBar = impl; if (getWindowToken() != null) { // This is being initialized after being added to a window; diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java index 1273c4dd6e36..60631b994984 100644 --- a/core/java/com/android/internal/widget/ActionBarView.java +++ b/core/java/com/android/internal/widget/ActionBarView.java @@ -430,7 +430,7 @@ public class ActionBarView extends AbsActionBarView { mActionMenuPresenter.setItemLimit(Integer.MAX_VALUE); // Span the whole width layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = mContentHeight; + layoutParams.height = LayoutParams.WRAP_CONTENT; configPresenters(builder); menuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this); if (mSplitView != null) { diff --git a/core/java/com/android/internal/widget/AutoScrollHelper.java b/core/java/com/android/internal/widget/AutoScrollHelper.java index 7a294aa52401..0d468ca24941 100644 --- a/core/java/com/android/internal/widget/AutoScrollHelper.java +++ b/core/java/com/android/internal/widget/AutoScrollHelper.java @@ -892,6 +892,10 @@ public abstract class AutoScrollHelper implements View.OnTouchListener { public boolean canTargetScrollVertically(int direction) { final AbsListView target = mTarget; final int itemCount = target.getCount(); + if (itemCount == 0) { + return false; + } + final int childCount = target.getChildCount(); final int firstPosition = target.getFirstVisiblePosition(); final int lastPosition = firstPosition + childCount; diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index 310966cb8222..e0fa9ba432a5 100644 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -329,7 +329,7 @@ static jobject Bitmap_copy(JNIEnv* env, jobject, jlong srcHandle, SkBitmap result;
JavaPixelAllocator allocator(env);
- if (!src->copyTo(&result, dstConfig, &allocator)) {
+ if (!src->copyTo(&result, SkBitmapConfigToColorType(dstConfig), &allocator)) { return NULL;
}
return GraphicsJNI::createBitmap(env, new SkBitmap(result), allocator.getStorageObj(),
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index b89dceda547f..7aa241ab7764 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -151,7 +151,7 @@ static SkColorType colorTypeForScaledOutput(SkColorType colorType) { switch (colorType) { case kUnknown_SkColorType: case kIndex_8_SkColorType: - return kPMColor_SkColorType; + return kNative_8888_SkColorType; default: break; } diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp index 098643b1eda3..ead2de373ab5 100644 --- a/core/jni/android/graphics/BitmapRegionDecoder.cpp +++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp @@ -62,7 +62,7 @@ public: bool decodeRegion(SkBitmap* bitmap, const SkIRect& rect, SkBitmap::Config pref, int sampleSize) { fDecoder->setSampleSize(sampleSize); - return fDecoder->decodeRegion(bitmap, rect, pref); + return fDecoder->decodeSubset(bitmap, rect, pref); } SkImageDecoder* getDecoder() const { return fDecoder; } diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp index 946b8987db63..27ed0f6a5bc6 100644 --- a/core/jni/android/graphics/Canvas.cpp +++ b/core/jni/android/graphics/Canvas.cpp @@ -65,6 +65,9 @@ public: virtual void clipRect(const SkRect& rect, SkRegion::Op op, bool antialias) { m_dstCanvas->clipRect(rect, op, antialias); } + virtual void clipRRect(const SkRRect& rrect, SkRegion::Op op, bool antialias) { + m_dstCanvas->clipRRect(rrect, op, antialias); + } virtual void clipPath(const SkPath& path, SkRegion::Op op, bool antialias) { m_dstCanvas->clipPath(path, op, antialias); } @@ -73,6 +76,12 @@ private: SkCanvas* m_dstCanvas; }; +// Returns true if the SkCanvas's clip is non-empty. +static jboolean hasNonEmptyClip(const SkCanvas& canvas) { + bool emptyClip = canvas.isClipEmpty(); + return emptyClip ? JNI_FALSE : JNI_TRUE; +} + class SkCanvasGlue { public: @@ -281,8 +290,8 @@ public: r.set(SkFloatToScalar(left), SkFloatToScalar(top), SkFloatToScalar(right), SkFloatToScalar(bottom)); SkCanvas* c = GraphicsJNI::getNativeCanvas(env, jcanvas); - bool result = c->clipRect(r); - return result ? JNI_TRUE : JNI_FALSE; + c->clipRect(r); + return hasNonEmptyClip(*c); } static jboolean clipRect_IIII(JNIEnv* env, jobject jcanvas, jint left, @@ -291,8 +300,9 @@ public: SkRect r; r.set(SkIntToScalar(left), SkIntToScalar(top), SkIntToScalar(right), SkIntToScalar(bottom)); - bool result = GraphicsJNI::getNativeCanvas(env, jcanvas)->clipRect(r); - return result ? JNI_TRUE : JNI_FALSE; + SkCanvas* c = GraphicsJNI::getNativeCanvas(env, jcanvas); + c->clipRect(r); + return hasNonEmptyClip(*c); } static jboolean clipRect_RectF(JNIEnv* env, jobject jcanvas, jobject rectf) { @@ -300,8 +310,8 @@ public: NPE_CHECK_RETURN_ZERO(env, rectf); SkCanvas* c = GraphicsJNI::getNativeCanvas(env, jcanvas); SkRect tmp; - bool result = c->clipRect(*GraphicsJNI::jrectf_to_rect(env, rectf, &tmp)); - return result ? JNI_TRUE : JNI_FALSE; + c->clipRect(*GraphicsJNI::jrectf_to_rect(env, rectf, &tmp)); + return hasNonEmptyClip(*c); } static jboolean clipRect_Rect(JNIEnv* env, jobject jcanvas, jobject rect) { @@ -309,8 +319,8 @@ public: NPE_CHECK_RETURN_ZERO(env, rect); SkCanvas* c = GraphicsJNI::getNativeCanvas(env, jcanvas); SkRect tmp; - bool result = c->clipRect(*GraphicsJNI::jrect_to_rect(env, rect, &tmp)); - return result ? JNI_TRUE : JNI_FALSE; + c->clipRect(*GraphicsJNI::jrect_to_rect(env, rect, &tmp)); + return hasNonEmptyClip(*c); } @@ -321,25 +331,24 @@ public: SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); rect.set(SkFloatToScalar(left), SkFloatToScalar(top), SkFloatToScalar(right), SkFloatToScalar(bottom)); - bool result = canvas->clipRect(rect, static_cast<SkRegion::Op>(op)); - return result ? JNI_TRUE : JNI_FALSE; - + canvas->clipRect(rect, static_cast<SkRegion::Op>(op)); + return hasNonEmptyClip(*canvas); } static jboolean clipPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle, jint op) { SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); - bool result = canvas->clipPath(*reinterpret_cast<SkPath*>(pathHandle), - static_cast<SkRegion::Op>(op)); - return result ? JNI_TRUE : JNI_FALSE; + canvas->clipPath(*reinterpret_cast<SkPath*>(pathHandle), + static_cast<SkRegion::Op>(op)); + return hasNonEmptyClip(*canvas); } static jboolean clipRegion(JNIEnv* env, jobject, jlong canvasHandle, jlong deviceRgnHandle, jint op) { SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); SkRegion* deviceRgn = reinterpret_cast<SkRegion*>(deviceRgnHandle); - bool result = canvas->clipRegion(*deviceRgn, static_cast<SkRegion::Op>(op)); - return result ? JNI_TRUE : JNI_FALSE; + canvas->clipRegion(*deviceRgn, static_cast<SkRegion::Op>(op)); + return hasNonEmptyClip(*canvas); } static void setDrawFilter(JNIEnv* env, jobject, jlong canvasHandle, @@ -353,7 +362,8 @@ public: SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); SkRect rect_; GraphicsJNI::jrectf_to_rect(env, rect, &rect_); - return canvas->quickReject(rect_); + bool result = canvas->quickReject(rect_); + return result ? JNI_TRUE : JNI_FALSE; } static jboolean quickReject__Path(JNIEnv* env, jobject, jlong canvasHandle, diff --git a/core/jni/android/graphics/ColorFilter.cpp b/core/jni/android/graphics/ColorFilter.cpp index a59ed1912441..c66c84416111 100644 --- a/core/jni/android/graphics/ColorFilter.cpp +++ b/core/jni/android/graphics/ColorFilter.cpp @@ -51,7 +51,7 @@ public: const float* src = autoArray.ptr(); #ifdef SK_SCALAR_IS_FLOAT - return reinterpret_cast<jlong>(new SkColorMatrixFilter(src)); + return reinterpret_cast<jlong>(SkColorMatrixFilter::Create(src)); #else SkASSERT(false); #endif diff --git a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp index 2cb101515586..b64ab0d9d3ea 100644 --- a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp +++ b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp @@ -175,10 +175,14 @@ static jmethodID gOutputStream_flushMethodID; class SkJavaOutputStream : public SkWStream { public: SkJavaOutputStream(JNIEnv* env, jobject stream, jbyteArray storage) - : fEnv(env), fJavaOutputStream(stream), fJavaByteArray(storage) { + : fEnv(env), fJavaOutputStream(stream), fJavaByteArray(storage), fBytesWritten(0) { fCapacity = env->GetArrayLength(storage); } + virtual size_t bytesWritten() const { + return fBytesWritten; + } + virtual bool write(const void* buffer, size_t size) { JNIEnv* env = fEnv; jbyteArray storage = fJavaByteArray; @@ -213,6 +217,7 @@ public: buffer = (void*)((char*)buffer + requested); size -= requested; + fBytesWritten += requested; } return true; } @@ -226,6 +231,7 @@ private: jobject fJavaOutputStream; // the caller owns this object jbyteArray fJavaByteArray; // the caller owns this object jint fCapacity; + size_t fBytesWritten; }; SkWStream* CreateJavaOutputStreamAdaptor(JNIEnv* env, jobject stream, diff --git a/core/jni/android/graphics/MaskFilter.cpp b/core/jni/android/graphics/MaskFilter.cpp index 557336688989..7dc04464a1ba 100644 --- a/core/jni/android/graphics/MaskFilter.cpp +++ b/core/jni/android/graphics/MaskFilter.cpp @@ -45,7 +45,7 @@ public: static jlong createTable(JNIEnv* env, jobject, jbyteArray jtable) { AutoJavaByteArray autoTable(env, jtable, 256); - SkMaskFilter* filter = new SkTableMaskFilter((const uint8_t*)autoTable.ptr()); + SkMaskFilter* filter = SkTableMaskFilter::Create((const uint8_t*)autoTable.ptr()); return reinterpret_cast<jlong>(filter); } diff --git a/core/jni/android/graphics/Matrix.cpp b/core/jni/android/graphics/Matrix.cpp index c400c57a7e3e..d573aa8cda3e 100644 --- a/core/jni/android/graphics/Matrix.cpp +++ b/core/jni/android/graphics/Matrix.cpp @@ -123,129 +123,129 @@ public: SkScalar ky_ = SkFloatToScalar(ky); obj->setSkew(kx_, ky_); } - static jboolean setConcat(JNIEnv* env, jobject clazz, jlong objHandle, jlong aHandle, jlong bHandle) { + static void setConcat(JNIEnv* env, jobject clazz, jlong objHandle, jlong aHandle, jlong bHandle) { SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); SkMatrix* a = reinterpret_cast<SkMatrix*>(aHandle); SkMatrix* b = reinterpret_cast<SkMatrix*>(bHandle); - return obj->setConcat(*a, *b) ? JNI_TRUE : JNI_FALSE; + obj->setConcat(*a, *b); } - static jboolean preTranslate(JNIEnv* env, jobject clazz, jlong objHandle, jfloat dx, jfloat dy) { + static void preTranslate(JNIEnv* env, jobject clazz, jlong objHandle, jfloat dx, jfloat dy) { SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); SkScalar dx_ = SkFloatToScalar(dx); SkScalar dy_ = SkFloatToScalar(dy); - return obj->preTranslate(dx_, dy_) ? JNI_TRUE : JNI_FALSE; + obj->preTranslate(dx_, dy_); } - static jboolean preScale__FFFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat sx, jfloat sy, jfloat px, jfloat py) { + static void preScale__FFFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat sx, jfloat sy, jfloat px, jfloat py) { SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); SkScalar sx_ = SkFloatToScalar(sx); SkScalar sy_ = SkFloatToScalar(sy); SkScalar px_ = SkFloatToScalar(px); SkScalar py_ = SkFloatToScalar(py); - return obj->preScale(sx_, sy_, px_, py_) ? JNI_TRUE : JNI_FALSE; + obj->preScale(sx_, sy_, px_, py_); } - static jboolean preScale__FF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat sx, jfloat sy) { + static void preScale__FF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat sx, jfloat sy) { SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); SkScalar sx_ = SkFloatToScalar(sx); SkScalar sy_ = SkFloatToScalar(sy); - return obj->preScale(sx_, sy_) ? JNI_TRUE : JNI_FALSE; + obj->preScale(sx_, sy_); } - static jboolean preRotate__FFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat degrees, jfloat px, jfloat py) { + static void preRotate__FFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat degrees, jfloat px, jfloat py) { SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); SkScalar degrees_ = SkFloatToScalar(degrees); SkScalar px_ = SkFloatToScalar(px); SkScalar py_ = SkFloatToScalar(py); - return obj->preRotate(degrees_, px_, py_) ? JNI_TRUE : JNI_FALSE; + obj->preRotate(degrees_, px_, py_); } - static jboolean preRotate__F(JNIEnv* env, jobject clazz, jlong objHandle, jfloat degrees) { + static void preRotate__F(JNIEnv* env, jobject clazz, jlong objHandle, jfloat degrees) { SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); SkScalar degrees_ = SkFloatToScalar(degrees); - return obj->preRotate(degrees_) ? JNI_TRUE : JNI_FALSE; + obj->preRotate(degrees_); } - static jboolean preSkew__FFFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat kx, jfloat ky, jfloat px, jfloat py) { + static void preSkew__FFFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat kx, jfloat ky, jfloat px, jfloat py) { SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); SkScalar kx_ = SkFloatToScalar(kx); SkScalar ky_ = SkFloatToScalar(ky); SkScalar px_ = SkFloatToScalar(px); SkScalar py_ = SkFloatToScalar(py); - return obj->preSkew(kx_, ky_, px_, py_) ? JNI_TRUE : JNI_FALSE; + obj->preSkew(kx_, ky_, px_, py_); } - static jboolean preSkew__FF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat kx, jfloat ky) { + static void preSkew__FF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat kx, jfloat ky) { SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); SkScalar kx_ = SkFloatToScalar(kx); SkScalar ky_ = SkFloatToScalar(ky); - return obj->preSkew(kx_, ky_) ? JNI_TRUE : JNI_FALSE; + obj->preSkew(kx_, ky_); } - static jboolean preConcat(JNIEnv* env, jobject clazz, jlong objHandle, jlong otherHandle) { + static void preConcat(JNIEnv* env, jobject clazz, jlong objHandle, jlong otherHandle) { SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); SkMatrix* other = reinterpret_cast<SkMatrix*>(otherHandle); - return obj->preConcat(*other) ? JNI_TRUE : JNI_FALSE; + obj->preConcat(*other); } - static jboolean postTranslate(JNIEnv* env, jobject clazz, jlong objHandle, jfloat dx, jfloat dy) { + static void postTranslate(JNIEnv* env, jobject clazz, jlong objHandle, jfloat dx, jfloat dy) { SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); SkScalar dx_ = SkFloatToScalar(dx); SkScalar dy_ = SkFloatToScalar(dy); - return obj->postTranslate(dx_, dy_) ? JNI_TRUE : JNI_FALSE; + obj->postTranslate(dx_, dy_); } - static jboolean postScale__FFFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat sx, jfloat sy, jfloat px, jfloat py) { + static void postScale__FFFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat sx, jfloat sy, jfloat px, jfloat py) { SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); SkScalar sx_ = SkFloatToScalar(sx); SkScalar sy_ = SkFloatToScalar(sy); SkScalar px_ = SkFloatToScalar(px); SkScalar py_ = SkFloatToScalar(py); - return obj->postScale(sx_, sy_, px_, py_) ? JNI_TRUE : JNI_FALSE; + obj->postScale(sx_, sy_, px_, py_); } - static jboolean postScale__FF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat sx, jfloat sy) { + static void postScale__FF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat sx, jfloat sy) { SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); SkScalar sx_ = SkFloatToScalar(sx); SkScalar sy_ = SkFloatToScalar(sy); - return obj->postScale(sx_, sy_) ? JNI_TRUE : JNI_FALSE; + obj->postScale(sx_, sy_); } - static jboolean postRotate__FFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat degrees, jfloat px, jfloat py) { + static void postRotate__FFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat degrees, jfloat px, jfloat py) { SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); SkScalar degrees_ = SkFloatToScalar(degrees); SkScalar px_ = SkFloatToScalar(px); SkScalar py_ = SkFloatToScalar(py); - return obj->postRotate(degrees_, px_, py_) ? JNI_TRUE : JNI_FALSE; + obj->postRotate(degrees_, px_, py_); } - static jboolean postRotate__F(JNIEnv* env, jobject clazz, jlong objHandle, jfloat degrees) { + static void postRotate__F(JNIEnv* env, jobject clazz, jlong objHandle, jfloat degrees) { SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); SkScalar degrees_ = SkFloatToScalar(degrees); - return obj->postRotate(degrees_) ? JNI_TRUE : JNI_FALSE; + obj->postRotate(degrees_); } - static jboolean postSkew__FFFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat kx, jfloat ky, jfloat px, jfloat py) { + static void postSkew__FFFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat kx, jfloat ky, jfloat px, jfloat py) { SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); SkScalar kx_ = SkFloatToScalar(kx); SkScalar ky_ = SkFloatToScalar(ky); SkScalar px_ = SkFloatToScalar(px); SkScalar py_ = SkFloatToScalar(py); - return obj->postSkew(kx_, ky_, px_, py_) ? JNI_TRUE : JNI_FALSE; + obj->postSkew(kx_, ky_, px_, py_); } - static jboolean postSkew__FF(JNIEnv* env, jobject clazz, jlong matrixHandle, jfloat kx, jfloat ky) { + static void postSkew__FF(JNIEnv* env, jobject clazz, jlong matrixHandle, jfloat kx, jfloat ky) { SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle); SkScalar kx_ = SkFloatToScalar(kx); SkScalar ky_ = SkFloatToScalar(ky); - return matrix->postSkew(kx_, ky_) ? JNI_TRUE : JNI_FALSE; + matrix->postSkew(kx_, ky_); } - static jboolean postConcat(JNIEnv* env, jobject clazz, jlong matrixHandle, jlong otherHandle) { + static void postConcat(JNIEnv* env, jobject clazz, jlong matrixHandle, jlong otherHandle) { SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle); SkMatrix* other = reinterpret_cast<SkMatrix*>(otherHandle); - return matrix->postConcat(*other) ? JNI_TRUE : JNI_FALSE; + matrix->postConcat(*other); } static jboolean setRectToRect(JNIEnv* env, jobject clazz, jlong matrixHandle, jobject src, jobject dst, jint stfHandle) { @@ -374,23 +374,23 @@ static JNINativeMethod methods[] = { {"native_setSinCos","(JFF)V", (void*) SkMatrixGlue::setSinCos__FF}, {"native_setSkew","(JFFFF)V", (void*) SkMatrixGlue::setSkew__FFFF}, {"native_setSkew","(JFF)V", (void*) SkMatrixGlue::setSkew__FF}, - {"native_setConcat","(JJJ)Z", (void*) SkMatrixGlue::setConcat}, - {"native_preTranslate","(JFF)Z", (void*) SkMatrixGlue::preTranslate}, - {"native_preScale","(JFFFF)Z", (void*) SkMatrixGlue::preScale__FFFF}, - {"native_preScale","(JFF)Z", (void*) SkMatrixGlue::preScale__FF}, - {"native_preRotate","(JFFF)Z", (void*) SkMatrixGlue::preRotate__FFF}, - {"native_preRotate","(JF)Z", (void*) SkMatrixGlue::preRotate__F}, - {"native_preSkew","(JFFFF)Z", (void*) SkMatrixGlue::preSkew__FFFF}, - {"native_preSkew","(JFF)Z", (void*) SkMatrixGlue::preSkew__FF}, - {"native_preConcat","(JJ)Z", (void*) SkMatrixGlue::preConcat}, - {"native_postTranslate","(JFF)Z", (void*) SkMatrixGlue::postTranslate}, - {"native_postScale","(JFFFF)Z", (void*) SkMatrixGlue::postScale__FFFF}, - {"native_postScale","(JFF)Z", (void*) SkMatrixGlue::postScale__FF}, - {"native_postRotate","(JFFF)Z", (void*) SkMatrixGlue::postRotate__FFF}, - {"native_postRotate","(JF)Z", (void*) SkMatrixGlue::postRotate__F}, - {"native_postSkew","(JFFFF)Z", (void*) SkMatrixGlue::postSkew__FFFF}, - {"native_postSkew","(JFF)Z", (void*) SkMatrixGlue::postSkew__FF}, - {"native_postConcat","(JJ)Z", (void*) SkMatrixGlue::postConcat}, + {"native_setConcat","(JJJ)V", (void*) SkMatrixGlue::setConcat}, + {"native_preTranslate","(JFF)V", (void*) SkMatrixGlue::preTranslate}, + {"native_preScale","(JFFFF)V", (void*) SkMatrixGlue::preScale__FFFF}, + {"native_preScale","(JFF)V", (void*) SkMatrixGlue::preScale__FF}, + {"native_preRotate","(JFFF)V", (void*) SkMatrixGlue::preRotate__FFF}, + {"native_preRotate","(JF)V", (void*) SkMatrixGlue::preRotate__F}, + {"native_preSkew","(JFFFF)V", (void*) SkMatrixGlue::preSkew__FFFF}, + {"native_preSkew","(JFF)V", (void*) SkMatrixGlue::preSkew__FF}, + {"native_preConcat","(JJ)V", (void*) SkMatrixGlue::preConcat}, + {"native_postTranslate","(JFF)V", (void*) SkMatrixGlue::postTranslate}, + {"native_postScale","(JFFFF)V", (void*) SkMatrixGlue::postScale__FFFF}, + {"native_postScale","(JFF)V", (void*) SkMatrixGlue::postScale__FF}, + {"native_postRotate","(JFFF)V", (void*) SkMatrixGlue::postRotate__FFF}, + {"native_postRotate","(JF)V", (void*) SkMatrixGlue::postRotate__F}, + {"native_postSkew","(JFFFF)V", (void*) SkMatrixGlue::postSkew__FFFF}, + {"native_postSkew","(JFF)V", (void*) SkMatrixGlue::postSkew__FF}, + {"native_postConcat","(JJ)V", (void*) SkMatrixGlue::postConcat}, {"native_setRectToRect","(JLandroid/graphics/RectF;Landroid/graphics/RectF;I)Z", (void*) SkMatrixGlue::setRectToRect}, {"native_setPolyToPoly","(J[FI[FII)Z", (void*) SkMatrixGlue::setPolyToPoly}, {"native_invert","(JJ)Z", (void*) SkMatrixGlue::invert}, diff --git a/core/jni/android/graphics/PathEffect.cpp b/core/jni/android/graphics/PathEffect.cpp index 280375898a10..81b46ce2beb2 100644 --- a/core/jni/android/graphics/PathEffect.cpp +++ b/core/jni/android/graphics/PathEffect.cpp @@ -20,7 +20,7 @@ public: jlong outerHandle, jlong innerHandle) { SkPathEffect* outer = reinterpret_cast<SkPathEffect*>(outerHandle); SkPathEffect* inner = reinterpret_cast<SkPathEffect*>(innerHandle); - SkPathEffect* effect = new SkComposePathEffect(outer, inner); + SkPathEffect* effect = SkComposePathEffect::Create(outer, inner); return reinterpret_cast<jlong>(effect); } @@ -28,7 +28,7 @@ public: jlong firstHandle, jlong secondHandle) { SkPathEffect* first = reinterpret_cast<SkPathEffect*>(firstHandle); SkPathEffect* second = reinterpret_cast<SkPathEffect*>(secondHandle); - SkPathEffect* effect = new SkSumPathEffect(first, second); + SkPathEffect* effect = SkSumPathEffect::Create(first, second); return reinterpret_cast<jlong>(effect); } @@ -43,7 +43,7 @@ public: for (int i = 0; i < count; i++) { intervals[i] = SkFloatToScalar(values[i]); } - SkPathEffect* effect = new SkDashPathEffect(intervals, count, SkFloatToScalar(phase)); + SkPathEffect* effect = SkDashPathEffect::Create(intervals, count, SkFloatToScalar(phase)); return reinterpret_cast<jlong>(effect); } @@ -51,19 +51,19 @@ public: jlong shapeHandle, jfloat advance, jfloat phase, jint style) { const SkPath* shape = reinterpret_cast<SkPath*>(shapeHandle); SkASSERT(shape != NULL); - SkPathEffect* effect = new SkPath1DPathEffect(*shape, SkFloatToScalar(advance), + SkPathEffect* effect = SkPath1DPathEffect::Create(*shape, SkFloatToScalar(advance), SkFloatToScalar(phase), (SkPath1DPathEffect::Style)style); return reinterpret_cast<jlong>(effect); } static jlong Corner_constructor(JNIEnv* env, jobject, jfloat radius){ - SkPathEffect* effect = new SkCornerPathEffect(SkFloatToScalar(radius)); + SkPathEffect* effect = SkCornerPathEffect::Create(SkFloatToScalar(radius)); return reinterpret_cast<jlong>(effect); } static jlong Discrete_constructor(JNIEnv* env, jobject, jfloat length, jfloat deviation) { - SkPathEffect* effect = new SkDiscretePathEffect(SkFloatToScalar(length), + SkPathEffect* effect = SkDiscretePathEffect::Create(SkFloatToScalar(length), SkFloatToScalar(deviation)); return reinterpret_cast<jlong>(effect); } diff --git a/core/jni/android/graphics/Xfermode.cpp b/core/jni/android/graphics/Xfermode.cpp index eedceb7e8360..6bf6f8a6abe9 100644 --- a/core/jni/android/graphics/Xfermode.cpp +++ b/core/jni/android/graphics/Xfermode.cpp @@ -36,12 +36,12 @@ public: jint tolerance, jint modeHandle) { SkAvoidXfermode::Mode mode = static_cast<SkAvoidXfermode::Mode>(modeHandle); - return reinterpret_cast<jlong>(new SkAvoidXfermode(opColor, tolerance, mode)); + return reinterpret_cast<jlong>(SkAvoidXfermode::Create(opColor, tolerance, mode)); } static jlong pixelxor_create(JNIEnv* env, jobject, jint opColor) { - return reinterpret_cast<jlong>(new SkPixelXorXfermode(opColor)); + return reinterpret_cast<jlong>(SkPixelXorXfermode::Create(opColor)); } }; diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp index d079349cb256..cf9565770d13 100644 --- a/core/jni/android_view_RenderNode.cpp +++ b/core/jni/android_view_RenderNode.cpp @@ -59,13 +59,14 @@ static void android_view_RenderNode_output(JNIEnv* env, static jlong android_view_RenderNode_create(JNIEnv* env, jobject clazz) { RenderNode* displayList = new RenderNode(); + displayList->incStrong(0); return reinterpret_cast<jlong>(displayList); } static void android_view_RenderNode_destroyDisplayList(JNIEnv* env, jobject clazz, jlong displayListPtr) { RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr); - RenderNode::destroyDisplayListDeferred(displayList); + displayList->decStrong(0); } // ---------------------------------------------------------------------------- diff --git a/core/res/res/drawable-hdpi/ab_bottom_solid_qntm_alpha.9.png b/core/res/res/drawable-hdpi/ab_bottom_solid_qntm_alpha.9.png Binary files differdeleted file mode 100644 index 406bf58c96d0..000000000000 --- a/core/res/res/drawable-hdpi/ab_bottom_solid_qntm_alpha.9.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/ab_bottom_transparent_qntm_alpha.9.png b/core/res/res/drawable-hdpi/ab_bottom_transparent_qntm_alpha.9.png Binary files differdeleted file mode 100644 index 409d3cdc79da..000000000000 --- a/core/res/res/drawable-hdpi/ab_bottom_transparent_qntm_alpha.9.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/ab_solid_qntm_alpha.9.png b/core/res/res/drawable-hdpi/ab_solid_qntm_alpha.9.png Binary files differdeleted file mode 100644 index 9d7b25fa4b76..000000000000 --- a/core/res/res/drawable-hdpi/ab_solid_qntm_alpha.9.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/ab_stacked_solid_qntm_alpha.9.png b/core/res/res/drawable-hdpi/ab_stacked_solid_qntm_alpha.9.png Binary files differdeleted file mode 100644 index 1222711a6c3a..000000000000 --- a/core/res/res/drawable-hdpi/ab_stacked_solid_qntm_alpha.9.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/ab_stacked_transparent_qntm_alpha.9.png b/core/res/res/drawable-hdpi/ab_stacked_transparent_qntm_alpha.9.png Binary files differdeleted file mode 100644 index d0fcc25c9239..000000000000 --- a/core/res/res/drawable-hdpi/ab_stacked_transparent_qntm_alpha.9.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/ab_transparent_qntm_alpha.9.png b/core/res/res/drawable-hdpi/ab_transparent_qntm_alpha.9.png Binary files differdeleted file mode 100644 index 5fa4ec416859..000000000000 --- a/core/res/res/drawable-hdpi/ab_transparent_qntm_alpha.9.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/ic_audio_alarm.png b/core/res/res/drawable-hdpi/ic_audio_alarm_alpha.png Binary files differindex 1b41de4f70cb..1b41de4f70cb 100644 --- a/core/res/res/drawable-hdpi/ic_audio_alarm.png +++ b/core/res/res/drawable-hdpi/ic_audio_alarm_alpha.png diff --git a/core/res/res/drawable-hdpi/ic_audio_alarm_mute.png b/core/res/res/drawable-hdpi/ic_audio_alarm_mute_alpha.png Binary files differindex 45ed7b603a41..45ed7b603a41 100644 --- a/core/res/res/drawable-hdpi/ic_audio_alarm_mute.png +++ b/core/res/res/drawable-hdpi/ic_audio_alarm_mute_alpha.png diff --git a/core/res/res/drawable-hdpi/ic_audio_bt.png b/core/res/res/drawable-hdpi/ic_audio_bt_alpha.png Binary files differindex 597c384cb7af..597c384cb7af 100644 --- a/core/res/res/drawable-hdpi/ic_audio_bt.png +++ b/core/res/res/drawable-hdpi/ic_audio_bt_alpha.png diff --git a/core/res/res/drawable-hdpi/ic_audio_bt_mute.png b/core/res/res/drawable-hdpi/ic_audio_bt_mute_alpha.png Binary files differindex 298db927e52c..298db927e52c 100644 --- a/core/res/res/drawable-hdpi/ic_audio_bt_mute.png +++ b/core/res/res/drawable-hdpi/ic_audio_bt_mute_alpha.png diff --git a/core/res/res/drawable-hdpi/ic_audio_notification_am.png b/core/res/res/drawable-hdpi/ic_audio_notification_am_alpha.png Binary files differindex 00e8f8aec728..00e8f8aec728 100644 --- a/core/res/res/drawable-hdpi/ic_audio_notification_am.png +++ b/core/res/res/drawable-hdpi/ic_audio_notification_am_alpha.png diff --git a/core/res/res/drawable-hdpi/ic_audio_notification_mute_am.png b/core/res/res/drawable-hdpi/ic_audio_notification_mute_am_alpha.png Binary files differindex 697cc9254935..697cc9254935 100644 --- a/core/res/res/drawable-hdpi/ic_audio_notification_mute_am.png +++ b/core/res/res/drawable-hdpi/ic_audio_notification_mute_am_alpha.png diff --git a/core/res/res/drawable-hdpi/ic_audio_phone_am.png b/core/res/res/drawable-hdpi/ic_audio_phone_am_alpha.png Binary files differindex 8a7d67ab5082..8a7d67ab5082 100644 --- a/core/res/res/drawable-hdpi/ic_audio_phone_am.png +++ b/core/res/res/drawable-hdpi/ic_audio_phone_am_alpha.png diff --git a/core/res/res/drawable-hdpi/ic_audio_ring_notif_am.png b/core/res/res/drawable-hdpi/ic_audio_ring_notif_am_alpha.png Binary files differindex a89f45f5b970..a89f45f5b970 100644 --- a/core/res/res/drawable-hdpi/ic_audio_ring_notif_am.png +++ b/core/res/res/drawable-hdpi/ic_audio_ring_notif_am_alpha.png diff --git a/core/res/res/drawable-hdpi/ic_audio_ring_notif_mute_am.png b/core/res/res/drawable-hdpi/ic_audio_ring_notif_mute_am_alpha.png Binary files differindex d03badea7849..d03badea7849 100644 --- a/core/res/res/drawable-hdpi/ic_audio_ring_notif_mute_am.png +++ b/core/res/res/drawable-hdpi/ic_audio_ring_notif_mute_am_alpha.png diff --git a/core/res/res/drawable-hdpi/ic_audio_ring_notif_vibrate_am.png b/core/res/res/drawable-hdpi/ic_audio_ring_notif_vibrate_am_alpha.png Binary files differindex 41991067dbcc..41991067dbcc 100644 --- a/core/res/res/drawable-hdpi/ic_audio_ring_notif_vibrate_am.png +++ b/core/res/res/drawable-hdpi/ic_audio_ring_notif_vibrate_am_alpha.png diff --git a/core/res/res/drawable-hdpi/ic_audio_vol_am.png b/core/res/res/drawable-hdpi/ic_audio_vol_am_alpha.png Binary files differindex 6ea269330dd5..6ea269330dd5 100644 --- a/core/res/res/drawable-hdpi/ic_audio_vol_am.png +++ b/core/res/res/drawable-hdpi/ic_audio_vol_am_alpha.png diff --git a/core/res/res/drawable-hdpi/ic_audio_vol_mute_am.png b/core/res/res/drawable-hdpi/ic_audio_vol_mute_am_alpha.png Binary files differindex 4256385a8718..4256385a8718 100644 --- a/core/res/res/drawable-hdpi/ic_audio_vol_mute_am.png +++ b/core/res/res/drawable-hdpi/ic_audio_vol_mute_am_alpha.png diff --git a/core/res/res/drawable-hdpi/ic_lock_airplane_mode.png b/core/res/res/drawable-hdpi/ic_lock_airplane_mode_alpha.png Binary files differindex 90c80fdb65d9..90c80fdb65d9 100644 --- a/core/res/res/drawable-hdpi/ic_lock_airplane_mode.png +++ b/core/res/res/drawable-hdpi/ic_lock_airplane_mode_alpha.png diff --git a/core/res/res/drawable-hdpi/ic_lock_airplane_mode_off_am.png b/core/res/res/drawable-hdpi/ic_lock_airplane_mode_off_am_alpha.png Binary files differindex b05589459553..b05589459553 100644 --- a/core/res/res/drawable-hdpi/ic_lock_airplane_mode_off_am.png +++ b/core/res/res/drawable-hdpi/ic_lock_airplane_mode_off_am_alpha.png diff --git a/core/res/res/drawable-hdpi/ic_lock_bugreport_alpha.png b/core/res/res/drawable-hdpi/ic_lock_bugreport_alpha.png Binary files differnew file mode 100644 index 000000000000..ba5bd01fc7f3 --- /dev/null +++ b/core/res/res/drawable-hdpi/ic_lock_bugreport_alpha.png diff --git a/core/res/res/drawable-hdpi/ic_lock_idle_alarm.png b/core/res/res/drawable-hdpi/ic_lock_idle_alarm_alpha.png Binary files differindex 3cadaff0f798..3cadaff0f798 100644 --- a/core/res/res/drawable-hdpi/ic_lock_idle_alarm.png +++ b/core/res/res/drawable-hdpi/ic_lock_idle_alarm_alpha.png diff --git a/core/res/res/drawable-hdpi/ic_lock_lock.png b/core/res/res/drawable-hdpi/ic_lock_lock_alpha.png Binary files differindex 6d1029ce9384..6d1029ce9384 100644 --- a/core/res/res/drawable-hdpi/ic_lock_lock.png +++ b/core/res/res/drawable-hdpi/ic_lock_lock_alpha.png diff --git a/core/res/res/drawable-hdpi/ic_lock_power_off.png b/core/res/res/drawable-hdpi/ic_lock_power_off_alpha.png Binary files differindex bc2dc706a385..bc2dc706a385 100644 --- a/core/res/res/drawable-hdpi/ic_lock_power_off.png +++ b/core/res/res/drawable-hdpi/ic_lock_power_off_alpha.png diff --git a/core/res/res/drawable-hdpi/ic_lock_ringer_off.png b/core/res/res/drawable-hdpi/ic_lock_ringer_off_alpha.png Binary files differindex e7cb234bf04e..e7cb234bf04e 100644 --- a/core/res/res/drawable-hdpi/ic_lock_ringer_off.png +++ b/core/res/res/drawable-hdpi/ic_lock_ringer_off_alpha.png diff --git a/core/res/res/drawable-hdpi/ic_lock_ringer_on.png b/core/res/res/drawable-hdpi/ic_lock_ringer_on_alpha.png Binary files differindex ce0cfab936b2..ce0cfab936b2 100644 --- a/core/res/res/drawable-hdpi/ic_lock_ringer_on.png +++ b/core/res/res/drawable-hdpi/ic_lock_ringer_on_alpha.png diff --git a/core/res/res/drawable-ldpi/ic_lock_airplane_mode.png b/core/res/res/drawable-ldpi/ic_lock_airplane_mode_alpha.png Binary files differindex 65a101bec089..65a101bec089 100644 --- a/core/res/res/drawable-ldpi/ic_lock_airplane_mode.png +++ b/core/res/res/drawable-ldpi/ic_lock_airplane_mode_alpha.png diff --git a/core/res/res/drawable-ldpi/ic_lock_airplane_mode_off_am.png b/core/res/res/drawable-ldpi/ic_lock_airplane_mode_off_am_alpha.png Binary files differindex 11adeb83a7d0..11adeb83a7d0 100644 --- a/core/res/res/drawable-ldpi/ic_lock_airplane_mode_off_am.png +++ b/core/res/res/drawable-ldpi/ic_lock_airplane_mode_off_am_alpha.png diff --git a/core/res/res/drawable-ldpi/ic_lock_idle_alarm.png b/core/res/res/drawable-ldpi/ic_lock_idle_alarm_alpha.png Binary files differindex dc133c562f2e..dc133c562f2e 100644 --- a/core/res/res/drawable-ldpi/ic_lock_idle_alarm.png +++ b/core/res/res/drawable-ldpi/ic_lock_idle_alarm_alpha.png diff --git a/core/res/res/drawable-ldpi/ic_lock_lock.png b/core/res/res/drawable-ldpi/ic_lock_lock_alpha.png Binary files differindex bde40f6dc5cc..bde40f6dc5cc 100644 --- a/core/res/res/drawable-ldpi/ic_lock_lock.png +++ b/core/res/res/drawable-ldpi/ic_lock_lock_alpha.png diff --git a/core/res/res/drawable-ldpi/ic_lock_power_off.png b/core/res/res/drawable-ldpi/ic_lock_power_off_alpha.png Binary files differindex 074d6d09c255..074d6d09c255 100644 --- a/core/res/res/drawable-ldpi/ic_lock_power_off.png +++ b/core/res/res/drawable-ldpi/ic_lock_power_off_alpha.png diff --git a/core/res/res/drawable-ldpi/ic_lock_ringer_off.png b/core/res/res/drawable-ldpi/ic_lock_ringer_off_alpha.png Binary files differindex 50ff3de7041c..50ff3de7041c 100644 --- a/core/res/res/drawable-ldpi/ic_lock_ringer_off.png +++ b/core/res/res/drawable-ldpi/ic_lock_ringer_off_alpha.png diff --git a/core/res/res/drawable-ldpi/ic_lock_ringer_on.png b/core/res/res/drawable-ldpi/ic_lock_ringer_on_alpha.png Binary files differindex 723272836c6f..723272836c6f 100644 --- a/core/res/res/drawable-ldpi/ic_lock_ringer_on.png +++ b/core/res/res/drawable-ldpi/ic_lock_ringer_on_alpha.png diff --git a/core/res/res/drawable-mdpi/ab_bottom_solid_qntm_alpha.9.png b/core/res/res/drawable-mdpi/ab_bottom_solid_qntm_alpha.9.png Binary files differdeleted file mode 100644 index 219d3114b6be..000000000000 --- a/core/res/res/drawable-mdpi/ab_bottom_solid_qntm_alpha.9.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/ab_bottom_transparent_qntm_alpha.9.png b/core/res/res/drawable-mdpi/ab_bottom_transparent_qntm_alpha.9.png Binary files differdeleted file mode 100644 index bfdc933f18c3..000000000000 --- a/core/res/res/drawable-mdpi/ab_bottom_transparent_qntm_alpha.9.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/ab_solid_qntm_alpha.9.png b/core/res/res/drawable-mdpi/ab_solid_qntm_alpha.9.png Binary files differdeleted file mode 100644 index bd818b5f73f7..000000000000 --- a/core/res/res/drawable-mdpi/ab_solid_qntm_alpha.9.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/ab_stacked_solid_qntm_alpha.9.png b/core/res/res/drawable-mdpi/ab_stacked_solid_qntm_alpha.9.png Binary files differdeleted file mode 100644 index 41c7ce616319..000000000000 --- a/core/res/res/drawable-mdpi/ab_stacked_solid_qntm_alpha.9.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/ab_stacked_transparent_qntm_alpha.9.png b/core/res/res/drawable-mdpi/ab_stacked_transparent_qntm_alpha.9.png Binary files differdeleted file mode 100644 index 961a73e5619b..000000000000 --- a/core/res/res/drawable-mdpi/ab_stacked_transparent_qntm_alpha.9.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/ab_transparent_qntm_alpha.9.png b/core/res/res/drawable-mdpi/ab_transparent_qntm_alpha.9.png Binary files differdeleted file mode 100644 index 2e423865ee76..000000000000 --- a/core/res/res/drawable-mdpi/ab_transparent_qntm_alpha.9.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/ic_audio_alarm.png b/core/res/res/drawable-mdpi/ic_audio_alarm_alpha.png Binary files differindex fab95aadf09b..fab95aadf09b 100644 --- a/core/res/res/drawable-mdpi/ic_audio_alarm.png +++ b/core/res/res/drawable-mdpi/ic_audio_alarm_alpha.png diff --git a/core/res/res/drawable-mdpi/ic_audio_alarm_mute.png b/core/res/res/drawable-mdpi/ic_audio_alarm_mute_alpha.png Binary files differindex 451e9321b085..451e9321b085 100644 --- a/core/res/res/drawable-mdpi/ic_audio_alarm_mute.png +++ b/core/res/res/drawable-mdpi/ic_audio_alarm_mute_alpha.png diff --git a/core/res/res/drawable-mdpi/ic_audio_bt.png b/core/res/res/drawable-mdpi/ic_audio_bt_alpha.png Binary files differindex 282c643ddcf9..282c643ddcf9 100644 --- a/core/res/res/drawable-mdpi/ic_audio_bt.png +++ b/core/res/res/drawable-mdpi/ic_audio_bt_alpha.png diff --git a/core/res/res/drawable-mdpi/ic_audio_bt_mute.png b/core/res/res/drawable-mdpi/ic_audio_bt_mute_alpha.png Binary files differindex f734c1c630bb..f734c1c630bb 100644 --- a/core/res/res/drawable-mdpi/ic_audio_bt_mute.png +++ b/core/res/res/drawable-mdpi/ic_audio_bt_mute_alpha.png diff --git a/core/res/res/drawable-mdpi/ic_audio_notification_am.png b/core/res/res/drawable-mdpi/ic_audio_notification_am_alpha.png Binary files differindex b41ccd09cb15..b41ccd09cb15 100644 --- a/core/res/res/drawable-mdpi/ic_audio_notification_am.png +++ b/core/res/res/drawable-mdpi/ic_audio_notification_am_alpha.png diff --git a/core/res/res/drawable-mdpi/ic_audio_notification_mute_am.png b/core/res/res/drawable-mdpi/ic_audio_notification_mute_am_alpha.png Binary files differindex 2567f7678f33..2567f7678f33 100644 --- a/core/res/res/drawable-mdpi/ic_audio_notification_mute_am.png +++ b/core/res/res/drawable-mdpi/ic_audio_notification_mute_am_alpha.png diff --git a/core/res/res/drawable-mdpi/ic_audio_phone_am.png b/core/res/res/drawable-mdpi/ic_audio_phone_am_alpha.png Binary files differindex beda721fdc3c..beda721fdc3c 100644 --- a/core/res/res/drawable-mdpi/ic_audio_phone_am.png +++ b/core/res/res/drawable-mdpi/ic_audio_phone_am_alpha.png diff --git a/core/res/res/drawable-mdpi/ic_audio_ring_notif_am.png b/core/res/res/drawable-mdpi/ic_audio_ring_notif_am_alpha.png Binary files differindex 1ce4f52d36dc..1ce4f52d36dc 100644 --- a/core/res/res/drawable-mdpi/ic_audio_ring_notif_am.png +++ b/core/res/res/drawable-mdpi/ic_audio_ring_notif_am_alpha.png diff --git a/core/res/res/drawable-mdpi/ic_audio_ring_notif_mute_am.png b/core/res/res/drawable-mdpi/ic_audio_ring_notif_mute_am_alpha.png Binary files differindex cb17415d4aa3..cb17415d4aa3 100644 --- a/core/res/res/drawable-mdpi/ic_audio_ring_notif_mute_am.png +++ b/core/res/res/drawable-mdpi/ic_audio_ring_notif_mute_am_alpha.png diff --git a/core/res/res/drawable-mdpi/ic_audio_ring_notif_vibrate_am.png b/core/res/res/drawable-mdpi/ic_audio_ring_notif_vibrate_am_alpha.png Binary files differindex 2d99b7695a24..2d99b7695a24 100644 --- a/core/res/res/drawable-mdpi/ic_audio_ring_notif_vibrate_am.png +++ b/core/res/res/drawable-mdpi/ic_audio_ring_notif_vibrate_am_alpha.png diff --git a/core/res/res/drawable-mdpi/ic_audio_vol_am.png b/core/res/res/drawable-mdpi/ic_audio_vol_am_alpha.png Binary files differindex c32fdbc0d70c..c32fdbc0d70c 100644 --- a/core/res/res/drawable-mdpi/ic_audio_vol_am.png +++ b/core/res/res/drawable-mdpi/ic_audio_vol_am_alpha.png diff --git a/core/res/res/drawable-mdpi/ic_audio_vol_mute_am.png b/core/res/res/drawable-mdpi/ic_audio_vol_mute_am_alpha.png Binary files differindex 0dfc21ff7ce0..0dfc21ff7ce0 100644 --- a/core/res/res/drawable-mdpi/ic_audio_vol_mute_am.png +++ b/core/res/res/drawable-mdpi/ic_audio_vol_mute_am_alpha.png diff --git a/core/res/res/drawable-mdpi/ic_lock_airplane_mode.png b/core/res/res/drawable-mdpi/ic_lock_airplane_mode_alpha.png Binary files differindex 2b1dc1a8c5da..2b1dc1a8c5da 100644 --- a/core/res/res/drawable-mdpi/ic_lock_airplane_mode.png +++ b/core/res/res/drawable-mdpi/ic_lock_airplane_mode_alpha.png diff --git a/core/res/res/drawable-mdpi/ic_lock_airplane_mode_off_am.png b/core/res/res/drawable-mdpi/ic_lock_airplane_mode_off_am_alpha.png Binary files differindex 49ed3d2ddb87..49ed3d2ddb87 100644 --- a/core/res/res/drawable-mdpi/ic_lock_airplane_mode_off_am.png +++ b/core/res/res/drawable-mdpi/ic_lock_airplane_mode_off_am_alpha.png diff --git a/core/res/res/drawable-mdpi/ic_lock_bugreport_alpha.png b/core/res/res/drawable-mdpi/ic_lock_bugreport_alpha.png Binary files differnew file mode 100644 index 000000000000..4e2612df11c2 --- /dev/null +++ b/core/res/res/drawable-mdpi/ic_lock_bugreport_alpha.png diff --git a/core/res/res/drawable-mdpi/ic_lock_idle_alarm.png b/core/res/res/drawable-mdpi/ic_lock_idle_alarm_alpha.png Binary files differindex b5d3e0980551..b5d3e0980551 100644 --- a/core/res/res/drawable-mdpi/ic_lock_idle_alarm.png +++ b/core/res/res/drawable-mdpi/ic_lock_idle_alarm_alpha.png diff --git a/core/res/res/drawable-mdpi/ic_lock_lock.png b/core/res/res/drawable-mdpi/ic_lock_lock_alpha.png Binary files differindex 5ff3654d3172..5ff3654d3172 100644 --- a/core/res/res/drawable-mdpi/ic_lock_lock.png +++ b/core/res/res/drawable-mdpi/ic_lock_lock_alpha.png diff --git a/core/res/res/drawable-mdpi/ic_lock_power_off.png b/core/res/res/drawable-mdpi/ic_lock_power_off_alpha.png Binary files differindex 2c55e475bac1..2c55e475bac1 100644 --- a/core/res/res/drawable-mdpi/ic_lock_power_off.png +++ b/core/res/res/drawable-mdpi/ic_lock_power_off_alpha.png diff --git a/core/res/res/drawable-mdpi/ic_lock_ringer_off.png b/core/res/res/drawable-mdpi/ic_lock_ringer_off_alpha.png Binary files differindex 98cfb11e4081..98cfb11e4081 100644 --- a/core/res/res/drawable-mdpi/ic_lock_ringer_off.png +++ b/core/res/res/drawable-mdpi/ic_lock_ringer_off_alpha.png diff --git a/core/res/res/drawable-mdpi/ic_lock_ringer_on.png b/core/res/res/drawable-mdpi/ic_lock_ringer_on_alpha.png Binary files differindex 691b99e3b200..691b99e3b200 100644 --- a/core/res/res/drawable-mdpi/ic_lock_ringer_on.png +++ b/core/res/res/drawable-mdpi/ic_lock_ringer_on_alpha.png diff --git a/core/res/res/drawable-xhdpi/ab_bottom_solid_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/ab_bottom_solid_qntm_alpha.9.png Binary files differdeleted file mode 100644 index 312f76c047e4..000000000000 --- a/core/res/res/drawable-xhdpi/ab_bottom_solid_qntm_alpha.9.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/ab_bottom_transparent_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/ab_bottom_transparent_qntm_alpha.9.png Binary files differdeleted file mode 100644 index 49e0a49f9b89..000000000000 --- a/core/res/res/drawable-xhdpi/ab_bottom_transparent_qntm_alpha.9.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/ab_solid_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/ab_solid_qntm_alpha.9.png Binary files differdeleted file mode 100644 index fb0d0b6b5419..000000000000 --- a/core/res/res/drawable-xhdpi/ab_solid_qntm_alpha.9.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/ab_stacked_solid_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/ab_stacked_solid_qntm_alpha.9.png Binary files differdeleted file mode 100644 index 6d8789022efc..000000000000 --- a/core/res/res/drawable-xhdpi/ab_stacked_solid_qntm_alpha.9.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/ab_stacked_transparent_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/ab_stacked_transparent_qntm_alpha.9.png Binary files differdeleted file mode 100644 index 58f3e0fac378..000000000000 --- a/core/res/res/drawable-xhdpi/ab_stacked_transparent_qntm_alpha.9.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/ab_transparent_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/ab_transparent_qntm_alpha.9.png Binary files differdeleted file mode 100644 index c37ba9ee25e1..000000000000 --- a/core/res/res/drawable-xhdpi/ab_transparent_qntm_alpha.9.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/ic_audio_alarm.png b/core/res/res/drawable-xhdpi/ic_audio_alarm_alpha.png Binary files differindex c1f56a1d6509..c1f56a1d6509 100644 --- a/core/res/res/drawable-xhdpi/ic_audio_alarm.png +++ b/core/res/res/drawable-xhdpi/ic_audio_alarm_alpha.png diff --git a/core/res/res/drawable-xhdpi/ic_audio_alarm_mute.png b/core/res/res/drawable-xhdpi/ic_audio_alarm_mute_alpha.png Binary files differindex 0d7034f2f0ef..0d7034f2f0ef 100644 --- a/core/res/res/drawable-xhdpi/ic_audio_alarm_mute.png +++ b/core/res/res/drawable-xhdpi/ic_audio_alarm_mute_alpha.png diff --git a/core/res/res/drawable-xhdpi/ic_audio_bt.png b/core/res/res/drawable-xhdpi/ic_audio_bt_alpha.png Binary files differindex b8aa083ac178..b8aa083ac178 100644 --- a/core/res/res/drawable-xhdpi/ic_audio_bt.png +++ b/core/res/res/drawable-xhdpi/ic_audio_bt_alpha.png diff --git a/core/res/res/drawable-xhdpi/ic_audio_bt_mute.png b/core/res/res/drawable-xhdpi/ic_audio_bt_mute_alpha.png Binary files differindex 93a248178a35..93a248178a35 100644 --- a/core/res/res/drawable-xhdpi/ic_audio_bt_mute.png +++ b/core/res/res/drawable-xhdpi/ic_audio_bt_mute_alpha.png diff --git a/core/res/res/drawable-xhdpi/ic_audio_notification_am.png b/core/res/res/drawable-xhdpi/ic_audio_notification_am_alpha.png Binary files differindex 15182b952805..15182b952805 100644 --- a/core/res/res/drawable-xhdpi/ic_audio_notification_am.png +++ b/core/res/res/drawable-xhdpi/ic_audio_notification_am_alpha.png diff --git a/core/res/res/drawable-xhdpi/ic_audio_notification_mute_am.png b/core/res/res/drawable-xhdpi/ic_audio_notification_mute_am_alpha.png Binary files differindex c26b839bd577..c26b839bd577 100644 --- a/core/res/res/drawable-xhdpi/ic_audio_notification_mute_am.png +++ b/core/res/res/drawable-xhdpi/ic_audio_notification_mute_am_alpha.png diff --git a/core/res/res/drawable-xhdpi/ic_audio_phone_am.png b/core/res/res/drawable-xhdpi/ic_audio_phone_am_alpha.png Binary files differindex 2a04619b4faa..2a04619b4faa 100644 --- a/core/res/res/drawable-xhdpi/ic_audio_phone_am.png +++ b/core/res/res/drawable-xhdpi/ic_audio_phone_am_alpha.png diff --git a/core/res/res/drawable-xhdpi/ic_audio_ring_notif_am.png b/core/res/res/drawable-xhdpi/ic_audio_ring_notif_am_alpha.png Binary files differindex 0df19342106c..0df19342106c 100644 --- a/core/res/res/drawable-xhdpi/ic_audio_ring_notif_am.png +++ b/core/res/res/drawable-xhdpi/ic_audio_ring_notif_am_alpha.png diff --git a/core/res/res/drawable-xhdpi/ic_audio_ring_notif_mute_am.png b/core/res/res/drawable-xhdpi/ic_audio_ring_notif_mute_am_alpha.png Binary files differindex 85acb93db2b5..85acb93db2b5 100644 --- a/core/res/res/drawable-xhdpi/ic_audio_ring_notif_mute_am.png +++ b/core/res/res/drawable-xhdpi/ic_audio_ring_notif_mute_am_alpha.png diff --git a/core/res/res/drawable-xhdpi/ic_audio_ring_notif_vibrate_am.png b/core/res/res/drawable-xhdpi/ic_audio_ring_notif_vibrate_am_alpha.png Binary files differindex 122c7081c0ee..122c7081c0ee 100644 --- a/core/res/res/drawable-xhdpi/ic_audio_ring_notif_vibrate_am.png +++ b/core/res/res/drawable-xhdpi/ic_audio_ring_notif_vibrate_am_alpha.png diff --git a/core/res/res/drawable-xhdpi/ic_audio_vol_am.png b/core/res/res/drawable-xhdpi/ic_audio_vol_am_alpha.png Binary files differindex 4e2e20e2252c..4e2e20e2252c 100644 --- a/core/res/res/drawable-xhdpi/ic_audio_vol_am.png +++ b/core/res/res/drawable-xhdpi/ic_audio_vol_am_alpha.png diff --git a/core/res/res/drawable-xhdpi/ic_audio_vol_mute_am.png b/core/res/res/drawable-xhdpi/ic_audio_vol_mute_am_alpha.png Binary files differindex 64a52150192e..64a52150192e 100644 --- a/core/res/res/drawable-xhdpi/ic_audio_vol_mute_am.png +++ b/core/res/res/drawable-xhdpi/ic_audio_vol_mute_am_alpha.png diff --git a/core/res/res/drawable-xhdpi/ic_lock_airplane_mode.png b/core/res/res/drawable-xhdpi/ic_lock_airplane_mode_alpha.png Binary files differindex dc7a9172eb76..dc7a9172eb76 100644 --- a/core/res/res/drawable-xhdpi/ic_lock_airplane_mode.png +++ b/core/res/res/drawable-xhdpi/ic_lock_airplane_mode_alpha.png diff --git a/core/res/res/drawable-xhdpi/ic_lock_airplane_mode_off_am.png b/core/res/res/drawable-xhdpi/ic_lock_airplane_mode_off_am_alpha.png Binary files differindex 497ca2b81e99..497ca2b81e99 100644 --- a/core/res/res/drawable-xhdpi/ic_lock_airplane_mode_off_am.png +++ b/core/res/res/drawable-xhdpi/ic_lock_airplane_mode_off_am_alpha.png diff --git a/core/res/res/drawable-xhdpi/ic_lock_bugreport_alpha.png b/core/res/res/drawable-xhdpi/ic_lock_bugreport_alpha.png Binary files differnew file mode 100644 index 000000000000..e6ca1ea1d623 --- /dev/null +++ b/core/res/res/drawable-xhdpi/ic_lock_bugreport_alpha.png diff --git a/core/res/res/drawable-xhdpi/ic_lock_idle_alarm.png b/core/res/res/drawable-xhdpi/ic_lock_idle_alarm_alpha.png Binary files differindex 2822a922fa48..2822a922fa48 100644 --- a/core/res/res/drawable-xhdpi/ic_lock_idle_alarm.png +++ b/core/res/res/drawable-xhdpi/ic_lock_idle_alarm_alpha.png diff --git a/core/res/res/drawable-xhdpi/ic_lock_lock.png b/core/res/res/drawable-xhdpi/ic_lock_lock_alpha.png Binary files differindex 086a0ca0aac4..086a0ca0aac4 100644 --- a/core/res/res/drawable-xhdpi/ic_lock_lock.png +++ b/core/res/res/drawable-xhdpi/ic_lock_lock_alpha.png diff --git a/core/res/res/drawable-xhdpi/ic_lock_power_off.png b/core/res/res/drawable-xhdpi/ic_lock_power_off_alpha.png Binary files differindex 530236ccd8df..530236ccd8df 100644 --- a/core/res/res/drawable-xhdpi/ic_lock_power_off.png +++ b/core/res/res/drawable-xhdpi/ic_lock_power_off_alpha.png diff --git a/core/res/res/drawable-xhdpi/ic_lock_ringer_off.png b/core/res/res/drawable-xhdpi/ic_lock_ringer_off_alpha.png Binary files differindex dff2c8932660..dff2c8932660 100644 --- a/core/res/res/drawable-xhdpi/ic_lock_ringer_off.png +++ b/core/res/res/drawable-xhdpi/ic_lock_ringer_off_alpha.png diff --git a/core/res/res/drawable-xhdpi/ic_lock_ringer_on.png b/core/res/res/drawable-xhdpi/ic_lock_ringer_on_alpha.png Binary files differindex 98341b06265f..98341b06265f 100644 --- a/core/res/res/drawable-xhdpi/ic_lock_ringer_on.png +++ b/core/res/res/drawable-xhdpi/ic_lock_ringer_on_alpha.png diff --git a/core/res/res/drawable-xxhdpi/ab_bottom_solid_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/ab_bottom_solid_qntm_alpha.9.png Binary files differdeleted file mode 100644 index 65b8d4af6194..000000000000 --- a/core/res/res/drawable-xxhdpi/ab_bottom_solid_qntm_alpha.9.png +++ /dev/null diff --git a/core/res/res/drawable-xxhdpi/ab_bottom_transparent_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/ab_bottom_transparent_qntm_alpha.9.png Binary files differdeleted file mode 100644 index a397baadf22c..000000000000 --- a/core/res/res/drawable-xxhdpi/ab_bottom_transparent_qntm_alpha.9.png +++ /dev/null diff --git a/core/res/res/drawable-xxhdpi/ab_solid_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/ab_solid_qntm_alpha.9.png Binary files differdeleted file mode 100644 index b56fa4ad1614..000000000000 --- a/core/res/res/drawable-xxhdpi/ab_solid_qntm_alpha.9.png +++ /dev/null diff --git a/core/res/res/drawable-xxhdpi/ab_stacked_solid_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/ab_stacked_solid_qntm_alpha.9.png Binary files differdeleted file mode 100644 index 71b9737011b0..000000000000 --- a/core/res/res/drawable-xxhdpi/ab_stacked_solid_qntm_alpha.9.png +++ /dev/null diff --git a/core/res/res/drawable-xxhdpi/ab_stacked_transparent_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/ab_stacked_transparent_qntm_alpha.9.png Binary files differdeleted file mode 100644 index e2cc71531b7c..000000000000 --- a/core/res/res/drawable-xxhdpi/ab_stacked_transparent_qntm_alpha.9.png +++ /dev/null diff --git a/core/res/res/drawable-xxhdpi/ab_transparent_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/ab_transparent_qntm_alpha.9.png Binary files differdeleted file mode 100644 index 605d1de1e107..000000000000 --- a/core/res/res/drawable-xxhdpi/ab_transparent_qntm_alpha.9.png +++ /dev/null diff --git a/core/res/res/drawable-xxhdpi/ic_audio_alarm.png b/core/res/res/drawable-xxhdpi/ic_audio_alarm_alpha.png Binary files differindex c1c3d35514d4..c1c3d35514d4 100755 --- a/core/res/res/drawable-xxhdpi/ic_audio_alarm.png +++ b/core/res/res/drawable-xxhdpi/ic_audio_alarm_alpha.png diff --git a/core/res/res/drawable-xxhdpi/ic_audio_alarm_mute.png b/core/res/res/drawable-xxhdpi/ic_audio_alarm_mute_alpha.png Binary files differindex 4bcee685021a..4bcee685021a 100644 --- a/core/res/res/drawable-xxhdpi/ic_audio_alarm_mute.png +++ b/core/res/res/drawable-xxhdpi/ic_audio_alarm_mute_alpha.png diff --git a/core/res/res/drawable-xxhdpi/ic_audio_bt.png b/core/res/res/drawable-xxhdpi/ic_audio_bt_alpha.png Binary files differindex 140edac614b5..140edac614b5 100755 --- a/core/res/res/drawable-xxhdpi/ic_audio_bt.png +++ b/core/res/res/drawable-xxhdpi/ic_audio_bt_alpha.png diff --git a/core/res/res/drawable-xxhdpi/ic_audio_bt_mute.png b/core/res/res/drawable-xxhdpi/ic_audio_bt_mute_alpha.png Binary files differindex 97829b456fda..97829b456fda 100644 --- a/core/res/res/drawable-xxhdpi/ic_audio_bt_mute.png +++ b/core/res/res/drawable-xxhdpi/ic_audio_bt_mute_alpha.png diff --git a/core/res/res/drawable-xxhdpi/ic_audio_notification_am.png b/core/res/res/drawable-xxhdpi/ic_audio_notification_am_alpha.png Binary files differindex fb0e96e86813..fb0e96e86813 100755 --- a/core/res/res/drawable-xxhdpi/ic_audio_notification_am.png +++ b/core/res/res/drawable-xxhdpi/ic_audio_notification_am_alpha.png diff --git a/core/res/res/drawable-xxhdpi/ic_audio_notification_mute_am.png b/core/res/res/drawable-xxhdpi/ic_audio_notification_mute_am_alpha.png Binary files differindex 3aa7b53fd4cc..3aa7b53fd4cc 100644 --- a/core/res/res/drawable-xxhdpi/ic_audio_notification_mute_am.png +++ b/core/res/res/drawable-xxhdpi/ic_audio_notification_mute_am_alpha.png diff --git a/core/res/res/drawable-xxhdpi/ic_audio_phone_am.png b/core/res/res/drawable-xxhdpi/ic_audio_phone_am_alpha.png Binary files differindex 1fd54a10eaa8..1fd54a10eaa8 100644 --- a/core/res/res/drawable-xxhdpi/ic_audio_phone_am.png +++ b/core/res/res/drawable-xxhdpi/ic_audio_phone_am_alpha.png diff --git a/core/res/res/drawable-xxhdpi/ic_audio_ring_notif_am.png b/core/res/res/drawable-xxhdpi/ic_audio_ring_notif_am_alpha.png Binary files differindex 699711c62c99..699711c62c99 100644 --- a/core/res/res/drawable-xxhdpi/ic_audio_ring_notif_am.png +++ b/core/res/res/drawable-xxhdpi/ic_audio_ring_notif_am_alpha.png diff --git a/core/res/res/drawable-xxhdpi/ic_audio_ring_notif_mute_am.png b/core/res/res/drawable-xxhdpi/ic_audio_ring_notif_mute_am_alpha.png Binary files differindex 19d92ba60917..19d92ba60917 100644 --- a/core/res/res/drawable-xxhdpi/ic_audio_ring_notif_mute_am.png +++ b/core/res/res/drawable-xxhdpi/ic_audio_ring_notif_mute_am_alpha.png diff --git a/core/res/res/drawable-xxhdpi/ic_audio_ring_notif_vibrate_am.png b/core/res/res/drawable-xxhdpi/ic_audio_ring_notif_vibrate_am_alpha.png Binary files differindex fdcfd56d20d2..fdcfd56d20d2 100644 --- a/core/res/res/drawable-xxhdpi/ic_audio_ring_notif_vibrate_am.png +++ b/core/res/res/drawable-xxhdpi/ic_audio_ring_notif_vibrate_am_alpha.png diff --git a/core/res/res/drawable-xxhdpi/ic_audio_vol_am.png b/core/res/res/drawable-xxhdpi/ic_audio_vol_am_alpha.png Binary files differindex 15b63116b30b..15b63116b30b 100755 --- a/core/res/res/drawable-xxhdpi/ic_audio_vol_am.png +++ b/core/res/res/drawable-xxhdpi/ic_audio_vol_am_alpha.png diff --git a/core/res/res/drawable-xxhdpi/ic_audio_vol_mute_am.png b/core/res/res/drawable-xxhdpi/ic_audio_vol_mute_am_alpha.png Binary files differindex b8f41119a13d..b8f41119a13d 100644 --- a/core/res/res/drawable-xxhdpi/ic_audio_vol_mute_am.png +++ b/core/res/res/drawable-xxhdpi/ic_audio_vol_mute_am_alpha.png diff --git a/core/res/res/drawable-xxhdpi/ic_lock_airplane_mode.png b/core/res/res/drawable-xxhdpi/ic_lock_airplane_mode_alpha.png Binary files differindex 116b891d869e..116b891d869e 100644 --- a/core/res/res/drawable-xxhdpi/ic_lock_airplane_mode.png +++ b/core/res/res/drawable-xxhdpi/ic_lock_airplane_mode_alpha.png diff --git a/core/res/res/drawable-xxhdpi/ic_lock_airplane_mode_off_am.png b/core/res/res/drawable-xxhdpi/ic_lock_airplane_mode_off_am_alpha.png Binary files differindex 5ca72edae31b..5ca72edae31b 100644 --- a/core/res/res/drawable-xxhdpi/ic_lock_airplane_mode_off_am.png +++ b/core/res/res/drawable-xxhdpi/ic_lock_airplane_mode_off_am_alpha.png diff --git a/core/res/res/drawable-xxhdpi/ic_lock_bugreport_alpha.png b/core/res/res/drawable-xxhdpi/ic_lock_bugreport_alpha.png Binary files differnew file mode 100644 index 000000000000..d6018dd5d8f0 --- /dev/null +++ b/core/res/res/drawable-xxhdpi/ic_lock_bugreport_alpha.png diff --git a/core/res/res/drawable-xxhdpi/ic_lock_idle_alarm.png b/core/res/res/drawable-xxhdpi/ic_lock_idle_alarm_alpha.png Binary files differindex ed2d3c528523..ed2d3c528523 100644 --- a/core/res/res/drawable-xxhdpi/ic_lock_idle_alarm.png +++ b/core/res/res/drawable-xxhdpi/ic_lock_idle_alarm_alpha.png diff --git a/core/res/res/drawable-xxhdpi/ic_lock_lock.png b/core/res/res/drawable-xxhdpi/ic_lock_lock_alpha.png Binary files differindex 1b8882c7a81f..1b8882c7a81f 100644 --- a/core/res/res/drawable-xxhdpi/ic_lock_lock.png +++ b/core/res/res/drawable-xxhdpi/ic_lock_lock_alpha.png diff --git a/core/res/res/drawable-xxhdpi/ic_lock_power_off.png b/core/res/res/drawable-xxhdpi/ic_lock_power_off_alpha.png Binary files differindex 061dc786ed25..061dc786ed25 100644 --- a/core/res/res/drawable-xxhdpi/ic_lock_power_off.png +++ b/core/res/res/drawable-xxhdpi/ic_lock_power_off_alpha.png diff --git a/core/res/res/drawable/ab_bottom_transparent_quantum.xml b/core/res/res/drawable/ab_bottom_transparent_quantum.xml deleted file mode 100644 index 29df6b909c73..000000000000 --- a/core/res/res/drawable/ab_bottom_transparent_quantum.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2014 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<bitmap xmlns:android="http://schemas.android.com/apk/res/android" - android:src="@drawable/ab_bottom_transparent_qntm_alpha" - android:tint="?attr/colorBackground" - android:tintMode="multiply" /> diff --git a/core/res/res/drawable/ab_stacked_transparent_quantum.xml b/core/res/res/drawable/ab_stacked_transparent_quantum.xml deleted file mode 100644 index bdae6b9bc399..000000000000 --- a/core/res/res/drawable/ab_stacked_transparent_quantum.xml +++ /dev/null @@ -1,20 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2014 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<bitmap xmlns:android="http://schemas.android.com/apk/res/android" - android:src="@drawable/ab_stacked_transparent_qntm_alpha" - android:tint="?attr/colorBackground" - android:tintMode="multiply" /> diff --git a/core/res/res/drawable/ab_transparent_quantum.xml b/core/res/res/drawable/ic_audio_alarm.xml index 495bfb6d59c7..d3e547088f88 100644 --- a/core/res/res/drawable/ab_transparent_quantum.xml +++ b/core/res/res/drawable/ic_audio_alarm.xml @@ -15,5 +15,5 @@ --> <bitmap xmlns:android="http://schemas.android.com/apk/res/android" - android:src="@drawable/ab_transparent_qntm_alpha" + android:src="@drawable/ic_audio_alarm_alpha" android:tint="?attr/colorControlNormal" /> diff --git a/core/res/res/drawable/ab_solid_quantum.xml b/core/res/res/drawable/ic_audio_alarm_mute.xml index e56bb401ad4d..7d85872a925c 100644 --- a/core/res/res/drawable/ab_solid_quantum.xml +++ b/core/res/res/drawable/ic_audio_alarm_mute.xml @@ -15,6 +15,5 @@ --> <bitmap xmlns:android="http://schemas.android.com/apk/res/android" - android:src="@drawable/ab_solid_qntm_alpha" - android:tint="?attr/colorBackground" - android:tintMode="multiply" /> + android:src="@drawable/ic_audio_alarm_mute_alpha" + android:tint="?attr/colorControlNormal" /> diff --git a/core/res/res/drawable/ab_bottom_solid_quantum.xml b/core/res/res/drawable/ic_audio_bt.xml index 848737e30cc2..4f5af3d09588 100644 --- a/core/res/res/drawable/ab_bottom_solid_quantum.xml +++ b/core/res/res/drawable/ic_audio_bt.xml @@ -15,6 +15,5 @@ --> <bitmap xmlns:android="http://schemas.android.com/apk/res/android" - android:src="@drawable/ab_bottom_solid_qntm_alpha" - android:tint="?attr/colorBackground" - android:tintMode="multiply" /> + android:src="@drawable/ic_audio_bt_alpha" + android:tint="?attr/colorControlNormal" /> diff --git a/core/res/res/drawable/ab_stacked_solid_quantum.xml b/core/res/res/drawable/ic_audio_bt_mute.xml index df775afa29d0..d2004c0efdd3 100644 --- a/core/res/res/drawable/ab_stacked_solid_quantum.xml +++ b/core/res/res/drawable/ic_audio_bt_mute.xml @@ -15,6 +15,5 @@ --> <bitmap xmlns:android="http://schemas.android.com/apk/res/android" - android:src="@drawable/ab_stacked_solid_qntm_alpha" - android:tint="?attr/colorBackground" - android:tintMode="multiply" /> + android:src="@drawable/ic_audio_bt_mute_alpha" + android:tint="?attr/colorControlNormal" /> diff --git a/core/res/res/drawable/ic_audio_notification.xml b/core/res/res/drawable/ic_audio_notification.xml index b87e4c8b9d5f..65249f51ccd8 100644 --- a/core/res/res/drawable/ic_audio_notification.xml +++ b/core/res/res/drawable/ic_audio_notification.xml @@ -18,6 +18,6 @@ --> <bitmap xmlns:android="http://schemas.android.com/apk/res/android" - android:src="@drawable/ic_audio_notification_am" - android:autoMirrored="true"> -</bitmap> + android:src="@drawable/ic_audio_notification_am_alpha" + android:autoMirrored="true" + android:tint="?attr/colorControlNormal" /> diff --git a/core/res/res/drawable/ic_audio_notification_mute.xml b/core/res/res/drawable/ic_audio_notification_mute.xml index 1caf27c4e318..af6a8e053fcf 100644 --- a/core/res/res/drawable/ic_audio_notification_mute.xml +++ b/core/res/res/drawable/ic_audio_notification_mute.xml @@ -18,6 +18,6 @@ --> <bitmap xmlns:android="http://schemas.android.com/apk/res/android" - android:src="@drawable/ic_audio_notification_mute_am" - android:autoMirrored="true"> -</bitmap> + android:src="@drawable/ic_audio_notification_mute_am_alpha" + android:autoMirrored="true" + android:tint="?attr/colorControlNormal" /> diff --git a/core/res/res/drawable/ic_audio_phone.xml b/core/res/res/drawable/ic_audio_phone.xml index e6869fd0582c..1bab863b426e 100644 --- a/core/res/res/drawable/ic_audio_phone.xml +++ b/core/res/res/drawable/ic_audio_phone.xml @@ -18,6 +18,6 @@ --> <bitmap xmlns:android="http://schemas.android.com/apk/res/android" - android:src="@drawable/ic_audio_phone_am" - android:autoMirrored="true"> -</bitmap> + android:src="@drawable/ic_audio_phone_am_alpha" + android:autoMirrored="true" + android:tint="?attr/colorControlNormal" /> diff --git a/core/res/res/drawable/ic_audio_ring_notif.xml b/core/res/res/drawable/ic_audio_ring_notif.xml index 2f48741ea768..247d1b48f850 100644 --- a/core/res/res/drawable/ic_audio_ring_notif.xml +++ b/core/res/res/drawable/ic_audio_ring_notif.xml @@ -18,6 +18,6 @@ --> <bitmap xmlns:android="http://schemas.android.com/apk/res/android" - android:src="@drawable/ic_audio_ring_notif_am" - android:autoMirrored="true"> -</bitmap> + android:src="@drawable/ic_audio_ring_notif_am_alpha" + android:autoMirrored="true" + android:tint="?attr/colorControlNormal" /> diff --git a/core/res/res/drawable/ic_audio_ring_notif_mute.xml b/core/res/res/drawable/ic_audio_ring_notif_mute.xml index 7549f6dc046a..72aaa9dc04ea 100644 --- a/core/res/res/drawable/ic_audio_ring_notif_mute.xml +++ b/core/res/res/drawable/ic_audio_ring_notif_mute.xml @@ -18,6 +18,6 @@ --> <bitmap xmlns:android="http://schemas.android.com/apk/res/android" - android:src="@drawable/ic_audio_ring_notif_mute_am" - android:autoMirrored="true"> -</bitmap> + android:src="@drawable/ic_audio_ring_notif_mute_am_alpha" + android:autoMirrored="true" + android:tint="?attr/colorControlNormal" /> diff --git a/core/res/res/drawable/ic_audio_ring_notif_vibrate.xml b/core/res/res/drawable/ic_audio_ring_notif_vibrate.xml index 3481e27861eb..9e31aba1305c 100644 --- a/core/res/res/drawable/ic_audio_ring_notif_vibrate.xml +++ b/core/res/res/drawable/ic_audio_ring_notif_vibrate.xml @@ -18,6 +18,6 @@ --> <bitmap xmlns:android="http://schemas.android.com/apk/res/android" - android:src="@drawable/ic_audio_ring_notif_vibrate_am" - android:autoMirrored="true"> -</bitmap> + android:src="@drawable/ic_audio_ring_notif_vibrate_am_alpha" + android:autoMirrored="true" + android:tint="?attr/colorControlNormal" /> diff --git a/core/res/res/drawable/ic_audio_vol.xml b/core/res/res/drawable/ic_audio_vol.xml index 6dd249bf0fb2..8d07ded12f2a 100644 --- a/core/res/res/drawable/ic_audio_vol.xml +++ b/core/res/res/drawable/ic_audio_vol.xml @@ -18,6 +18,6 @@ --> <bitmap xmlns:android="http://schemas.android.com/apk/res/android" - android:src="@drawable/ic_audio_vol_am" - android:autoMirrored="true"> -</bitmap> + android:src="@drawable/ic_audio_vol_am_alpha" + android:autoMirrored="true" + android:tint="?attr/colorControlNormal" /> diff --git a/core/res/res/drawable/ic_audio_vol_mute.xml b/core/res/res/drawable/ic_audio_vol_mute.xml index b093f59242db..edbdb23878fb 100644 --- a/core/res/res/drawable/ic_audio_vol_mute.xml +++ b/core/res/res/drawable/ic_audio_vol_mute.xml @@ -18,6 +18,6 @@ --> <bitmap xmlns:android="http://schemas.android.com/apk/res/android" - android:src="@drawable/ic_audio_vol_mute_am" - android:autoMirrored="true"> -</bitmap> + android:src="@drawable/ic_audio_vol_mute_am_alpha" + android:autoMirrored="true" + android:tint="?attr/colorControlNormal" /> diff --git a/core/res/res/drawable/ic_lock_airplane_mode.xml b/core/res/res/drawable/ic_lock_airplane_mode.xml new file mode 100644 index 000000000000..4a169224379a --- /dev/null +++ b/core/res/res/drawable/ic_lock_airplane_mode.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<bitmap xmlns:android="http://schemas.android.com/apk/res/android" + android:src="@drawable/ic_lock_airplane_mode_alpha" + android:tint="?attr/colorControlActivated" /> diff --git a/core/res/res/drawable/ic_lock_airplane_mode_off.xml b/core/res/res/drawable/ic_lock_airplane_mode_off.xml index b344e28ef25f..a8cd390fd0b9 100644 --- a/core/res/res/drawable/ic_lock_airplane_mode_off.xml +++ b/core/res/res/drawable/ic_lock_airplane_mode_off.xml @@ -18,6 +18,6 @@ --> <bitmap xmlns:android="http://schemas.android.com/apk/res/android" - android:src="@drawable/ic_lock_airplane_mode_off_am" - android:autoMirrored="true"> -</bitmap> + android:src="@drawable/ic_lock_airplane_mode_off_am_alpha" + android:autoMirrored="true" + android:tint="?attr/colorControlNormal" /> diff --git a/core/res/res/drawable/ic_lock_bugreport.xml b/core/res/res/drawable/ic_lock_bugreport.xml new file mode 100644 index 000000000000..a3f82ce92589 --- /dev/null +++ b/core/res/res/drawable/ic_lock_bugreport.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<bitmap xmlns:android="http://schemas.android.com/apk/res/android" + android:src="@drawable/ic_lock_bugreport_alpha" + android:tint="?attr/colorControlNormal" /> diff --git a/core/res/res/drawable/ic_lock_idle_alarm.xml b/core/res/res/drawable/ic_lock_idle_alarm.xml new file mode 100644 index 000000000000..e8189bd111d4 --- /dev/null +++ b/core/res/res/drawable/ic_lock_idle_alarm.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<bitmap xmlns:android="http://schemas.android.com/apk/res/android" + android:src="@drawable/ic_lock_idle_alarm_alpha" + android:tint="?attr/colorControlNormal" /> diff --git a/core/res/res/drawable/ic_lock_lock.xml b/core/res/res/drawable/ic_lock_lock.xml new file mode 100644 index 000000000000..39f268a9d6ae --- /dev/null +++ b/core/res/res/drawable/ic_lock_lock.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<bitmap xmlns:android="http://schemas.android.com/apk/res/android" + android:src="@drawable/ic_lock_lock_alpha" + android:tint="?attr/colorControlActivated" /> diff --git a/core/res/res/drawable/ic_lock_power_off.xml b/core/res/res/drawable/ic_lock_power_off.xml new file mode 100644 index 000000000000..718f17ed0c19 --- /dev/null +++ b/core/res/res/drawable/ic_lock_power_off.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<bitmap xmlns:android="http://schemas.android.com/apk/res/android" + android:src="@drawable/ic_lock_power_off_alpha" + android:tint="?attr/colorControlNormal" /> diff --git a/core/res/res/layout/alert_dialog_quantum.xml b/core/res/res/layout/alert_dialog_quantum.xml index 59dba08eddac..98b687973ebd 100644 --- a/core/res/res/layout/alert_dialog_quantum.xml +++ b/core/res/res/layout/alert_dialog_quantum.xml @@ -35,9 +35,10 @@ android:paddingEnd="16dip" android:paddingTop="16dip"> <ImageView android:id="@+id/icon" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingEnd="8dip" + android:layout_width="32dip" + android:layout_height="32dip" + android:layout_marginEnd="8dip" + android:scaleType="fitCenter" android:src="@null" /> <TextView android:id="@+id/alertTitle" style="?android:attr/windowTitleStyle" @@ -101,10 +102,6 @@ android:maxLines="2" android:minHeight="@dimen/alert_dialog_button_bar_height" style="?android:attr/buttonBarButtonStyle" /> - <View android:layout_width="0dp" - android:layout_height="0dp" - android:layout_weight="1" - android:visibility="invisible" /> <Button android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" diff --git a/core/res/res/layout/dialog_custom_title_quantum.xml b/core/res/res/layout/dialog_custom_title_quantum.xml index f8a2bf7509cd..1bb93eb61f39 100644 --- a/core/res/res/layout/dialog_custom_title_quantum.xml +++ b/core/res/res/layout/dialog_custom_title_quantum.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2011 The Android Open Source Project +<!-- Copyright (C) 2014 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -23,17 +23,12 @@ This is a custom layout for a dialog. android:fitsSystemWindows="true"> <FrameLayout android:id="@android:id/title_container" android:layout_width="match_parent" - android:layout_height="@dimen/alert_dialog_title_height" android:layout_weight="0" android:gravity="center_vertical|start" - style="?android:attr/windowTitleBackgroundStyle"> - </FrameLayout> - <View android:id="@+id/titleDivider" - android:layout_width="match_parent" - android:layout_height="2dip" - android:background="@android:color/holo_blue_light" /> + style="?android:attr/windowTitleBackgroundStyle" /> <FrameLayout - android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_width="match_parent" + android:layout_height="wrap_content" android:layout_weight="1" android:orientation="vertical" android:foreground="?android:attr/windowContentOverlay"> diff --git a/core/res/res/layout/dialog_title_icons_quantum.xml b/core/res/res/layout/dialog_title_icons_quantum.xml index e3d771cbf2be..28e20d93ffc3 100644 --- a/core/res/res/layout/dialog_title_icons_quantum.xml +++ b/core/res/res/layout/dialog_title_icons_quantum.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2011 The Android Open Source Project +<!-- Copyright (C) 2014 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -28,9 +28,9 @@ enabled. android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center_vertical" - android:minHeight="@android:dimen/alert_dialog_title_height" android:paddingStart="16dip" - android:paddingEnd="16dip"> + android:paddingEnd="16dip" + android:paddingTop="16dip"> <ImageView android:id="@+id/left_icon" android:layout_width="32dip" android:layout_height="32dip" @@ -48,11 +48,6 @@ enabled. android:layout_marginStart="8dip" /> </LinearLayout> - <View android:id="@+id/titleDivider" - android:layout_width="match_parent" - android:layout_height="1dip" - android:background="@android:color/holo_blue_light" /> - <FrameLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1" diff --git a/core/res/res/layout/dialog_title_quantum.xml b/core/res/res/layout/dialog_title_quantum.xml index 0a692ee61c94..b92c1e701726 100644 --- a/core/res/res/layout/dialog_title_quantum.xml +++ b/core/res/res/layout/dialog_title_quantum.xml @@ -1,21 +1,20 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2011, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ +<!-- Copyright (C) 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- This is an optimized layout for a screen, with the minimum set of features enabled. --> @@ -23,17 +22,16 @@ enabled. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:fitsSystemWindows="true"> - <TextView android:id="@android:id/title" style="?android:attr/windowTitleStyle" + <TextView android:id="@+id/alertTitle" + style="?android:attr/windowTitleStyle" + android:singleLine="true" + android:ellipsize="end" android:layout_width="match_parent" android:layout_height="wrap_content" - android:minHeight="@android:dimen/alert_dialog_title_height" + android:textAlignment="viewStart" android:paddingStart="16dip" android:paddingEnd="16dip" - android:gravity="center_vertical|start" /> - <View android:id="@+id/titleDivider" - android:layout_width="match_parent" - android:layout_height="2dip" - android:background="@android:color/holo_blue_light" /> + android:paddingTop="16dip" /> <FrameLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1" diff --git a/core/res/res/layout/screen_action_bar.xml b/core/res/res/layout/screen_action_bar.xml index b1afec13075b..77f537b32837 100644 --- a/core/res/res/layout/screen_action_bar.xml +++ b/core/res/res/layout/screen_action_bar.xml @@ -24,7 +24,7 @@ This is an optimized layout for a screen with the Action Bar enabled. android:layout_width="match_parent" android:layout_height="match_parent" android:splitMotionEvents="false" - android:theme="?attr/actionBarWidgetTheme"> + android:theme="?attr/actionBarTheme"> <FrameLayout android:id="@android:id/content" android:layout_width="match_parent" android:layout_height="match_parent" /> @@ -33,25 +33,25 @@ This is an optimized layout for a screen with the Action Bar enabled. android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentTop="true" - style="?android:attr/actionBarStyle" + style="?attr/actionBarStyle" android:sharedElementName="android:action_bar" android:gravity="top"> <com.android.internal.widget.ActionBarView android:id="@+id/action_bar" android:layout_width="match_parent" android:layout_height="wrap_content" - style="?android:attr/actionBarStyle" /> + style="?attr/actionBarStyle" /> <com.android.internal.widget.ActionBarContextView android:id="@+id/action_context_bar" android:layout_width="match_parent" android:layout_height="wrap_content" android:visibility="gone" - style="?android:attr/actionModeStyle" /> + style="?attr/actionModeStyle" /> </com.android.internal.widget.ActionBarContainer> <com.android.internal.widget.ActionBarContainer android:id="@+id/split_action_bar" android:layout_width="match_parent" android:layout_height="wrap_content" - style="?android:attr/actionBarSplitStyle" + style="?attr/actionBarSplitStyle" android:visibility="gone" android:gravity="center"/> </com.android.internal.widget.ActionBarOverlayLayout> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index aa5005f00eac..46cb9b22728a 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -650,6 +650,8 @@ <!-- Default ActivityChooserView style. --> <attr name="activityChooserViewStyle" format="reference" /> + <attr name="toolbarStyle" format="reference" /> + <!-- Fast scroller styles --> <eat-comment /> @@ -689,6 +691,10 @@ buttons. actionBarStyle is still used for the primary bar. --> <attr name="actionBarSplitStyle" format="reference" /> + <!-- Reference to a theme that should be used to inflate the + action bar. This will be inherited by any widget inflated + into the action bar. --> + <attr name="actionBarTheme" format="reference" /> <!-- Reference to a theme that should be used to inflate widgets and layouts destined for the action bar. Most of the time this will be a reference to the current theme, but when @@ -911,8 +917,14 @@ <!-- ============= --> <!-- Color palette --> <!-- ============= --> + <attr name="colorPrimaryDark" format="color" /> + <attr name="colorPrimary" format="color" /> + <attr name="colorPrimaryLight" format="color" /> + <attr name="colorAccent" format="color" /> + <attr name="colorControlNormal" format="color" /> <attr name="colorControlActivated" format="color" /> + <attr name="colorButtonNormal" format="color" /> <attr name="colorButtonPressed" format="color" /> <attr name="colorButtonNormalColored" format="color" /> @@ -4889,6 +4901,24 @@ </attr> </declare-styleable> + <!-- Use <code>slide</code>as the root tag of the XML resource that + describes a {@link android.transition.Slide Slide} transition. + The attributes of the {@link android.R.styleable#Transition Transition} + resource are available in addition to the specific attributes of Slide + described here. --> + <declare-styleable name="Slide"> + <attr name="slideEdge"> + <!-- Slide to and from the bottom edge of the Scene. --> + <enum name="left" value="0" /> + <!-- Slide to and from the bottom edge of the Scene. --> + <enum name="top" value="1" /> + <!-- Slide to and from the bottom edge of the Scene. --> + <enum name="right" value="2" /> + <!-- Slide to and from the bottom edge of the Scene. --> + <enum name="bottom" value="3" /> + </attr> + </declare-styleable> + <!-- Use <code>target</code> as the root tag of the XML resource that describes a {@link android.transition.Transition#addTarget(int) targetId} of a transition. There can be one or more targets inside @@ -6235,10 +6265,6 @@ <attr name="inputType" /> </declare-styleable> - <declare-styleable name="ActionBar_LayoutParams"> - <attr name="layout_gravity" /> - </declare-styleable> - <declare-styleable name="Switch"> <!-- Drawable to use as the "thumb" that switches back and forth. --> <attr name="thumb" /> @@ -6447,4 +6473,25 @@ <attr name="textView" format="reference" /> </declare-styleable> + <declare-styleable name="Toolbar"> + <attr name="titleTextAppearance" format="reference" /> + <attr name="subtitleTextAppearance" format="reference" /> + <attr name="title" /> + <attr name="subtitle" /> + <attr name="gravity" /> + <attr name="titleMargins" format="dimension" /> + <attr name="titleMarginStart" format="dimension" /> + <attr name="titleMarginEnd" format="dimension" /> + <attr name="titleMarginTop" format="dimension" /> + <attr name="titleMarginBottom" format="dimension" /> + </declare-styleable> + + <declare-styleable name="Toolbar_LayoutParams"> + <attr name="layout_gravity" /> + </declare-styleable> + + <declare-styleable name="ActionBar_LayoutParams"> + <attr name="layout_gravity" /> + </declare-styleable> + </resources> diff --git a/core/res/res/values/colors_quantum.xml b/core/res/res/values/colors_quantum.xml index d10f7c06aae2..ebe4a495cca1 100644 --- a/core/res/res/values/colors_quantum.xml +++ b/core/res/res/values/colors_quantum.xml @@ -16,16 +16,14 @@ <resources> <color name="background_quantum_dark">@color/black</color> - <color name="background_quantum_light">@color/quantum_grey_50</color> - <color name="secondary_background_quantum_dark">@color/quantum_grey_700</color> - <color name="secondary_background_quantum_light">@color/quantum_grey_100</color> + <color name="background_quantum_light">@color/white</color> <color name="bright_foreground_quantum_dark">@color/background_quantum_light</color> <color name="bright_foreground_quantum_light">@color/background_quantum_dark</color> <!-- TODO: This is 50% alpha black --> <color name="bright_foreground_disabled_quantum_dark">#80000000</color> - <!-- TODO: This is 50% alpha grey_50 --> - <color name="bright_foreground_disabled_quantum_light">#80fafafa</color> + <!-- TODO: This is 50% alpha white --> + <color name="bright_foreground_disabled_quantum_light">#80ffffff</color> <color name="bright_foreground_inverse_quantum_dark">@color/bright_foreground_quantum_light</color> <color name="bright_foreground_inverse_quantum_light">@color/bright_foreground_quantum_dark</color> @@ -34,41 +32,13 @@ <color name="dim_foreground_disabled_quantum_dark">#80bebebe</color> <color name="dim_foreground_disabled_quantum_light">#80323232</color> - <!-- TODO: These should be theme attributes. --> - <color name="control_normal_foreground_quantum_light">@color/secondary_text_quantum_light</color> - <color name="control_activated_foreground_quantum_light">@color/quantum_teal_700</color> - - <!-- TODO: These should be theme attributes. --> - <color name="control_normal_foreground_quantum_dark">@color/secondary_text_quantum_dark</color> - <color name="control_activated_foreground_quantum_dark">@color/quantum_lime_A200</color> - - <!-- TODO: These should be theme attributes. --> - <color name="btn_default_normal_quantum_light">@color/quantum_grey_300</color> - <color name="btn_default_pressed_quantum_light">@color/quantum_grey_500</color> - - <!-- TODO: These should be theme attributes. --> - <color name="btn_default_normal_quantum_dark">@color/quantum_grey_700</color> - <color name="btn_default_pressed_quantum_dark">@color/quantum_grey_500</color> - <color name="hint_foreground_quantum_dark">@color/bright_foreground_disabled_quantum_dark</color> <color name="hint_foreground_quantum_light">@color/bright_foreground_disabled_quantum_light</color> - <!-- TODO: This is 40% alpha lime_A200 --> - <color name="highlighted_text_quantum_dark">#66eeff41</color> - <!-- TODO: This is 40% alpha teal_700 --> + <!-- TODO: This is 40% alpha teal_A200 --> + <color name="highlighted_text_quantum_dark">#660097a7</color> + <!-- TODO: This is 40% alpha teal_A200 --> <color name="highlighted_text_quantum_light">#660097a7</color> - <!-- TODO: These should all be pushed into a TimePicker widget style. --> - <color name="timepicker_default_background_quantum_dark">@color/background_quantum_dark</color> - <color name="timepicker_default_background_quantum_light">@color/background_quantum_light</color> - <color name="timepicker_default_text_color_quantum_dark">@color/bright_foreground_quantum_dark</color> - <color name="timepicker_default_text_color_quantum_light">@color/bright_foreground_quantum_light</color> - <color name="timepicker_default_disabled_color_quantum_dark">@color/bright_foreground_disabled_quantum_dark</color> - <color name="timepicker_default_disabled_color_quantum_light">@color/bright_foreground_disabled_quantum_light</color> - <color name="timepicker_default_ampm_selected_background_color_quantum_dark">@color/control_activated_foreground_quantum_dark</color> - <color name="timepicker_default_ampm_selected_background_color_quantum_light">@color/control_activated_foreground_quantum_light</color> - <color name="timepicker_default_ampm_unselected_background_color_quantum_dark">@color/transparent</color> - <color name="timepicker_default_ampm_unselected_background_color_quantum_light">@color/white</color> - <!-- Primary & accent colors --> <color name="quantum_red_100">#fff4c7c3</color> @@ -155,10 +125,4 @@ <color name="primary_text_quantum_dark">#deffffff</color> <color name="secondary_text_quantum_dark">#8affffff</color> <color name="tertiary_text_quantum_dark">#4dffffff</color> - - <!-- "Theme" colors to be replaced by attrs when available --> - <color name="theme_color_100">@color/quantum_teal_100</color> - <color name="theme_color_300">@color/quantum_teal_300</color> - <color name="theme_color_500">@color/quantum_teal_500</color> - <color name="theme_color_700">@color/quantum_teal_700</color> </resources> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 7ae5a037d16a..fbe066a42213 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -467,6 +467,7 @@ 1 UI_MODE_TYPE_NORMAL 4 UI_MODE_TYPE_TELEVISION 5 UI_MODE_TYPE_APPLIANCE + 6 UI_MODE_TYPE_WATCH Any other values will have surprising consequences. --> <integer name="config_defaultUiModeType">1</integer> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 667addeaeee2..0233b736f63c 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2143,6 +2143,10 @@ <public type="attr" name="colorButtonNormalColored" /> <public type="attr" name="colorButtonPressedColored" /> <public type="attr" name="persistable" /> + <public type="attr" name="titleTextAppearance" /> + <public type="attr" name="subtitleTextAppearance" /> + <public type="attr" name="slideEdge" /> + <public type="attr" name="actionBarTheme" /> <public-padding type="dimen" name="l_resource_pad" end="0x01050010" /> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index be875ffb2b04..fc0fccc91bb9 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -1208,6 +1208,13 @@ please see styles_device_defaults.xml. <item name="android:subtitleTextStyle">@android:style/TextAppearance.Widget.ActionMode.Subtitle</item> </style> + <style name="Widget.Toolbar"> + <item name="android:titleTextAppearance">@android:style/TextAppearance.Widget.Toolbar.Title</item> + <item name="android:subtitleTextAppearance">@android:style/TextAppearance.Widget.Toolbar.Subtitle</item> + <item name="android:minHeight">?android:attr/actionBarSize</item> + <item name="android:titleMargins">4dp</item> + </style> + <style name="TextAppearance.Widget.ActionBar.Title" parent="@android:style/TextAppearance.Medium"> </style> @@ -1225,6 +1232,14 @@ please see styles_device_defaults.xml. <item name="android:textColor">?android:attr/textColorSecondary</item> </style> + <style name="TextAppearance.Widget.Toolbar.Title" + parent="@android:style/TextAppearance.Widget.ActionBar.Title"> + </style> + + <style name="TextAppearance.Widget.Toolbar.Subtitle" + parent="@android:style/TextAppearance.Widget.ActionBar.Subtitle"> + </style> + <style name="Widget.ActionButton"> <item name="android:background">?android:attr/actionBarItemBackground</item> <item name="android:paddingStart">12dip</item> diff --git a/core/res/res/values/styles_quantum.xml b/core/res/res/values/styles_quantum.xml index 85d8761c2d5c..dd148c441363 100644 --- a/core/res/res/values/styles_quantum.xml +++ b/core/res/res/values/styles_quantum.xml @@ -340,13 +340,13 @@ please see styles_device_defaults.xml. <style name="TextAppearance.Quantum.TimePicker.TimeLabel" parent="TextAppearance.Quantum"> <item name="textSize">@dimen/timepicker_time_label_size</item> - <item name="textColor">@color/timepicker_default_text_color_quantum_dark</item> + <item name="textColor">?attr/textColorSecondary</item> </style> <style name="TextAppearance.Quantum.TimePicker.AmPmLabel" parent="TextAppearance.Quantum"> <item name="textSize">@dimen/timepicker_ampm_label_size</item> <item name="textAllCaps">true</item> - <item name="textColor">@color/timepicker_default_text_color_quantum_dark</item> + <item name="textColor">?attr/textColorSecondary</item> <item name="textStyle">bold</item> </style> @@ -377,11 +377,11 @@ please see styles_device_defaults.xml. <style name="TextAppearance.Quantum.Light.CalendarViewWeekDayView" parent="TextAppearance.Quantum.CalendarViewWeekDayView"/> <style name="TextAppearance.Quantum.Light.TimePicker.TimeLabel" parent="TextAppearance.Quantum.TimePicker.TimeLabel"> - <item name="textColor">@color/timepicker_default_text_color_quantum_light</item> + <item name="textColor">?attr/textColorSecondary</item> </style> <style name="TextAppearance.Quantum.Light.TimePicker.AmPmLabel" parent="TextAppearance.Quantum.TimePicker.AmPmLabel"> - <item name="textColor">@color/timepicker_default_text_color_quantum_light</item> + <item name="textColor">?attr/textColorSecondary</item> </style> <!-- Widget Styles --> @@ -545,16 +545,16 @@ please see styles_device_defaults.xml. <style name="Widget.Quantum.TimePicker" parent="Widget.TimePicker"> <item name="legacyLayout">@layout/time_picker_legacy_holo</item> <item name="internalLayout">@layout/time_picker_holo</item> - <item name="disabledColor">@color/timepicker_default_disabled_color_quantum_dark</item> - <item name="headerSelectedTextColor">@color/holo_blue_light</item> - <item name="headerUnselectedTextColor">@color/timepicker_default_text_color_quantum_dark</item> - <item name="headerBackgroundColor">@color/timepicker_default_background_quantum_dark</item> - <item name="numbersTextColor">@color/timepicker_default_text_color_quantum_dark</item> - <item name="numbersBackgroundColor">@color/timepicker_default_background_quantum_dark</item> - <item name="amPmTextColor">@color/timepicker_default_text_color_quantum_dark</item> - <item name="amPmUnselectedBackgroundColor">@color/timepicker_default_background_quantum_dark</item> - <item name="amPmSelectedBackgroundColor">@color/holo_blue_light</item> - <item name="numbersSelectorColor">@color/holo_blue_light</item> + <item name="disabledColor">@color/bright_foreground_disabled_quantum_dark</item> + <item name="headerSelectedTextColor">?attr/colorControlActivated</item> + <item name="headerUnselectedTextColor">?attr/textColorPrimary</item> + <item name="headerBackgroundColor">?attr/colorBackground</item> + <item name="numbersTextColor">?attr/textColorSecondary</item> + <item name="numbersBackgroundColor">?attr/colorControlNormal</item> + <item name="amPmTextColor">?attr/textColorSecondary</item> + <item name="amPmUnselectedBackgroundColor">?attr/colorControlNormal</item> + <item name="amPmSelectedBackgroundColor">?attr/colorControlActivated</item> + <item name="numbersSelectorColor">?attr/colorControlActivated</item> </style> <style name="Widget.Quantum.DatePicker" parent="Widget.DatePicker"> @@ -758,9 +758,9 @@ please see styles_device_defaults.xml. <style name="Widget.Quantum.ActionBar" parent="Widget.ActionBar"> <item name="titleTextStyle">@style/TextAppearance.Quantum.Widget.ActionBar.Title</item> <item name="subtitleTextStyle">@style/TextAppearance.Quantum.Widget.ActionBar.Subtitle</item> - <item name="background">@drawable/ab_transparent_quantum</item> - <item name="backgroundStacked">@drawable/ab_stacked_transparent_quantum</item> - <item name="backgroundSplit">@drawable/ab_bottom_transparent_quantum</item> + <item name="background">@null</item> + <item name="backgroundStacked">@null</item> + <item name="backgroundSplit">@null</item> <item name="divider">?attr/dividerVertical</item> <item name="progressBarStyle">@style/Widget.Quantum.ProgressBar.Horizontal</item> <item name="indeterminateProgressStyle">@style/Widget.Quantum.ProgressBar</item> @@ -769,16 +769,9 @@ please see styles_device_defaults.xml. </style> <style name="Widget.Quantum.ActionBar.Solid"> - <item name="titleTextStyle">@style/TextAppearance.Quantum.Widget.ActionBar.Title</item> - <item name="subtitleTextStyle">@style/TextAppearance.Quantum.Widget.ActionBar.Subtitle</item> - <item name="background">@drawable/ab_solid_quantum</item> - <item name="backgroundStacked">@drawable/ab_stacked_solid_quantum</item> - <item name="backgroundSplit">@drawable/ab_bottom_solid_quantum</item> - <item name="divider">?attr/dividerVertical</item> - <item name="progressBarStyle">@style/Widget.Quantum.ProgressBar.Horizontal</item> - <item name="indeterminateProgressStyle">@style/Widget.Quantum.ProgressBar</item> - <item name="progressBarPadding">32dip</item> - <item name="itemPadding">8dip</item> + <item name="background">?attr/colorPrimary</item> + <item name="backgroundStacked">?attr/colorPrimary</item> + <item name="backgroundSplit">?attr/colorPrimary</item> </style> <style name="Widget.Quantum.ActionMode" parent="Widget.ActionMode"> @@ -869,19 +862,10 @@ please see styles_device_defaults.xml. <style name="Widget.Quantum.Light.NumberPicker" parent="Widget.Quantum.NumberPicker"/> - <style name="Widget.Quantum.Light.TimePicker" parent="Widget.TimePicker"> + <style name="Widget.Quantum.Light.TimePicker" parent="Widget.Quantum.TimePicker"> <item name="legacyLayout">@layout/time_picker_legacy_holo</item> <item name="internalLayout">@layout/time_picker_holo</item> - <item name="disabledColor">@color/timepicker_default_disabled_color_quantum_light</item> - <item name="headerSelectedTextColor">@color/holo_blue_light</item> - <item name="headerUnselectedTextColor">@color/timepicker_default_text_color_quantum_light</item> - <item name="headerBackgroundColor">@color/timepicker_default_background_quantum_light</item> - <item name="numbersTextColor">@color/timepicker_default_text_color_quantum_light</item> - <item name="numbersBackgroundColor">@color/timepicker_default_background_quantum_light</item> - <item name="amPmTextColor">@color/timepicker_default_text_color_quantum_light</item> - <item name="amPmUnselectedBackgroundColor">@color/timepicker_default_background_quantum_light</item> - <item name="amPmSelectedBackgroundColor">@color/holo_blue_light</item> - <item name="numbersSelectorColor">@color/holo_blue_light</item> + <item name="disabledColor">@color/bright_foreground_disabled_quantum_light</item> </style> <style name="Widget.Quantum.Light.DatePicker" parent="Widget.Quantum.DatePicker"/> @@ -985,25 +969,18 @@ please see styles_device_defaults.xml. <style name="Widget.Quantum.Light.ActionBar" parent="Widget.Quantum.ActionBar"> <item name="titleTextStyle">@style/TextAppearance.Quantum.Widget.ActionBar.Title</item> <item name="subtitleTextStyle">@style/TextAppearance.Quantum.Widget.ActionBar.Subtitle</item> - <item name="background">@drawable/ab_transparent_quantum</item> - <item name="backgroundStacked">@drawable/ab_stacked_transparent_quantum</item> - <item name="backgroundSplit">@drawable/ab_bottom_transparent_quantum</item> + <item name="background">@null</item> + <item name="backgroundStacked">@null</item> + <item name="backgroundSplit">@null</item> <item name="homeAsUpIndicator">@drawable/ic_ab_back_quantum</item> <item name="progressBarStyle">@style/Widget.Quantum.Light.ProgressBar.Horizontal</item> <item name="indeterminateProgressStyle">@style/Widget.Quantum.Light.ProgressBar</item> </style> <style name="Widget.Quantum.Light.ActionBar.Solid"> - <item name="titleTextStyle">@style/TextAppearance.Quantum.Widget.ActionBar.Title</item> - <item name="subtitleTextStyle">@style/TextAppearance.Quantum.Widget.ActionBar.Subtitle</item> - <item name="background">@drawable/ab_solid_quantum</item> - <item name="backgroundStacked">@drawable/ab_stacked_solid_quantum</item> - <item name="backgroundSplit">@drawable/ab_bottom_solid_quantum</item> - <item name="divider">?attr/dividerVertical</item> - <item name="progressBarStyle">@style/Widget.Quantum.Light.ProgressBar.Horizontal</item> - <item name="indeterminateProgressStyle">@style/Widget.Quantum.Light.ProgressBar</item> - <item name="progressBarPadding">32dip</item> - <item name="itemPadding">8dip</item> + <item name="background">?attr/colorPrimary</item> + <item name="backgroundStacked">?attr/colorPrimary</item> + <item name="backgroundSplit">?attr/colorPrimary</item> </style> <style name="Widget.Quantum.Light.CompoundButton.Switch" parent="Widget.Quantum.CompoundButton.Switch"> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 557dce244293..682293d7e434 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1829,4 +1829,10 @@ <java-symbol type="color" name="timepicker_default_ampm_selected_background_color_holo_light" /> <java-symbol type="array" name="config_clockTickVibePattern" /> + <!-- From various Quantum changes --> + <java-symbol type="attr" name="toolbarStyle" /> + <java-symbol type="attr" name="titleTextAppearance" /> + <java-symbol type="attr" name="subtitleTextAppearance" /> + <java-symbol type="drawable" name="ic_lock_bugreport" /> + </resources> diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index 3c4e848f6a05..7aea81445eee 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -352,6 +352,8 @@ please see themes_device_defaults.xml. <item name="actionBarDivider">?android:attr/dividerVertical</item> <item name="actionBarItemBackground">?android:attr/selectableItemBackground</item> + <item name="toolbarStyle">@android:style/Widget.Toolbar</item> + <item name="dividerVertical">@drawable/divider_vertical_dark</item> <item name="dividerHorizontal">@drawable/divider_vertical_dark</item> <item name="buttonBarStyle">@android:style/ButtonBar</item> diff --git a/core/res/res/values/themes_quantum.xml b/core/res/res/values/themes_quantum.xml index 50f1ca68c8f1..17bab3071ec1 100644 --- a/core/res/res/values/themes_quantum.xml +++ b/core/res/res/values/themes_quantum.xml @@ -288,8 +288,8 @@ please see themes_device_defaults.xml. <item name="actionDropDownStyle">@style/Widget.Quantum.Spinner.DropDown.ActionBar</item> <item name="actionButtonStyle">@style/Widget.Quantum.ActionButton</item> <item name="actionOverflowButtonStyle">@style/Widget.Quantum.ActionButton.Overflow</item> - <item name="actionModeBackground">@color/theme_color_700</item> - <item name="actionModeSplitBackground">@color/theme_color_700</item> + <item name="actionModeBackground">?attr/colorPrimaryDark</item> + <item name="actionModeSplitBackground">?attr/colorPrimaryDark</item> <item name="actionModeCloseDrawable">@drawable/ic_cab_done_quantum</item> <item name="actionBarTabStyle">@style/Widget.Quantum.ActionBar.TabView</item> <item name="actionBarTabBarStyle">@style/Widget.Quantum.ActionBar.TabBar</item> @@ -334,7 +334,7 @@ please see themes_device_defaults.xml. <item name="timePickerStyle">@style/Widget.Quantum.TimePicker</item> <!-- TimePicker background color --> - <item name="timePickerHeaderBackgroundColor">@color/timepicker_default_background_quantum_dark</item> + <item name="timePickerHeaderBackgroundColor">?colorBackground</item> <!-- TimePicker Header time label text appearance --> <item name="timePickerHeaderTimeLabelTextAppearance">@style/TextAppearance.Quantum.TimePicker.TimeLabel</item> @@ -356,13 +356,18 @@ please see themes_device_defaults.xml. <item name="fastScrollOverlayPosition">atThumb</item> <!-- Color palette --> - <item name="colorControlNormal">@color/control_normal_foreground_quantum_dark</item> - <item name="colorControlActivated">@color/control_activated_foreground_quantum_dark</item> - <item name="colorButtonNormal">@color/btn_default_normal_quantum_dark</item> - <item name="colorButtonPressed">@color/btn_default_pressed_quantum_dark</item> + <item name="colorPrimaryDark">@color/quantum_grey_700</item> + <item name="colorPrimary">@color/quantum_grey_500</item> + <item name="colorPrimaryLight">@color/quantum_grey_100</item> + <item name="colorAccent">@color/quantum_teal_A200</item> + + <item name="colorControlNormal">?attr/textColorSecondary</item> + <item name="colorControlActivated">?attr/colorPrimaryDark</item> + <item name="colorButtonNormal">@color/quantum_grey_700</item> + <item name="colorButtonPressed">@color/quantum_grey_500</item> <!-- TODO: Remove these attrs and move into button style. --> - <item name="colorButtonNormalColored">@color/theme_color_500</item> - <item name="colorButtonPressedColored">@color/theme_color_300</item> + <item name="colorButtonNormalColored">?attr/colorPrimary</item> + <item name="colorButtonPressedColored">?attr/colorPrimaryDark</item> </style> <!-- Quantum Paper theme (light version). --> @@ -660,7 +665,7 @@ please see themes_device_defaults.xml. <item name="timePickerStyle">@style/Widget.Quantum.Light.TimePicker</item> <!-- TimePicker Header background color --> - <item name="timePickerHeaderBackgroundColor">@color/timepicker_default_background_quantum_light</item> + <item name="timePickerHeaderBackgroundColor">?attr/colorBackground</item> <!-- TimePicker Header time label text appearance --> <item name="timePickerHeaderTimeLabelTextAppearance">@style/TextAppearance.Quantum.Light.TimePicker.TimeLabel</item> @@ -681,20 +686,25 @@ please see themes_device_defaults.xml. <item name="fastScrollOverlayPosition">atThumb</item> <!-- Color palette --> - <item name="colorControlNormal">@color/control_normal_foreground_quantum_light</item> - <item name="colorControlActivated">@color/control_activated_foreground_quantum_light</item> - <item name="colorButtonNormal">@color/btn_default_normal_quantum_light</item> - <item name="colorButtonPressed">@color/btn_default_pressed_quantum_light</item> + <item name="colorPrimaryDark">@color/quantum_grey_700</item> + <item name="colorPrimary">@color/quantum_grey_500</item> + <item name="colorPrimaryLight">@color/quantum_grey_100</item> + <item name="colorAccent">@color/quantum_teal_A200</item> + + <item name="colorControlNormal">?attr/textColorSecondary</item> + <item name="colorControlActivated">?attr/colorPrimaryLight</item> + <item name="colorButtonNormal">@color/quantum_grey_100</item> + <item name="colorButtonPressed">@color/quantum_grey_500</item> <!-- TODO: Remove these attrs and move into button style. --> - <item name="colorButtonNormalColored">@color/theme_color_500</item> - <item name="colorButtonPressedColored">@color/theme_color_700</item> + <item name="colorButtonNormalColored">?attr/colorPrimary</item> + <item name="colorButtonPressedColored">?attr/colorPrimaryLight</item> </style> <!-- Variant of the quantum (light) theme that has a solid (opaque) action bar with an inverse color profile. The dark action bar sharply stands out against the light content. --> <style name="Theme.Quantum.Light.DarkActionBar"> - <item name="actionBarWidgetTheme">@style/Theme.Quantum</item> + <item name="actionBarTheme">@style/Theme.Quantum</item> </style> <!-- Variant of the quantum (dark) theme with no action bar. --> diff --git a/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/downloadmanagertests/DownloadManagerBaseTest.java b/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/downloadmanagertests/DownloadManagerBaseTest.java index fc2897f2418f..f4bab43e4c52 100644 --- a/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/downloadmanagertests/DownloadManagerBaseTest.java +++ b/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/downloadmanagertests/DownloadManagerBaseTest.java @@ -466,15 +466,16 @@ public class DownloadManagerBaseTest extends InstrumentationTestCase { * bytes downloaded so far. * * @param id DownloadManager download id that needs to be checked. + * @param bytesToReceive how many bytes do we need to wait to receive. * @throws Exception if timed out while waiting for the file to grow in size. */ - protected void waitToReceiveData(long id) throws Exception { + protected void waitToReceiveData(long id, long bytesToReceive) throws Exception { int currentWaitTime = 0; - long originalSize = getBytesDownloaded(id); + long expectedSize = getBytesDownloaded(id) + bytesToReceive; long currentSize = 0; - while ((currentSize = getBytesDownloaded(id)) <= originalSize) { - Log.i(LOG_TAG, String.format("orig: %d, cur: %d. Waiting for file to be written to...", - originalSize, currentSize)); + while ((currentSize = getBytesDownloaded(id)) <= expectedSize) { + Log.i(LOG_TAG, String.format("expect: %d, cur: %d. Waiting for file to be written to...", + expectedSize, currentSize)); currentWaitTime = timeoutWait(currentWaitTime, WAIT_FOR_DOWNLOAD_POLL_TIME, MAX_WAIT_FOR_DOWNLOAD_TIME, "Timed out waiting for file to be written to."); } diff --git a/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/downloadmanagertests/DownloadManagerTestApp.java b/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/downloadmanagertests/DownloadManagerTestApp.java index ef48a18b0bf8..bcf2e45d5bb2 100644 --- a/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/downloadmanagertests/DownloadManagerTestApp.java +++ b/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/downloadmanagertests/DownloadManagerTestApp.java @@ -40,6 +40,9 @@ public class DownloadManagerTestApp extends DownloadManagerBaseTest { protected static final String DOWNLOAD_FILENAME = "External93mb.apk"; protected static final long DOWNLOAD_FILESIZE = 95251708; + // Wait until download manager actually start downloading something + // Will wait for 1 MB to be downloaded. + private static final long EXPECTED_PROGRESS = 1024 * 1024; private static final String FILE_CONCURRENT_DOWNLOAD_FILE_PREFIX = "file"; private static final String FILE_CONCURRENT_DOWNLOAD_FILE_EXTENSION = ".bin"; @@ -284,7 +287,7 @@ public class DownloadManagerTestApp extends DownloadManagerBaseTest { dlRequest = mDownloadManager.enqueue(request); waitForDownloadToStart(dlRequest); // make sure we're starting to download some data... - waitToReceiveData(dlRequest); + waitToReceiveData(dlRequest, EXPECTED_PROGRESS); // download disable setWiFiStateOn(false); @@ -292,27 +295,29 @@ public class DownloadManagerTestApp extends DownloadManagerBaseTest { // download disable Log.i(LOG_TAG, "Turning on airplane mode..."); setAirplaneModeOn(true); - Thread.sleep(30 * 1000); // wait 30 secs + Thread.sleep(5 * 1000); // wait 5 secs // download disable setWiFiStateOn(true); - Thread.sleep(30 * 1000); // wait 30 secs + Thread.sleep(5 * 1000); // wait 5 secs + waitToReceiveData(dlRequest, EXPECTED_PROGRESS); // download enable Log.i(LOG_TAG, "Turning off airplane mode..."); setAirplaneModeOn(false); Thread.sleep(5 * 1000); // wait 5 seconds + waitToReceiveData(dlRequest, EXPECTED_PROGRESS); // download disable Log.i(LOG_TAG, "Turning off WiFi..."); setWiFiStateOn(false); - Thread.sleep(30 * 1000); // wait 30 secs + Thread.sleep(5 * 1000); // wait 5 secs // finally, turn WiFi back on and finish up the download Log.i(LOG_TAG, "Turning on WiFi..."); setWiFiStateOn(true); - Log.i(LOG_TAG, "Waiting up to 3 minutes for download to complete..."); - assertTrue(waitForDownload(dlRequest, 3 * 60 * 1000)); + Log.i(LOG_TAG, "Waiting up to 10 minutes for download to complete..."); + assertTrue(waitForDownload(dlRequest, 10 * 60 * 1000)); ParcelFileDescriptor pfd = mDownloadManager.openDownloadedFile(dlRequest); verifyFileSize(pfd, filesize); } finally { @@ -358,7 +363,7 @@ public class DownloadManagerTestApp extends DownloadManagerBaseTest { dlRequest = mDownloadManager.enqueue(request); waitForDownloadToStart(dlRequest); // are we making any progress? - waitToReceiveData(dlRequest); + waitToReceiveData(dlRequest, EXPECTED_PROGRESS); // download disable Log.i(LOG_TAG, "Turning off WiFi..."); @@ -368,7 +373,7 @@ public class DownloadManagerTestApp extends DownloadManagerBaseTest { // enable download... Log.i(LOG_TAG, "Turning on WiFi again..."); setWiFiStateOn(true); - waitToReceiveData(dlRequest); + waitToReceiveData(dlRequest, EXPECTED_PROGRESS); // download disable Log.i(LOG_TAG, "Turning off WiFi..."); @@ -379,8 +384,8 @@ public class DownloadManagerTestApp extends DownloadManagerBaseTest { Log.i(LOG_TAG, "Turning on WiFi again..."); setWiFiStateOn(true); - Log.i(LOG_TAG, "Waiting up to 3 minutes for download to complete..."); - assertTrue(waitForDownload(dlRequest, 3 * 60 * 1000)); + Log.i(LOG_TAG, "Waiting up to 10 minutes for download to complete..."); + assertTrue(waitForDownload(dlRequest, 10 * 60 * 1000)); ParcelFileDescriptor pfd = mDownloadManager.openDownloadedFile(dlRequest); verifyFileSize(pfd, filesize); } finally { @@ -428,7 +433,7 @@ public class DownloadManagerTestApp extends DownloadManagerBaseTest { dlRequest = mDownloadManager.enqueue(request); waitForDownloadToStart(dlRequest); // are we making any progress? - waitToReceiveData(dlRequest); + waitToReceiveData(dlRequest, EXPECTED_PROGRESS); // download disable Log.i(LOG_TAG, "Turning on Airplane mode..."); @@ -439,7 +444,7 @@ public class DownloadManagerTestApp extends DownloadManagerBaseTest { Log.i(LOG_TAG, "Turning off Airplane mode..."); setAirplaneModeOn(false); // make sure we're starting to download some data... - waitToReceiveData(dlRequest); + waitToReceiveData(dlRequest, EXPECTED_PROGRESS); // reenable the connection to start up the download again Log.i(LOG_TAG, "Turning on Airplane mode again..."); @@ -450,8 +455,8 @@ public class DownloadManagerTestApp extends DownloadManagerBaseTest { Log.i(LOG_TAG, "Turning off Airplane mode again..."); setAirplaneModeOn(false); - Log.i(LOG_TAG, "Waiting up to 3 minutes for donwload to complete..."); - assertTrue(waitForDownload(dlRequest, 180 * 1000)); // wait up to 3 mins before timeout + Log.i(LOG_TAG, "Waiting up to 10 minutes for donwload to complete..."); + assertTrue(waitForDownload(dlRequest, 10 * 60 * 1000)); // wait up to 10 mins ParcelFileDescriptor pfd = mDownloadManager.openDownloadedFile(dlRequest); verifyFileSize(pfd, filesize); } finally { diff --git a/docs/html/guide/topics/resources/providing-resources.jd b/docs/html/guide/topics/resources/providing-resources.jd index aec7fa70f7b0..bf16630a13db 100644 --- a/docs/html/guide/topics/resources/providing-resources.jd +++ b/docs/html/guide/topics/resources/providing-resources.jd @@ -562,6 +562,7 @@ which indicates the current device orientation.</p> <code>desk</code><br/> <code>television<br/> <code>appliance</code> + <code>watch</code> </td> <td> <ul class="nolist"> @@ -573,8 +574,9 @@ which indicates the current device orientation.</p> non-pointer interaction</li> <li>{@code appliance}: Device is serving as an appliance, with no display</li> + <li>{@code watch}: Device has a display and is worn on the wrist</li> </ul> - <p><em>Added in API level 8, television added in API 13.</em></p> + <p><em>Added in API level 8, television added in API 13, watch added in API 20.</em></p> <p>For information about how your app can respond when the device is inserted into or removed from a dock, read <a href="{@docRoot}training/monitoring-device-state/docking-monitoring.html">Determining diff --git a/docs/html/training/basics/actionbar/styling.jd b/docs/html/training/basics/actionbar/styling.jd index 1f76e03f31e1..4128a97952d6 100644 --- a/docs/html/training/basics/actionbar/styling.jd +++ b/docs/html/training/basics/actionbar/styling.jd @@ -144,13 +144,13 @@ background like this:</p> <resources> <!-- the theme applied to the application or activity --> <style name="CustomActionBarTheme" - parent="@style/Theme.Holo.Light.DarkActionBar"> + parent="@android:style/Theme.Holo.Light.DarkActionBar"> <item name="android:actionBarStyle">@style/MyActionBar</item> </style> <!-- ActionBar styles --> <style name="MyActionBar" - parent="@style/Widget.Holo.Light.ActionBar.Solid.Inverse"> + parent="@android:style/Widget.Holo.Light.ActionBar.Solid.Inverse"> <item name="android:background">@drawable/actionbar_background</item> </style> </resources> diff --git a/docs/html/training/game-controllers/controller-input.jd b/docs/html/training/game-controllers/controller-input.jd index 2c50ae1a67fb..c9517ba1dbd7 100644 --- a/docs/html/training/game-controllers/controller-input.jd +++ b/docs/html/training/game-controllers/controller-input.jd @@ -236,26 +236,33 @@ buttons.</p> </tr> <tr> <td>Start game in main menu, or pause/unpause during game</td> - <td>{@link android.view.KeyEvent#KEYCODE_BUTTON_START BUTTON_START}</td> + <td>{@link android.view.KeyEvent#KEYCODE_BUTTON_START BUTTON_START}<sup>*</sup></td> </tr> <tr> <td>Display menu</td> - <td>{@link android.view.KeyEvent#KEYCODE_BUTTON_SELECT BUTTON_SELECT} and -{@link android.view.KeyEvent#KEYCODE_MENU}</td> + <td>{@link android.view.KeyEvent#KEYCODE_BUTTON_SELECT BUTTON_SELECT}<sup>*</sup> + and {@link android.view.KeyEvent#KEYCODE_MENU}<sup>*</sup></td> </tr> <tr> <td>Same as Android <em>Back</em></td> - <td>{@link android.view.KeyEvent#KEYCODE_BUTTON_B BUTTON_B}<sup>*</sup> and -{@link android.view.KeyEvent#KEYCODE_BACK KEYCODE_BACK}</td> + <td>{@link android.view.KeyEvent#KEYCODE_BACK KEYCODE_BACK}</td> + </tr> + <tr> + <td>Navigate back to a previous item in a menu</td> + <td>{@link android.view.KeyEvent#KEYCODE_BUTTON_B BUTTON_B}<sup>**</sup></td> </tr> <tr> <td>Confirm selection, or perform primary game action</td> - <td>{@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A}<sup>*</sup> and + <td>{@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A}<sup>**</sup> and {@link android.view.KeyEvent#KEYCODE_DPAD_CENTER DPAD_CENTER}</td> </tr> </table> <p> -<em>* This could be the opposite button (A/B), depending on the locale that +<em>* Your game should not rely on the presence of the Start, Select, or Menu + buttons.</em> +</p> +<p> +<em>** This could be the opposite button (A/B), depending on the locale that you are supporting.</em> </p> diff --git a/docs/html/training/notify-user/display-progress.jd b/docs/html/training/notify-user/display-progress.jd index 2b2b3ae7b75d..c00576cad393 100644 --- a/docs/html/training/notify-user/display-progress.jd +++ b/docs/html/training/notify-user/display-progress.jd @@ -80,6 +80,7 @@ previous.link=expanded.html setProgress(0, 0, false)}. For example: </p> <pre> +int id = 1; ... mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); @@ -100,7 +101,7 @@ new Thread( // state mBuilder.setProgress(100, incr, false); // Displays the progress bar for the first time. - mNotifyManager.notify(0, mBuilder.build()); + mNotifyManager.notify(id, mBuilder.build()); // Sleeps the thread, simulating an operation // that takes time try { @@ -114,7 +115,7 @@ new Thread( mBuilder.setContentText("Download complete") // Removes the progress bar .setProgress(0,0,false); - mNotifyManager.notify(ID, mBuilder.build()); + mNotifyManager.notify(id, mBuilder.build()); } } // Starts the thread by calling the run() method in its Runnable @@ -157,7 +158,7 @@ new Thread( // percentage, and "determinate" state mBuilder.setProgress(100, incr, false); // Issues the notification -mNotifyManager.notify(0, mBuilder.build()); +mNotifyManager.notify(id, mBuilder.build()); </pre> <p> Replace the lines you've found with the following lines. Notice that the third parameter @@ -169,7 +170,7 @@ mNotifyManager.notify(0, mBuilder.build()); // Sets an activity indicator for an operation of indeterminate length mBuilder.setProgress(0, 0, true); // Issues the notification -mNotifyManager.notify(0, mBuilder.build()); +mNotifyManager.notify(id, mBuilder.build()); </pre> <p> The resulting indicator is shown in figure 2: diff --git a/docs/html/training/notify-user/navigation.jd b/docs/html/training/notify-user/navigation.jd index ac4689aa1286..fc95013145ac 100644 --- a/docs/html/training/notify-user/navigation.jd +++ b/docs/html/training/notify-user/navigation.jd @@ -95,6 +95,7 @@ next.link=managing.html {@link android.app.Activity}. For example: </p> <pre> +int id = 1; ... Intent resultIntent = new Intent(this, ResultActivity.class); TaskStackBuilder stackBuilder = TaskStackBuilder.create(this); diff --git a/graphics/java/android/graphics/Matrix.java b/graphics/java/android/graphics/Matrix.java index c8bcf2680d9a..66bf75c4fdac 100644 --- a/graphics/java/android/graphics/Matrix.java +++ b/graphics/java/android/graphics/Matrix.java @@ -352,13 +352,19 @@ public class Matrix { } /** - * Set the matrix to the concatenation of the two specified matrices, - * returning true if the the result can be represented. Either of the two - * matrices may also be the target matrix. this = a * b + * Set the matrix to the concatenation of the two specified matrices and + * return true. + * + * <p>Either of the two matrices may also be the target matrix, that is + * <code>matrixA.setConcat(matrixA, matrixB);</code> is valid.</p> + * + * <p class="note">In {@link android.os.Build.VERSION_CODES#GINGERBREAD_MR1} and below, this + * function returns true only if the result can be represented. In + * {@link android.os.Build.VERSION_CODES#HONEYCOMB} and above, it always returns true.</p> */ public boolean setConcat(Matrix a, Matrix b) { - return native_setConcat(native_instance, a.native_instance, - b.native_instance); + native_setConcat(native_instance, a.native_instance, b.native_instance); + return true; } /** @@ -366,7 +372,8 @@ public class Matrix { * M' = M * T(dx, dy) */ public boolean preTranslate(float dx, float dy) { - return native_preTranslate(native_instance, dx, dy); + native_preTranslate(native_instance, dx, dy); + return true; } /** @@ -374,7 +381,8 @@ public class Matrix { * M' = M * S(sx, sy, px, py) */ public boolean preScale(float sx, float sy, float px, float py) { - return native_preScale(native_instance, sx, sy, px, py); + native_preScale(native_instance, sx, sy, px, py); + return true; } /** @@ -382,7 +390,8 @@ public class Matrix { * M' = M * S(sx, sy) */ public boolean preScale(float sx, float sy) { - return native_preScale(native_instance, sx, sy); + native_preScale(native_instance, sx, sy); + return true; } /** @@ -390,7 +399,8 @@ public class Matrix { * M' = M * R(degrees, px, py) */ public boolean preRotate(float degrees, float px, float py) { - return native_preRotate(native_instance, degrees, px, py); + native_preRotate(native_instance, degrees, px, py); + return true; } /** @@ -398,7 +408,8 @@ public class Matrix { * M' = M * R(degrees) */ public boolean preRotate(float degrees) { - return native_preRotate(native_instance, degrees); + native_preRotate(native_instance, degrees); + return true; } /** @@ -406,7 +417,8 @@ public class Matrix { * M' = M * K(kx, ky, px, py) */ public boolean preSkew(float kx, float ky, float px, float py) { - return native_preSkew(native_instance, kx, ky, px, py); + native_preSkew(native_instance, kx, ky, px, py); + return true; } /** @@ -414,7 +426,8 @@ public class Matrix { * M' = M * K(kx, ky) */ public boolean preSkew(float kx, float ky) { - return native_preSkew(native_instance, kx, ky); + native_preSkew(native_instance, kx, ky); + return true; } /** @@ -422,7 +435,8 @@ public class Matrix { * M' = M * other */ public boolean preConcat(Matrix other) { - return native_preConcat(native_instance, other.native_instance); + native_preConcat(native_instance, other.native_instance); + return true; } /** @@ -430,7 +444,8 @@ public class Matrix { * M' = T(dx, dy) * M */ public boolean postTranslate(float dx, float dy) { - return native_postTranslate(native_instance, dx, dy); + native_postTranslate(native_instance, dx, dy); + return true; } /** @@ -438,7 +453,8 @@ public class Matrix { * M' = S(sx, sy, px, py) * M */ public boolean postScale(float sx, float sy, float px, float py) { - return native_postScale(native_instance, sx, sy, px, py); + native_postScale(native_instance, sx, sy, px, py); + return true; } /** @@ -446,7 +462,8 @@ public class Matrix { * M' = S(sx, sy) * M */ public boolean postScale(float sx, float sy) { - return native_postScale(native_instance, sx, sy); + native_postScale(native_instance, sx, sy); + return true; } /** @@ -454,7 +471,8 @@ public class Matrix { * M' = R(degrees, px, py) * M */ public boolean postRotate(float degrees, float px, float py) { - return native_postRotate(native_instance, degrees, px, py); + native_postRotate(native_instance, degrees, px, py); + return true; } /** @@ -462,7 +480,8 @@ public class Matrix { * M' = R(degrees) * M */ public boolean postRotate(float degrees) { - return native_postRotate(native_instance, degrees); + native_postRotate(native_instance, degrees); + return true; } /** @@ -470,7 +489,8 @@ public class Matrix { * M' = K(kx, ky, px, py) * M */ public boolean postSkew(float kx, float ky, float px, float py) { - return native_postSkew(native_instance, kx, ky, px, py); + native_postSkew(native_instance, kx, ky, px, py); + return true; } /** @@ -478,7 +498,8 @@ public class Matrix { * M' = K(kx, ky) * M */ public boolean postSkew(float kx, float ky) { - return native_postSkew(native_instance, kx, ky); + native_postSkew(native_instance, kx, ky); + return true; } /** @@ -486,7 +507,8 @@ public class Matrix { * M' = other * M */ public boolean postConcat(Matrix other) { - return native_postConcat(native_instance, other.native_instance); + native_postConcat(native_instance, other.native_instance); + return true; } /** Controlls how the src rect should align into the dst rect for @@ -828,41 +850,41 @@ public class Matrix { float kx, float ky, float px, float py); private static native void native_setSkew(long native_object, float kx, float ky); - private static native boolean native_setConcat(long native_object, - long native_a, - long native_b); - private static native boolean native_preTranslate(long native_object, - float dx, float dy); - private static native boolean native_preScale(long native_object, - float sx, float sy, float px, float py); - private static native boolean native_preScale(long native_object, - float sx, float sy); - private static native boolean native_preRotate(long native_object, - float degrees, float px, float py); - private static native boolean native_preRotate(long native_object, - float degrees); - private static native boolean native_preSkew(long native_object, - float kx, float ky, float px, float py); - private static native boolean native_preSkew(long native_object, - float kx, float ky); - private static native boolean native_preConcat(long native_object, - long native_other_matrix); - private static native boolean native_postTranslate(long native_object, - float dx, float dy); - private static native boolean native_postScale(long native_object, - float sx, float sy, float px, float py); - private static native boolean native_postScale(long native_object, - float sx, float sy); - private static native boolean native_postRotate(long native_object, - float degrees, float px, float py); - private static native boolean native_postRotate(long native_object, - float degrees); - private static native boolean native_postSkew(long native_object, - float kx, float ky, float px, float py); - private static native boolean native_postSkew(long native_object, - float kx, float ky); - private static native boolean native_postConcat(long native_object, - long native_other_matrix); + private static native void native_setConcat(long native_object, + long native_a, + long native_b); + private static native void native_preTranslate(long native_object, + float dx, float dy); + private static native void native_preScale(long native_object, + float sx, float sy, float px, float py); + private static native void native_preScale(long native_object, + float sx, float sy); + private static native void native_preRotate(long native_object, + float degrees, float px, float py); + private static native void native_preRotate(long native_object, + float degrees); + private static native void native_preSkew(long native_object, + float kx, float ky, float px, float py); + private static native void native_preSkew(long native_object, + float kx, float ky); + private static native void native_preConcat(long native_object, + long native_other_matrix); + private static native void native_postTranslate(long native_object, + float dx, float dy); + private static native void native_postScale(long native_object, + float sx, float sy, float px, float py); + private static native void native_postScale(long native_object, + float sx, float sy); + private static native void native_postRotate(long native_object, + float degrees, float px, float py); + private static native void native_postRotate(long native_object, + float degrees); + private static native void native_postSkew(long native_object, + float kx, float ky, float px, float py); + private static native void native_postSkew(long native_object, + float kx, float ky); + private static native void native_postConcat(long native_object, + long native_other_matrix); private static native boolean native_setRectToRect(long native_object, RectF src, RectF dst, int stf); private static native boolean native_setPolyToPoly(long native_object, diff --git a/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java b/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java index 64de95fabc20..16415114cc3b 100644 --- a/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java +++ b/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java @@ -485,7 +485,10 @@ public class TouchFeedbackDrawable extends LayerDrawable { } else if (state == null) { ns = new TouchFeedbackState(null, this, res); } else { - ns = state; + // We always need a new state since child drawables contain local + // state but live within the parent's constant state. + // TODO: Move child drawables into local state. + ns = new TouchFeedbackState(state, this, res); } if (res != null) { diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java index 33683ab9f82c..d6f75ba8f9c4 100644 --- a/graphics/java/android/graphics/drawable/VectorDrawable.java +++ b/graphics/java/android/graphics/drawable/VectorDrawable.java @@ -14,7 +14,6 @@ package android.graphics.drawable; -import android.animation.ArgbEvaluator; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.content.res.Resources; @@ -28,11 +27,7 @@ import android.graphics.Path; import android.graphics.PathMeasure; import android.graphics.PixelFormat; import android.graphics.Rect; -import android.graphics.RectF; import android.graphics.Region; -import android.location.Address; -import android.net.ParseException; -import android.sax.StartElementListener; import android.util.AttributeSet; import android.util.Log; import android.util.Xml; @@ -47,7 +42,6 @@ import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserFactory; import java.io.IOException; -import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -157,8 +151,8 @@ import java.util.HashSet; * </dd> */ public class VectorDrawable extends Drawable { - private static final String LOGTAG = "VectorDrawable"; - public static final int INFINITE = ValueAnimator.INFINITE; + private static final String LOGTAG = VectorDrawable.class.getSimpleName(); + private static final String SHAPE_SIZE = "size"; private static final String SHAPE_VIEWPORT = "viewport"; private static final String SHAPE_GROUP = "group"; @@ -170,23 +164,32 @@ public class VectorDrawable extends Drawable { private static final int LINECAP_BUTT = 0; private static final int LINECAP_ROUND = 1; private static final int LINECAP_SQUARE = 2; + private static final int LINEJOIN_MITER = 0; private static final int LINEJOIN_ROUND = 1; private static final int LINEJOIN_BEVEL = 2; + private static final int DEFAULT_DURATION = 1000; private static final long DEFAULT_INFINITE_DURATION = 60 * 60 * 1000; + private VectorDrawableState mVectorState; private int mAlpha = 0xFF; public VectorDrawable() { - mVectorState = new VectorDrawableState(); + mVectorState = new VectorDrawableState(null); mVectorState.mBasicAnimator = ObjectAnimator.ofFloat(this, "AnimationFraction", 0, 1); + setDuration(DEFAULT_DURATION); } - private VectorDrawable(VectorDrawableState state) { + private VectorDrawable(VectorDrawableState state, Resources res, Theme theme) { mVectorState = new VectorDrawableState(state); mVectorState.mBasicAnimator = ObjectAnimator.ofFloat(this, "AnimationFraction", 0, 1); + + if (theme != null && canApplyTheme()) { + applyTheme(theme); + } + long duration = mVectorState.mVAnimatedPath.getTotalAnimationDuration(); if (duration == -1) { // if it set to infinite set to 1 hour duration = DEFAULT_INFINITE_DURATION; // TODO define correct approach for infinite @@ -197,6 +200,7 @@ public class VectorDrawable extends Drawable { } final static class VectorDrawableState extends ConstantState { + int[] mThemeAttrs; int mChangingConfigurations; ValueAnimator mBasicAnimator; VAnimatedPath mVAnimatedPath = new VAnimatedPath(); @@ -204,20 +208,29 @@ public class VectorDrawable extends Drawable { int mIntrinsicHeight; int mIntrinsicWidth; - public VectorDrawableState(){ + public VectorDrawableState(VectorDrawableState copy) { + if (copy != null) { + mChangingConfigurations = copy.mChangingConfigurations; + mVAnimatedPath = new VAnimatedPath(copy.mVAnimatedPath); + mPadding = new Rect(copy.mPadding); + mIntrinsicHeight = copy.mIntrinsicHeight; + mIntrinsicWidth = copy.mIntrinsicWidth; + } } - public VectorDrawableState(VectorDrawableState copy){ - mChangingConfigurations = copy.mChangingConfigurations; - mVAnimatedPath = new VAnimatedPath(copy.mVAnimatedPath); - mPadding = new Rect(copy.mPadding); - mIntrinsicHeight = copy.mIntrinsicHeight; - mIntrinsicWidth = copy.mIntrinsicWidth; + @Override + public Drawable newDrawable() { + return new VectorDrawable(this, null, null); } @Override - public Drawable newDrawable() { - return new VectorDrawable(this); + public Drawable newDrawable(Resources res) { + return new VectorDrawable(this, res, null); + } + + @Override + public Drawable newDrawable(Resources res, Theme theme) { + return new VectorDrawable(this, res, theme); } @Override @@ -459,29 +472,49 @@ public class VectorDrawable extends Drawable { @Override public void inflate(Resources res, XmlPullParser parser, AttributeSet attrs, Theme theme) throws XmlPullParserException, IOException { - setAnimatedPath(inflateInternal(res, parser, attrs)); + final VAnimatedPath p = inflateInternal(res, parser, attrs, theme); + setAnimatedPath(p); } - private VAnimatedPath inflateInternal(Resources res, XmlPullParser parser, AttributeSet attrs) - throws XmlPullParserException, IOException { + @Override + public boolean canApplyTheme() { + return super.canApplyTheme() || mVectorState != null && mVectorState.canApplyTheme(); + } + + @Override + public void applyTheme(Theme t) { + super.applyTheme(t); + + final VectorDrawableState state = mVectorState; + final VAnimatedPath path = state.mVAnimatedPath; + if (path != null && path.canApplyTheme()) { + path.applyTheme(t); + } + } + + private VAnimatedPath inflateInternal(Resources res, XmlPullParser parser, AttributeSet attrs, + Theme theme) throws XmlPullParserException, IOException { + final VAnimatedPath animatedPath = new VAnimatedPath(); + boolean noSizeTag = true; boolean noViewportTag = true; boolean noGroupTag = true; boolean noPathTag = true; - final VAnimatedPath animatedPath = new VAnimatedPath(); - VectorDrawable.VGroup currentGroup = null; + + VGroup currentGroup = null; int eventType = parser.getEventType(); while (eventType != XmlPullParser.END_DOCUMENT) { if (eventType == XmlPullParser.START_TAG) { - String tagName = parser.getName(); + final String tagName = parser.getName(); if (SHAPE_PATH.equals(tagName)) { - VectorDrawable.VPath p = new VPath(res, attrs); - currentGroup.add(p); + final VPath path = new VPath(); + path.inflate(res, attrs, theme); + currentGroup.add(path); noPathTag = false; } else if (SHAPE_ANIMATION.equals(tagName)) { - VectorDrawable.VAnimation anim = - new VAnimation(res, attrs, animatedPath.mGroupList); + final VAnimation anim = new VAnimation(); + anim.inflate(animatedPath.mGroupList, res, attrs, theme); animatedPath.addAnimation(anim); } else if (SHAPE_SIZE.equals(tagName)) { animatedPath.parseSize(res, attrs); @@ -490,17 +523,17 @@ public class VectorDrawable extends Drawable { animatedPath.parseViewport(res, attrs); noViewportTag = false; } else if (SHAPE_GROUP.equals(tagName)) { - currentGroup = new VectorDrawable.VGroup(); + currentGroup = new VGroup(); animatedPath.mGroupList.add(currentGroup); noGroupTag = false; } else if (SHAPE_VECTOR.equals(tagName)) { - TypedArray a = res.obtainAttributes(attrs, R.styleable.VectorDrawable); + final TypedArray a = res.obtainAttributes(attrs, R.styleable.VectorDrawable); animatedPath.setTrigger(a.getInteger(R.styleable.VectorDrawable_trigger, 0)); // Parsing the version information. // Right now, we only support version "1". // If the xml didn't specify the version number, the default version is "1". - int versionCode = a.getInt(R.styleable.VectorDrawable_versionCode, 1); + final int versionCode = a.getInt(R.styleable.VectorDrawable_versionCode, 1); if (versionCode != 1) { throw new IllegalArgumentException( "So far, VectorDrawable only support version 1"); @@ -509,33 +542,41 @@ public class VectorDrawable extends Drawable { a.recycle(); } } + eventType = parser.next(); } + if (noSizeTag || noViewportTag || noGroupTag || noPathTag) { - StringBuffer tag = new StringBuffer(); + final StringBuffer tag = new StringBuffer(); + if (noSizeTag) { - tag.append("size"); + tag.append(SHAPE_SIZE); } + if (noViewportTag){ if (tag.length()>0) { tag.append(" & "); } - tag.append("size"); + tag.append(SHAPE_SIZE); } + if (noGroupTag){ if (tag.length()>0) { tag.append(" & "); } - tag.append("group"); + tag.append(SHAPE_GROUP); } + if (noPathTag){ if (tag.length()>0) { tag.append(" or "); } - tag.append("path"); + tag.append(SHAPE_PATH); } - throw new XmlPullParserException("no "+tag+" defined"); + + throw new XmlPullParserException("no " + tag + " defined"); } + // post parse cleanup animatedPath.parseFinish(); return animatedPath; @@ -543,16 +584,18 @@ public class VectorDrawable extends Drawable { private void setAnimatedPath(VAnimatedPath animatedPath) { mVectorState.mVAnimatedPath = animatedPath; + setIntrinsicWidth((int) mVectorState.mVAnimatedPath.mBaseWidth); setIntrinsicHeight((int) mVectorState.mVAnimatedPath.mBaseHeight); + long duration = mVectorState.mVAnimatedPath.getTotalAnimationDuration(); if (duration == -1) { // if it set to infinite set to 1 hour duration = DEFAULT_INFINITE_DURATION; // TODO define correct approach for infinite mVectorState.mBasicAnimator.setFloatValues(0, duration / 1000); mVectorState.mBasicAnimator.setInterpolator(new LinearInterpolator()); } - setDuration(duration); + setDuration(duration); setAnimationFraction(0); } @@ -570,8 +613,6 @@ public class VectorDrawable extends Drawable { } private static class VAnimatedPath { - private static final String LOGTAG = "VAnimatedPath"; - private ArrayList<VAnimation> mCurrentAnimList = null; private VPath[] mCurrentPaths; private float mAnimationValue = 0; // value goes from 0 to 1 @@ -585,7 +626,9 @@ public class VectorDrawable extends Drawable { private int[] mCurrentState = new int[0]; private int mTrigger; private boolean mTriggerState; - ArrayList<VGroup> mGroupList = new ArrayList<VGroup>(); + + final ArrayList<VGroup> mGroupList = new ArrayList<VGroup>(); + float mBaseWidth = 1; float mBaseHeight = 1; float mViewportWidth; @@ -594,10 +637,11 @@ public class VectorDrawable extends Drawable { public VAnimatedPath() { setup(); } + public VAnimatedPath(VAnimatedPath copy) { setup(); mCurrentAnimList = new ArrayList<VAnimation>(copy.mCurrentAnimList); - mGroupList = new ArrayList<VGroup>(copy.mGroupList); + mGroupList.addAll(copy.mGroupList); if (copy.mCurrentPaths != null) { mCurrentPaths = new VPath[copy.mCurrentPaths.length]; for (int i = 0; i < mCurrentPaths.length; i++) { @@ -615,8 +659,53 @@ public class VectorDrawable extends Drawable { mCurrentState = new int[0]; } + public boolean canApplyTheme() { + final ArrayList<VGroup> groups = mGroupList; + for (int i = groups.size() - 1; i >= 0; i--) { + final ArrayList<VPath> paths = groups.get(i).mVGList; + for (int j = paths.size() - 1; j >= 0; j--) { + final VPath path = paths.get(j); + if (path.canApplyTheme()) { + return true; + } + } + } + + final ArrayList<VAnimation> anims = mCurrentAnimList; + for (int i = anims.size() - 1; i >= 0; i--) { + final VAnimation anim = anims.get(i); + if (anim.canApplyTheme()) { + return true; + } + } + + return false; + } + + public void applyTheme(Theme t) { + final ArrayList<VGroup> groups = mGroupList; + for (int i = groups.size() - 1; i >= 0; i--) { + final ArrayList<VPath> paths = groups.get(i).mVGList; + for (int j = paths.size() - 1; j >= 0; j--) { + final VPath path = paths.get(j); + if (path.canApplyTheme()) { + path.applyTheme(t); + } + } + } + + final ArrayList<VAnimation> anims = mCurrentAnimList; + for (int i = anims.size() - 1; i >= 0; i--) { + final VAnimation anim = anims.get(i); + if (anim.canApplyTheme()) { + anim.applyTheme(t); + } + } + } + public void setTrigger(int trigger){ - int []lut = { 0, + final int [] lut = { + 0, R.attr.state_pressed, R.attr.state_focused, R.attr.state_hovered, @@ -626,6 +715,7 @@ public class VectorDrawable extends Drawable { R.attr.state_activated, R.attr.state_focused }; + mTrigger = lut[trigger]; } @@ -663,23 +753,27 @@ public class VectorDrawable extends Drawable { * @return true if you need to keep repeating */ public boolean setAnimationFraction(float value) { - int len = mCurrentPaths.length; getTotalAnimationDuration(); + long animationTime = (long) (value * mTotalDuration); + final int len = mCurrentPaths.length; for (int i = 0; i < len; i++) { - VPath path = mCurrentPaths[i]; animationTime = (long) ((mTotalDuration == -1) ? value * 1000 : mTotalDuration * value); - int size = mCurrentAnimList.size(); + + final VPath path = mCurrentPaths[i]; + final int size = mCurrentAnimList.size(); for (int j = 0; j < size; j++) { - VAnimation vAnimation = mCurrentAnimList.get(j); + final VAnimation vAnimation = mCurrentAnimList.get(j); if (vAnimation.doesAdjustPath(path)) { mCurrentPaths[i] = vAnimation.getPathAtTime(animationTime, path); } } } - this.mAnimationValue = value; + + mAnimationValue = value; + if (mTotalDuration == -1) { return true; } else { @@ -688,16 +782,15 @@ public class VectorDrawable extends Drawable { } public void draw(Canvas canvas) { - int w = canvas.getWidth(); - int h = canvas.getHeight(); - float scale = w / mViewportWidth; - scale = Math.min(h / mViewportHeight, scale); - if (mCurrentPaths == null) { Log.e(LOGTAG,"mCurrentPaths == null"); return; } + // TODO: This should probably use getBounds(). + final int w = canvas.getWidth(); + final int h = canvas.getHeight(); + for (int i = 0; i < mCurrentPaths.length; i++) { if (mCurrentPaths[i] != null && mCurrentPaths[i].isVisible(mCurrentState)) { drawPath(mCurrentPaths[i], canvas, w, h); @@ -706,8 +799,7 @@ public class VectorDrawable extends Drawable { } private void drawPath(VPath vPath, Canvas canvas, int w, int h) { - float scale = w / mViewportWidth; - scale = Math.min(h / mViewportHeight, scale); + final float scale = Math.min(h / mViewportHeight, w / mViewportWidth); vPath.toPath(mPath); Path path = mPath; @@ -746,12 +838,14 @@ public class VectorDrawable extends Drawable { if (vPath.mClip) { canvas.clipPath(mRenderPath, Region.Op.REPLACE); } + if (vPath.mFillColor != 0) { mFillPaint.setColor(vPath.mFillColor); int alpha = 0xFF & (vPath.mFillColor >> 24); mFillPaint.setAlpha(alpha); canvas.drawPath(mRenderPath, mFillPaint); } + if (vPath.mStrokeColor != 0) { if (vPath.mStrokelineJoin != null) { mStrokePaint.setStrokeJoin(vPath.mStrokelineJoin); @@ -773,7 +867,7 @@ public class VectorDrawable extends Drawable { * TODO: improve memory use & performance or move to C++ */ public void parseFinish() { - HashMap<String, VAnimation> newAnimations = new HashMap<String, VAnimation>(); + final HashMap<String, VAnimation> newAnimations = new HashMap<String, VAnimation>(); for (VGroup group : mGroupList) { for (VPath vPath : group.getPaths()) { if (!vPath.mAnimated) { @@ -784,16 +878,19 @@ public class VectorDrawable extends Drawable { } else { ap = newAnimations.get(vPath.getID()); } + ap.addPath(vPath); vPath.mAnimated = true; } } } + if (mCurrentAnimList == null) { mCurrentAnimList = new ArrayList<VectorDrawable.VAnimation>(); } mCurrentAnimList.addAll(newAnimations.values()); - Collection<VPath> paths = mGroupList.get(0).getPaths(); + + final Collection<VPath> paths = mGroupList.get(0).getPaths(); mCurrentPaths = paths.toArray(new VPath[paths.size()]); for (int i = 0; i < mCurrentPaths.length; i++) { mCurrentPaths[i] = new VPath(mCurrentPaths[i]); @@ -854,40 +951,41 @@ public class VectorDrawable extends Drawable { } private static class VAnimation { - private static final String LOGTAG = "VAnimation"; + private final static int DIRECTION_FORWARD = 0; + private final static int DIRECTION_IN_AND_OUT = 1; + private VPath[] mPaths = new VPath[0]; public enum Style { INTERPOLATE, CROSSFADE, WIPE } + Interpolator mAnimInterpolator = new AccelerateDecelerateInterpolator(); + + private int[] mThemeAttrs; private Style mStyle; private int mLimitProperty = 0; private long[] mDuration = {DEFAULT_DURATION}; private long mStartOffset; private long mRepeat = 1; - private HashSet<String>mSeqMap = new HashSet<String>(); + private HashSet<String> mSeqMap = new HashSet<String>(); private long mWipeDirection; private int mMode = 0; // forward = 0 inAndOut = 1; private int mInterpolatorType; private String mId; - private final static int DIRECTION_FORWARD = 0; - private final static int DIRECTION_IN_AND_OUT = 1; public VAnimation() { + // Empty constructor. } - public boolean doesAdjustPath(VPath path) { - return mSeqMap.contains(path.getID()); - } - - public VAnimation(Resources r, AttributeSet attrs, ArrayList<VGroup> groups) + public void inflate(ArrayList<VGroup> groups, Resources r, AttributeSet attrs, Theme theme) throws XmlPullParserException { String value; String[] sp; int name; - TypedArray a = r.obtainAttributes(attrs, R.styleable.VectorDrawableAnimation); + final TypedArray a = r.obtainAttributes(attrs, R.styleable.VectorDrawableAnimation); + mThemeAttrs = a.extractThemeAttrs(); value = a.getString(R.styleable.VectorDrawableAnimation_sequence); if (value != null) { @@ -910,28 +1008,42 @@ public class VectorDrawable extends Drawable { if (value != null) { long totalDuration = 0; sp = value.split(","); - long[] dur = new long[sp.length]; + + final long[] dur = new long[sp.length]; for (int j = 0; j < dur.length; j++) { dur[j] = Long.parseLong(sp[j]); totalDuration += dur[j]; } + if (totalDuration == 0){ throw new XmlPullParserException(a.getPositionDescription()+ "total duration must not be zero"); } + setDuration(dur); } setRepeat(a.getInt(R.styleable.VectorDrawableAnimation_repeatCount, 1)); - setStartOffset(a.getInt(R.styleable.VectorDrawableAnimation_startDelay, 0)); - setMode(a.getInt(R.styleable.VectorDrawableAnimation_repeatStyle, 0)); fixMissingParameters(); + a.recycle(); } + public boolean canApplyTheme() { + return mThemeAttrs != null; + } + + public void applyTheme(Theme t) { + // TODO: Apply theme. + } + + public boolean doesAdjustPath(VPath path) { + return mSeqMap.contains(path.getID()); + } + public String getId() { if (mId == null) { mId = mPaths[0].getID(); @@ -1031,7 +1143,7 @@ public class VectorDrawable extends Drawable { } public void setPaths(VPath[] paths) { - this.mPaths = paths; + mPaths = paths; } public void addPath(VPath path) { @@ -1049,7 +1161,7 @@ public class VectorDrawable extends Drawable { } public void interpolate(VPath p1, VPath p2, float time, VPath dest) { - dest.interpolate(time, p1, p2, dest, mLimitProperty); + VPath.interpolate(time, p1, p2, dest, mLimitProperty); } public VPath getPathAtTime(long milliseconds, VPath dest) { @@ -1150,8 +1262,8 @@ public class VectorDrawable extends Drawable { } private static class VGroup { - private HashMap<String, VPath> mVGPathMap = new HashMap<String, VPath>(); - private ArrayList<VPath> mVGList = new ArrayList<VPath>(); + private final HashMap<String, VPath> mVGPathMap = new HashMap<String, VPath>(); + private final ArrayList<VPath> mVGList = new ArrayList<VPath>(); public void add(VPath path) { String id = path.getID(); @@ -1183,34 +1295,52 @@ public class VectorDrawable extends Drawable { private static final int LIMIT_TRIM_PATH_START = 3; private static final int LIMIT_TRIM_PATH_OFFSET = 5; private static final int LIMIT_TRIM_PATH_END = 4; + private static final int STATE_UNDEFINED=0; private static final int STATE_TRUE=1; private static final int STATE_FALSE=2; + private static final int MAX_STATES = 10; - private VNode[] mNode = null; - private String mId; + + private int[] mThemeAttrs; + int mStrokeColor = 0; float mStrokeWidth = 0; float mStrokeOpacity = Float.NaN; + int mFillColor = 0; int mFillRule; float mFillOpacity = Float.NaN; + float mRotate = 0; float mPivotX = 0; float mPivotY = 0; + float mTrimPathStart = 0; float mTrimPathEnd = 1; float mTrimPathOffset = 0; + boolean mAnimated = false; boolean mClip = false; - public Paint.Cap mStrokelineCap = null; - public Paint.Join mStrokelineJoin = null; + Paint.Cap mStrokelineCap = null; + Paint.Join mStrokelineJoin = null; float mStrokeMiterlimit = 4; + + private VNode[] mNode = null; + private String mId; private int[] mCheckState = new int[MAX_STATES]; private boolean[] mCheckValue = new boolean[MAX_STATES]; private int mNumberOfStates = 0; private int mNumberOfTrue = 0; + public VPath() { + // Empty constructor. + } + + public VPath(VPath p) { + copyFrom(p); + } + public void addStateFilter(int state, boolean condition) { int k = 0; while (k < mNumberOfStates) { @@ -1228,7 +1358,7 @@ public class VectorDrawable extends Drawable { } } - int getState(int state){ + private int getState(int state){ for (int i = 0; i < mNumberOfStates; i++) { if (mCheckState[mNumberOfStates] == state){ return (mCheckValue[i])?STATE_TRUE:STATE_FALSE; @@ -1250,34 +1380,33 @@ public class VectorDrawable extends Drawable { } } - public VPath() { - mId = this.toString(); // to ensure paths have unique names - } - - public VPath(VPath p) { - copyFrom(p); - } - public String getID(){ return mId; } - public VPath(Resources r, AttributeSet attrs) { - TypedArray a = r.obtainAttributes(attrs, R.styleable.VectorDrawablePath); + public void inflate(Resources r, AttributeSet attrs, Theme theme) { + final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.VectorDrawablePath); + final int[] themeAttrs = a.extractThemeAttrs(); + mThemeAttrs = themeAttrs; + mClip = a.getBoolean(R.styleable.VectorDrawablePath_clipToPath, false); mId = a.getString(R.styleable.VectorDrawablePath_name); mNode = parsePath(a.getString(R.styleable.VectorDrawablePath_pathData)); - mFillColor = a.getColor(R.styleable.VectorDrawablePath_fill, 0); - mFillOpacity = a.getFloat(R.styleable.VectorDrawablePath_fillOpacity, Float.NaN); - if (!Float.isNaN(mFillOpacity)) { - mFillColor &= 0x00FFFFFF; - mFillColor |= ((int) (0xFF * mFillOpacity)) << 24; + if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_fill] == 0) { + mFillColor = a.getColor(R.styleable.VectorDrawablePath_fill, 0); + } + + if (themeAttrs == null + || themeAttrs[R.styleable.VectorDrawablePath_fillOpacity] == 0) { + mFillOpacity = a.getFloat(R.styleable.VectorDrawablePath_fillOpacity, Float.NaN); } + mRotate = a.getFloat(R.styleable.VectorDrawablePath_rotation, 0); mPivotX = a.getFloat(R.styleable.VectorDrawablePath_pivotX, 0); mPivotY = a.getFloat(R.styleable.VectorDrawablePath_pivotY, 0); - int lineCap = a.getInt(R.styleable.VectorDrawablePath_strokeLineCap, 0); + + final int lineCap = a.getInt(R.styleable.VectorDrawablePath_strokeLineCap, 0); switch (lineCap) { case LINECAP_BUTT: mStrokelineCap = Paint.Cap.BUTT; @@ -1289,7 +1418,8 @@ public class VectorDrawable extends Drawable { mStrokelineCap = Paint.Cap.SQUARE; break; } - int lineJoin = a.getInt(R.styleable.VectorDrawablePath_strokeLineJoin, 0); + + final int lineJoin = a.getInt(R.styleable.VectorDrawablePath_strokeLineJoin, 0); switch (lineJoin) { case LINEJOIN_MITER: mStrokelineJoin = Paint.Join.MITER; @@ -1301,19 +1431,27 @@ public class VectorDrawable extends Drawable { mStrokelineJoin = Paint.Join.BEVEL; break; } + mStrokeMiterlimit = a.getFloat(R.styleable.VectorDrawablePath_strokeMiterLimit, mStrokeMiterlimit); - mStrokeColor = a.getColor(R.styleable.VectorDrawablePath_stroke, mStrokeColor); - mStrokeOpacity = a.getFloat(R.styleable.VectorDrawablePath_strokeOpacity, Float.NaN); - if (!Float.isNaN(mStrokeOpacity)) { - mStrokeColor &= 0x00FFFFFF; - mStrokeColor |= ((int) (0xFF * mStrokeOpacity)) << 24; + + if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_stroke] == 0) { + mStrokeColor = a.getColor(R.styleable.VectorDrawablePath_stroke, mStrokeColor); } + + if (themeAttrs == null + || themeAttrs[R.styleable.VectorDrawablePath_strokeOpacity] == 0) { + mStrokeOpacity = a.getFloat( + R.styleable.VectorDrawablePath_strokeOpacity, Float.NaN); + } + mStrokeWidth = a.getFloat(R.styleable.VectorDrawablePath_strokeWidth, 0); mTrimPathEnd = a.getFloat(R.styleable.VectorDrawablePath_trimPathEnd, 1); mTrimPathOffset = a.getFloat(R.styleable.VectorDrawablePath_trimPathOffset, 0); mTrimPathStart = a.getFloat(R.styleable.VectorDrawablePath_trimPathStart, 0); - int[] states = {R.styleable.VectorDrawablePath_state_activated, + + final int[] states = { + R.styleable.VectorDrawablePath_state_activated, R.styleable.VectorDrawablePath_state_checkable, R.styleable.VectorDrawablePath_state_checked, R.styleable.VectorDrawablePath_state_enabled, @@ -1321,15 +1459,66 @@ public class VectorDrawable extends Drawable { R.styleable.VectorDrawablePath_state_hovered, R.styleable.VectorDrawablePath_state_pressed, R.styleable.VectorDrawablePath_state_selected, - R.styleable.VectorDrawablePath_state_window_focused}; - for (int state : states) { + R.styleable.VectorDrawablePath_state_window_focused + }; + + final int N = states.length; + for (int i = 0; i < N; i++) { + final int state = states[i]; if (a.hasValue(state)) { addStateFilter(state, a.getBoolean(state, false)); } } + + updateColorAlphas(); + a.recycle(); } + public boolean canApplyTheme() { + return mThemeAttrs != null; + } + + public void applyTheme(Theme t) { + if (mThemeAttrs == null) { + return; + } + + final TypedArray a = t.resolveAttributes( + mThemeAttrs, R.styleable.VectorDrawablePath, 0, 0); + + if (a.hasValue(R.styleable.VectorDrawablePath_fill)) { + mFillColor = a.getColor(R.styleable.VectorDrawablePath_fill, 0); + } + + if (a.hasValue(R.styleable.VectorDrawablePath_fillOpacity)) { + mFillOpacity = a.getFloat(R.styleable.VectorDrawablePath_fillOpacity, Float.NaN); + } + + if (a.hasValue(R.styleable.VectorDrawablePath_stroke)) { + mStrokeColor = a.getColor(R.styleable.VectorDrawablePath_stroke, mStrokeColor); + } + + if (a.hasValue(R.styleable.VectorDrawablePath_strokeOpacity)) { + mStrokeOpacity = a.getFloat( + R.styleable.VectorDrawablePath_strokeOpacity, Float.NaN); + } + + updateColorAlphas(); + } + + private void updateColorAlphas() { + if (!Float.isNaN(mFillOpacity)) { + mFillColor &= 0x00FFFFFF; + mFillColor |= ((int) (0xFF * mFillOpacity)) << 24; + } + + if (!Float.isNaN(mStrokeOpacity)) { + mStrokeColor &= 0x00FFFFFF; + mStrokeColor |= ((int) (0xFF * mStrokeOpacity)) << 24; + } + } + private static int nextStart(String s, int end) { char c; @@ -1564,9 +1753,11 @@ public class VectorDrawable extends Drawable { } private static class VNode { + private static float[] current = new float[4]; + char type; float[] params; - private static float[] current = new float[4]; + public VNode(char type, float[] params) { this.type = type; this.params = params; diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h index 7cc10be872ac..4d8e512d1651 100644 --- a/include/androidfw/ResourceTypes.h +++ b/include/androidfw/ResourceTypes.h @@ -1054,6 +1054,7 @@ struct ResTable_config UI_MODE_TYPE_CAR = ACONFIGURATION_UI_MODE_TYPE_CAR, UI_MODE_TYPE_TELEVISION = ACONFIGURATION_UI_MODE_TYPE_TELEVISION, UI_MODE_TYPE_APPLIANCE = ACONFIGURATION_UI_MODE_TYPE_APPLIANCE, + UI_MODE_TYPE_WATCH = ACONFIGURATION_UI_MODE_TYPE_WATCH, // uiMode bits for the night switch. MASK_UI_MODE_NIGHT = 0x30, diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 7616ab0eafa6..098753bd5afa 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -2557,6 +2557,9 @@ String8 ResTable_config::toString() const { case ResTable_config::UI_MODE_TYPE_APPLIANCE: res.append("appliance"); break; + case ResTable_config::UI_MODE_TYPE_WATCH: + res.append("watch"); + break; default: res.appendFormat("uiModeType=%d", dtohs(screenLayout&ResTable_config::MASK_UI_MODE_TYPE)); diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp index 477d69172c78..df2123bb5db4 100644 --- a/libs/hwui/Caches.cpp +++ b/libs/hwui/Caches.cpp @@ -315,24 +315,15 @@ void Caches::clearGarbage() { pathCache.clearGarbage(); patchCache.clearGarbage(); - Vector<RenderNode*> displayLists; Vector<Layer*> layers; { // scope for the lock Mutex::Autolock _l(mGarbageLock); - displayLists = mDisplayListGarbage; layers = mLayerGarbage; - mDisplayListGarbage.clear(); mLayerGarbage.clear(); } - size_t count = displayLists.size(); - for (size_t i = 0; i < count; i++) { - RenderNode* displayList = displayLists.itemAt(i); - delete displayList; - } - - count = layers.size(); + size_t count = layers.size(); for (size_t i = 0; i < count; i++) { Layer* layer = layers.itemAt(i); delete layer; @@ -345,11 +336,6 @@ void Caches::deleteLayerDeferred(Layer* layer) { mLayerGarbage.push(layer); } -void Caches::deleteDisplayListDeferred(RenderNode* displayList) { - Mutex::Autolock _l(mGarbageLock); - mDisplayListGarbage.push(displayList); -} - void Caches::flush(FlushMode mode) { FLUSH_LOGD("Flushing caches (mode %d)", mode); diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h index 50c5fef633d7..ba3ccafc2879 100644 --- a/libs/hwui/Caches.h +++ b/libs/hwui/Caches.h @@ -166,11 +166,6 @@ public: */ void deleteLayerDeferred(Layer* layer); - /* - * Can be used to delete a display list from a non EGL thread. - */ - void deleteDisplayListDeferred(RenderNode* layer); - /** * Binds the VBO used to render simple textured quads. */ @@ -420,7 +415,6 @@ private: mutable Mutex mGarbageLock; Vector<Layer*> mLayerGarbage; - Vector<RenderNode*> mDisplayListGarbage; DebugLevel mDebugLevel; bool mInitialized; diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp index 5b4e03f515e0..ce711b65c9f9 100644 --- a/libs/hwui/DeferredLayerUpdater.cpp +++ b/libs/hwui/DeferredLayerUpdater.cpp @@ -68,13 +68,13 @@ bool DeferredLayerUpdater::apply() { mLayer->setColorFilter(mColorFilter); mLayer->setAlpha(mAlpha, mMode); - if (mDisplayList) { + if (mDisplayList.get()) { if (mWidth != mLayer->layer.getWidth() || mHeight != mLayer->layer.getHeight()) { success = LayerRenderer::resizeLayer(mLayer, mWidth, mHeight); } mLayer->setBlend(mBlend); mDisplayList->updateProperties(); - mLayer->updateDeferred(mDisplayList, + mLayer->updateDeferred(mDisplayList.get(), mDirtyRect.left, mDirtyRect.top, mDirtyRect.right, mDirtyRect.bottom); mDirtyRect.setEmpty(); mDisplayList = 0; diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h index 10fa264d87fe..ce08c2de8960 100644 --- a/libs/hwui/DeferredLayerUpdater.h +++ b/libs/hwui/DeferredLayerUpdater.h @@ -101,7 +101,7 @@ private: // Layer type specific properties // displayList and surfaceTexture are mutually exclusive, only 1 may be set // dirtyRect is only valid if displayList is set - RenderNode* mDisplayList; + sp<RenderNode> mDisplayList; Rect mDirtyRect; sp<GLConsumer> mSurfaceTexture; SkMatrix* mTransform; diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp index 9e6a96d38713..a5d8dcb3fe0f 100644 --- a/libs/hwui/DisplayList.cpp +++ b/libs/hwui/DisplayList.cpp @@ -29,6 +29,13 @@ namespace android { namespace uirenderer { +DisplayListData::DisplayListData() : projectionReceiveIndex(-1), functorCount(0), hasDrawOps(false) { +} + +DisplayListData::~DisplayListData() { + cleanupResources(); +} + void DisplayListData::cleanupResources() { Caches& caches = Caches::getInstance(); caches.unregisterFunctors(functorCount); @@ -91,5 +98,12 @@ void DisplayListData::cleanupResources() { layers.clear(); } +void DisplayListData::addChild(DrawDisplayListOp* op) { + LOG_ALWAYS_FATAL_IF(!op->renderNode(), "DrawDisplayListOp with no render node!"); + + mChildren.push(op); + mReferenceHolders.push(op->renderNode()); +} + }; // namespace uirenderer }; // namespace android diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h index df5cba61d7cb..fe70d13fb984 100644 --- a/libs/hwui/DisplayList.h +++ b/libs/hwui/DisplayList.h @@ -41,6 +41,7 @@ #include "Matrix.h" #include "DeferredDisplayList.h" #include "RenderProperties.h" +#include "utils/VirtualLightRefBase.h" class SkBitmap; class SkPaint; @@ -106,8 +107,8 @@ public: */ class DisplayListData { public: - DisplayListData() : projectionReceiveIndex(-1), functorCount(0), hasDrawOps(false) {} - virtual ~DisplayListData() { cleanupResources(); } + DisplayListData(); + ~DisplayListData(); // allocator into which all ops were allocated LinearAllocator allocator; @@ -115,9 +116,6 @@ public: // pointers to all ops within display list, pointing into allocator data Vector<DisplayListOp*> displayListOps; - // list of children display lists for quick, non-drawing traversal - Vector<DrawDisplayListOp*> children; - // index of DisplayListOp restore, after which projected descendents should be drawn int projectionReceiveIndex; @@ -139,7 +137,15 @@ public: return !displayListOps.size(); } + void addChild(DrawDisplayListOp* childOp); + const Vector<DrawDisplayListOp*>& children() { return mChildren; } + private: + Vector< sp<VirtualLightRefBase> > mReferenceHolders; + + // list of children display lists for quick, non-drawing traversal + Vector<DrawDisplayListOp*> mChildren; + void cleanupResources(); }; diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h index 5fa8f1d5e9ee..06f675efcf23 100644 --- a/libs/hwui/DisplayListOp.h +++ b/libs/hwui/DisplayListOp.h @@ -1523,6 +1523,8 @@ public: virtual const char* name() { return "DrawDisplayList"; } + RenderNode* renderNode() { return mDisplayList; } + private: RenderNode* mDisplayList; const int mFlags; diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp index 78c97e14d363..a84aa6b690e1 100644 --- a/libs/hwui/DisplayListRenderer.cpp +++ b/libs/hwui/DisplayListRenderer.cpp @@ -191,7 +191,7 @@ status_t DisplayListRenderer::drawDisplayList(RenderNode* displayList, DrawDisplayListOp* op = new (alloc()) DrawDisplayListOp(displayList, flags, *currentTransform()); addDrawOp(op); - mDisplayListData->children.push(op); + mDisplayListData->addChild(op); if (displayList->isProjectionReceiver()) { mDisplayListData->projectionReceiveIndex = mDisplayListData->displayListOps.size() - 1; } diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp index 27409a2887c2..bfe4eda44ddc 100644 --- a/libs/hwui/Layer.cpp +++ b/libs/hwui/Layer.cpp @@ -140,6 +140,15 @@ void Layer::removeFbo(bool flush) { } } +void Layer::updateDeferred(RenderNode* displayList, + int left, int top, int right, int bottom) { + requireRenderer(); + this->displayList = displayList; + const Rect r(left, top, right, bottom); + dirtyRect.unionWith(r); + deferredUpdateScheduled = true; +} + void Layer::setPaint(const SkPaint* paint) { OpenGLRenderer::getAlphaAndModeDirect(paint, &alpha, &mode); setColorFilter((paint) ? paint->getColorFilter() : NULL); @@ -244,7 +253,7 @@ void Layer::render() { renderer->prepareDirty(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom, !isBlend()); - renderer->drawDisplayList(displayList, dirtyRect, RenderNode::kReplayFlag_ClipChildren); + renderer->drawDisplayList(displayList.get(), dirtyRect, RenderNode::kReplayFlag_ClipChildren); renderer->finish(); diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h index b428404f04e0..5375b4509db1 100644 --- a/libs/hwui/Layer.h +++ b/libs/hwui/Layer.h @@ -19,6 +19,7 @@ #include <cutils/compiler.h> #include <sys/types.h> +#include <utils/StrongPointer.h> #include <GLES2/gl2.h> @@ -85,13 +86,7 @@ public: } void updateDeferred(RenderNode* displayList, - int left, int top, int right, int bottom) { - requireRenderer(); - this->displayList = displayList; - const Rect r(left, top, right, bottom); - dirtyRect.unionWith(r); - deferredUpdateScheduled = true; - } + int left, int top, int right, int bottom); inline uint32_t getWidth() const { return texture.width; @@ -294,7 +289,7 @@ public: */ bool deferredUpdateScheduled; OpenGLRenderer* renderer; - RenderNode* displayList; + sp<RenderNode> displayList; Rect dirtyRect; bool debugDrawUpdate; bool hasDrawnSinceUpdate; diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index e1f484d90fd7..9dbcd36677fd 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -537,7 +537,7 @@ void OpenGLRenderer::countOverdraw() { bool OpenGLRenderer::updateLayer(Layer* layer, bool inFrame) { if (layer->deferredUpdateScheduled && layer->renderer && - layer->displayList && layer->displayList->isRenderable()) { + layer->displayList.get() && layer->displayList->isRenderable()) { ATRACE_CALL(); Rect& dirty = layer->dirtyRect; diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index 663b67ef053c..501007679308 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -59,17 +59,6 @@ RenderNode::~RenderNode() { delete mDisplayListData; } -void RenderNode::destroyDisplayListDeferred(RenderNode* displayList) { - if (displayList) { - if (Caches::hasInstance()) { - DISPLAY_LIST_LOGD("Deferring display list destruction"); - Caches::getInstance().deleteDisplayListDeferred(displayList); - } else { - delete displayList; - } - } -} - void RenderNode::setData(DisplayListData* data) { delete mDisplayListData; mDisplayListData = data; @@ -104,8 +93,8 @@ void RenderNode::updateProperties() { } if (mDisplayListData) { - for (size_t i = 0; i < mDisplayListData->children.size(); i++) { - RenderNode* childNode = mDisplayListData->children[i]->mDisplayList; + for (size_t i = 0; i < mDisplayListData->children().size(); i++) { + RenderNode* childNode = mDisplayListData->children()[i]->mDisplayList; childNode->updateProperties(); } } @@ -118,8 +107,8 @@ bool RenderNode::hasFunctors() { return true; } - for (size_t i = 0; i < mDisplayListData->children.size(); i++) { - RenderNode* childNode = mDisplayListData->children[i]->mDisplayList; + for (size_t i = 0; i < mDisplayListData->children().size(); i++) { + RenderNode* childNode = mDisplayListData->children()[i]->mDisplayList; if (childNode->hasFunctors()) { return true; } @@ -248,8 +237,8 @@ void RenderNode::computeOrdering() { // TODO: create temporary DDLOp and call computeOrderingImpl on top DisplayList so that // transform properties are applied correctly to top level children if (mDisplayListData == NULL) return; - for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) { - DrawDisplayListOp* childOp = mDisplayListData->children[i]; + for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) { + DrawDisplayListOp* childOp = mDisplayListData->children()[i]; childOp->mDisplayList->computeOrderingImpl(childOp, &mProjectedNodes, &mat4::identity()); } @@ -277,11 +266,11 @@ void RenderNode::computeOrderingImpl( opState->mSkipInOrderDraw = false; } - if (mDisplayListData->children.size() > 0) { + if (mDisplayListData->children().size() > 0) { const bool isProjectionReceiver = mDisplayListData->projectionReceiveIndex >= 0; bool haveAppliedPropertiesToProjection = false; - for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) { - DrawDisplayListOp* childOp = mDisplayListData->children[i]; + for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) { + DrawDisplayListOp* childOp = mDisplayListData->children()[i]; RenderNode* child = childOp->mDisplayList; Vector<DrawDisplayListOp*>* projectionChildren = NULL; @@ -375,10 +364,10 @@ void RenderNode::replayNodeInParent(ReplayStateStruct& replayStruct, const int l } void RenderNode::buildZSortedChildList(Vector<ZDrawDisplayListOpPair>& zTranslatedNodes) { - if (mDisplayListData == NULL || mDisplayListData->children.size() == 0) return; + if (mDisplayListData == NULL || mDisplayListData->children().size() == 0) return; - for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) { - DrawDisplayListOp* childOp = mDisplayListData->children[i]; + for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) { + DrawDisplayListOp* childOp = mDisplayListData->children()[i]; RenderNode* child = childOp->mDisplayList; float childZ = child->properties().getTranslationZ(); diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index fb5336d1277c..fd0fabcffbbb 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -41,6 +41,7 @@ #include "DeferredDisplayList.h" #include "DisplayList.h" #include "RenderProperties.h" +#include "utils/VirtualLightRefBase.h" class SkBitmap; class SkPaint; @@ -76,7 +77,7 @@ class DrawDisplayListOp; * recorded stream of canvas operations is refreshed. The DisplayList (and its properties) stay * attached. */ -class RenderNode { +class RenderNode : public VirtualLightRefBase { public: ANDROID_API RenderNode(); ANDROID_API ~RenderNode(); @@ -86,7 +87,6 @@ public: kReplayFlag_ClipChildren = 0x1 }; - ANDROID_API static void destroyDisplayListDeferred(RenderNode* displayList); ANDROID_API static void outputLogBuffer(int fd); ANDROID_API void setData(DisplayListData* newData); diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp index 1e9089a30a46..7b509a2040a1 100644 --- a/libs/hwui/renderthread/DrawFrameTask.cpp +++ b/libs/hwui/renderthread/DrawFrameTask.cpp @@ -30,7 +30,19 @@ namespace android { namespace uirenderer { namespace renderthread { -DrawFrameTask::DrawFrameTask() : mContext(0), mRenderNode(0) { +SetDisplayListData::SetDisplayListData() : mNewData(0) {} + +SetDisplayListData::SetDisplayListData(RenderNode* node, DisplayListData* newData) + : mTargetNode(node), mNewData(newData) { +} + +SetDisplayListData::~SetDisplayListData() {} + +void SetDisplayListData::apply() const { + mTargetNode->setData(mNewData); +} + +DrawFrameTask::DrawFrameTask() : mContext(0), mTaskMode(MODE_INVALID), mRenderNode(0) { } DrawFrameTask::~DrawFrameTask() { @@ -41,13 +53,15 @@ void DrawFrameTask::setContext(CanvasContext* context) { } void DrawFrameTask::setDisplayListData(RenderNode* renderNode, DisplayListData* newData) { - SetDisplayListData setter; - setter.targetNode = renderNode; - setter.newData = newData; + LOG_ALWAYS_FATAL_IF(!mContext, "Lifecycle violation, there's no context to setDisplayListData with!"); + + SetDisplayListData setter(renderNode, newData); mDisplayListDataUpdates.push(setter); } void DrawFrameTask::addLayer(DeferredLayerUpdater* layer) { + LOG_ALWAYS_FATAL_IF(!mContext, "Lifecycle violation, there's no context to addLayer with!"); + mLayers.push(layer); } @@ -61,6 +75,8 @@ void DrawFrameTask::removeLayer(DeferredLayerUpdater* layer) { } void DrawFrameTask::setRenderNode(RenderNode* renderNode) { + LOG_ALWAYS_FATAL_IF(!mContext, "Lifecycle violation, there's no context to setRenderNode with!"); + mRenderNode = renderNode; } @@ -69,37 +85,55 @@ void DrawFrameTask::setDirty(int left, int top, int right, int bottom) { } void DrawFrameTask::drawFrame(RenderThread* renderThread) { - LOG_ALWAYS_FATAL_IF(!mRenderNode, "Cannot drawFrame with no render node!"); + LOG_ALWAYS_FATAL_IF(!mRenderNode.get(), "Cannot drawFrame with no render node!"); LOG_ALWAYS_FATAL_IF(!mContext, "Cannot drawFrame with no CanvasContext!"); - AutoMutex _lock(mLock); - renderThread->queue(this); - mSignal.wait(mLock); + postAndWait(renderThread, MODE_FULL); // Reset the single-frame data mDirty.setEmpty(); mRenderNode = 0; } +void DrawFrameTask::flushStateChanges(RenderThread* renderThread) { + LOG_ALWAYS_FATAL_IF(!mContext, "Cannot drawFrame with no CanvasContext!"); + + postAndWait(renderThread, MODE_STATE_ONLY); +} + +void DrawFrameTask::postAndWait(RenderThread* renderThread, TaskMode mode) { + LOG_ALWAYS_FATAL_IF(mode == MODE_INVALID, "That's not a real mode, silly!"); + + mTaskMode = mode; + AutoMutex _lock(mLock); + renderThread->queue(this); + mSignal.wait(mLock); +} + void DrawFrameTask::run() { ATRACE_NAME("DrawFrame"); syncFrameState(); + if (mTaskMode == MODE_STATE_ONLY) { + unblockUiThread(); + return; + } + // Grab a copy of everything we need Rect dirtyCopy(mDirty); - RenderNode* renderNode = mRenderNode; + sp<RenderNode> renderNode = mRenderNode; CanvasContext* context = mContext; // This is temporary until WebView has a solution for syncing frame state - bool canUnblockUiThread = !requiresSynchronousDraw(renderNode); + bool canUnblockUiThread = !requiresSynchronousDraw(renderNode.get()); // From this point on anything in "this" is *UNSAFE TO ACCESS* if (canUnblockUiThread) { unblockUiThread(); } - drawRenderNode(context, renderNode, &dirtyCopy); + drawRenderNode(context, renderNode.get(), &dirtyCopy); if (!canUnblockUiThread) { unblockUiThread(); @@ -111,12 +145,16 @@ void DrawFrameTask::syncFrameState() { for (size_t i = 0; i < mDisplayListDataUpdates.size(); i++) { const SetDisplayListData& setter = mDisplayListDataUpdates[i]; - setter.targetNode->setData(setter.newData); + setter.apply(); } mDisplayListDataUpdates.clear(); mContext->processLayerUpdates(&mLayers); - mRenderNode->updateProperties(); + + // If we don't have an mRenderNode this is a state flush only + if (mRenderNode.get()) { + mRenderNode->updateProperties(); + } } void DrawFrameTask::unblockUiThread() { diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h index 5450dd57133e..4e9b24487b47 100644 --- a/libs/hwui/renderthread/DrawFrameTask.h +++ b/libs/hwui/renderthread/DrawFrameTask.h @@ -18,6 +18,7 @@ #include <utils/Condition.h> #include <utils/Mutex.h> +#include <utils/StrongPointer.h> #include <utils/Vector.h> #include "RenderTask.h" @@ -36,9 +37,16 @@ namespace renderthread { class CanvasContext; class RenderThread; -struct SetDisplayListData { - RenderNode* targetNode; - DisplayListData* newData; +class SetDisplayListData { +public: + // This ctor exists for Vector's usage + SetDisplayListData(); + SetDisplayListData(RenderNode* node, DisplayListData* newData); + ~SetDisplayListData(); + void apply() const; +private: + sp<RenderNode> mTargetNode; + DisplayListData* mNewData; }; /* @@ -61,10 +69,18 @@ public: void setRenderNode(RenderNode* renderNode); void setDirty(int left, int top, int right, int bottom); void drawFrame(RenderThread* renderThread); + void flushStateChanges(RenderThread* renderThread); virtual void run(); private: + enum TaskMode { + MODE_INVALID, + MODE_FULL, + MODE_STATE_ONLY, + }; + + void postAndWait(RenderThread* renderThread, TaskMode mode); void syncFrameState(); void unblockUiThread(); static void drawRenderNode(CanvasContext* context, RenderNode* renderNode, Rect* dirty); @@ -81,7 +97,8 @@ private: /********************************************* * Single frame data *********************************************/ - RenderNode* mRenderNode; + TaskMode mTaskMode; + sp<RenderNode> mRenderNode; Rect mDirty; Vector<SetDisplayListData> mDisplayListDataUpdates; diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 0b6be1983f48..43e653ca76ec 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -75,6 +75,9 @@ CREATE_BRIDGE1(destroyContext, CanvasContext* context) { void RenderProxy::destroyContext() { if (mContext) { + // Flush any pending changes to ensure all garbage is destroyed + mDrawFrameTask.flushStateChanges(&mRenderThread); + SETUP_TASK(destroyContext); args->context = mContext; mContext = 0; @@ -138,6 +141,10 @@ CREATE_BRIDGE1(destroyCanvas, CanvasContext* context) { } void RenderProxy::destroyCanvas() { + // If the canvas is being destroyed we won't be drawing again anytime soon + // So flush any pending state changes to allow for resource cleanup. + mDrawFrameTask.flushStateChanges(&mRenderThread); + SETUP_TASK(destroyCanvas); args->context = mContext; post(task); diff --git a/libs/hwui/utils/VirtualLightRefBase.h b/libs/hwui/utils/VirtualLightRefBase.h new file mode 100644 index 000000000000..b545aabac802 --- /dev/null +++ b/libs/hwui/utils/VirtualLightRefBase.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef VIRTUALLIGHTREFBASE_H +#define VIRTUALLIGHTREFBASE_H + +#include <utils/RefBase.h> + +namespace android { +namespace uirenderer { + +// This is a wrapper around LightRefBase that simply enforces a virtual +// destructor to eliminate the template requirement of LightRefBase +class VirtualLightRefBase : public LightRefBase<VirtualLightRefBase> { +public: + virtual ~VirtualLightRefBase() {} +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* VIRTUALLIGHTREFBASE_H */ diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp index 2667a720af67..3f6ccc9acdf8 100644 --- a/libs/input/SpriteController.cpp +++ b/libs/input/SpriteController.cpp @@ -402,7 +402,7 @@ void SpriteController::SpriteImpl::setIcon(const SpriteIcon& icon) { uint32_t dirty; if (icon.isValid()) { - icon.bitmap.copyTo(&mLocked.state.icon.bitmap, SkBitmap::kARGB_8888_Config); + icon.bitmap.copyTo(&mLocked.state.icon.bitmap, kNative_8888_SkColorType); if (!mLocked.state.icon.isValid() || mLocked.state.icon.hotSpotX != icon.hotSpotX diff --git a/libs/input/SpriteController.h b/libs/input/SpriteController.h index 75e4843d38dc..797efd747066 100644 --- a/libs/input/SpriteController.h +++ b/libs/input/SpriteController.h @@ -65,7 +65,7 @@ struct SpriteIcon { inline SpriteIcon copy() const { SkBitmap bitmapCopy; - bitmap.copyTo(&bitmapCopy, SkBitmap::kARGB_8888_Config); + bitmap.copyTo(&bitmapCopy, kNative_8888_SkColorType); return SpriteIcon(bitmapCopy, hotSpotX, hotSpotY); } diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 77ab17be35ac..77944c8ec34b 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -107,5 +107,10 @@ <!-- milliseconds before the heads up notification accepts touches. --> <integer name="heads_up_sensitivity_delay">700</integer> + + <!-- The min animation duration for animating views that are currently visible. --> + <integer name="recents_filter_animate_current_views_min_duration">175</integer> + <!-- The min animation duration for animating views that are newly visible. --> + <integer name="recents_filter_animate_new_views_min_duration">125</integer> </resources> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 1c6d5ad0b98c..2c8f9a1612f9 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -236,6 +236,10 @@ <!-- The size of the activity icon in the recents task view. --> <dimen name="recents_task_view_activity_icon_size">60dp</dimen> + <!-- Used to calculate the translation animation duration, the expected amount of movement + in dps over one second of time. --> + <dimen name="recents_animation_movement_in_dps_per_second">800dp</dimen> + <!-- Space below the notification stack --> <dimen name="notification_stack_margin_bottom">0dp</dimen> diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java index ab2ad966e97f..eb09335c9565 100644 --- a/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java +++ b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java @@ -156,7 +156,7 @@ public class RecentTasksLoader implements View.OnTouchListener { // Create an TaskDescription, returning null if the title or icon is null TaskDescription createTaskDescription(int taskId, int persistentTaskId, Intent baseIntent, - ComponentName origActivity, CharSequence description) { + ComponentName origActivity, CharSequence description, int userId) { Intent intent = new Intent(baseIntent); if (origActivity != null) { intent.setComponent(origActivity); @@ -175,7 +175,7 @@ public class RecentTasksLoader implements View.OnTouchListener { TaskDescription item = new TaskDescription(taskId, persistentTaskId, resolveInfo, baseIntent, info.packageName, - description); + description, userId); item.setLabel(title); return item; @@ -391,7 +391,8 @@ public class RecentTasksLoader implements View.OnTouchListener { item = createTaskDescription(recentInfo.id, recentInfo.persistentId, recentInfo.baseIntent, - recentInfo.origActivity, recentInfo.description); + recentInfo.origActivity, recentInfo.description, + recentInfo.userId); if (item != null) { loadThumbnailAndIcon(item); } @@ -474,7 +475,8 @@ public class RecentTasksLoader implements View.OnTouchListener { TaskDescription item = createTaskDescription(recentInfo.id, recentInfo.persistentId, recentInfo.baseIntent, - recentInfo.origActivity, recentInfo.description); + recentInfo.origActivity, recentInfo.description, + recentInfo.userId); if (item != null) { while (true) { diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java index ef5604474baf..98bdee0c16ea 100644 --- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java @@ -689,7 +689,7 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener if (DEBUG) Log.v(TAG, "Starting activity " + intent); try { context.startActivityAsUser(intent, opts, - new UserHandle(UserHandle.USER_CURRENT)); + new UserHandle(ad.userId)); } catch (SecurityException e) { Log.e(TAG, "Recents does not have the permission to launch " + intent, e); } catch (ActivityNotFoundException e) { diff --git a/packages/SystemUI/src/com/android/systemui/recent/TaskDescription.java b/packages/SystemUI/src/com/android/systemui/recent/TaskDescription.java index 2e0ee3606956..5ad965f427a0 100644 --- a/packages/SystemUI/src/com/android/systemui/recent/TaskDescription.java +++ b/packages/SystemUI/src/com/android/systemui/recent/TaskDescription.java @@ -16,6 +16,7 @@ package com.android.systemui.recent; +import android.os.UserHandle; import android.content.Intent; import android.content.pm.ResolveInfo; import android.graphics.drawable.Drawable; @@ -27,6 +28,7 @@ public final class TaskDescription { final Intent intent; // launch intent for application final String packageName; // used to override animations (see onClick()) final CharSequence description; + final int userId; private Drawable mThumbnail; // generated by Activity.onCreateThumbnail() private Drawable mIcon; // application package icon @@ -35,7 +37,7 @@ public final class TaskDescription { public TaskDescription(int _taskId, int _persistentTaskId, ResolveInfo _resolveInfo, Intent _intent, - String _packageName, CharSequence _description) { + String _packageName, CharSequence _description, int _userId) { resolveInfo = _resolveInfo; intent = _intent; taskId = _taskId; @@ -43,6 +45,7 @@ public final class TaskDescription { description = _description; packageName = _packageName; + userId = _userId; } public TaskDescription() { @@ -53,6 +56,7 @@ public final class TaskDescription { description = null; packageName = null; + userId = UserHandle.USER_NULL; } public void setLoaded(boolean loaded) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/BakedBezierInterpolator.java b/packages/SystemUI/src/com/android/systemui/recents/BakedBezierInterpolator.java new file mode 100644 index 000000000000..b085211de5c2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/BakedBezierInterpolator.java @@ -0,0 +1,67 @@ +package com.android.systemui.recents; + +import android.animation.TimeInterpolator; + +/** + * A pre-baked bezier-curved interpolator for quantum-paper transitions. + */ +public class BakedBezierInterpolator implements TimeInterpolator { + public static final BakedBezierInterpolator INSTANCE = new BakedBezierInterpolator(); + + /** + * Use the INSTANCE variable instead of instantiating. + */ + private BakedBezierInterpolator() { + super(); + } + + /** + * Lookup table values. + * Generated using a Bezier curve from (0,0) to (1,1) with control points: + * P0 (0,0) + * P1 (0.4, 0) + * P2 (0.2, 1.0) + * P3 (1.0, 1.0) + * + * Values sampled with x at regular intervals between 0 and 1. + * + * These values were generated using: + * ./scripts/bezier_interpolator_values_gen.py 0.4 0.2 + */ + private static final float[] VALUES = new float[] { + 0.0f, 0.0002f, 0.0009f, 0.0019f, 0.0036f, 0.0059f, 0.0086f, 0.0119f, 0.0157f, 0.0209f, + 0.0257f, 0.0321f, 0.0392f, 0.0469f, 0.0566f, 0.0656f, 0.0768f, 0.0887f, 0.1033f, 0.1186f, + 0.1349f, 0.1519f, 0.1696f, 0.1928f, 0.2121f, 0.237f, 0.2627f, 0.2892f, 0.3109f, 0.3386f, + 0.3667f, 0.3952f, 0.4241f, 0.4474f, 0.4766f, 0.5f, 0.5234f, 0.5468f, 0.5701f, 0.5933f, + 0.6134f, 0.6333f, 0.6531f, 0.6698f, 0.6891f, 0.7054f, 0.7214f, 0.7346f, 0.7502f, 0.763f, + 0.7756f, 0.7879f, 0.8f, 0.8107f, 0.8212f, 0.8326f, 0.8415f, 0.8503f, 0.8588f, 0.8672f, + 0.8754f, 0.8833f, 0.8911f, 0.8977f, 0.9041f, 0.9113f, 0.9165f, 0.9232f, 0.9281f, 0.9328f, + 0.9382f, 0.9434f, 0.9476f, 0.9518f, 0.9557f, 0.9596f, 0.9632f, 0.9662f, 0.9695f, 0.9722f, + 0.9753f, 0.9777f, 0.9805f, 0.9826f, 0.9847f, 0.9866f, 0.9884f, 0.9901f, 0.9917f, 0.9931f, + 0.9944f, 0.9955f, 0.9964f, 0.9973f, 0.9981f, 0.9986f, 0.9992f, 0.9995f, 0.9998f, 1.0f, 1.0f + }; + + private static final float STEP_SIZE = 1.0f / (VALUES.length - 1); + + @Override + public float getInterpolation(float input) { + if (input >= 1.0f) { + return 1.0f; + } + + if (input <= 0f) { + return 0f; + } + + int position = Math.min( + (int)(input * (VALUES.length - 1)), + VALUES.length - 2); + + float quantized = position * STEP_SIZE; + float difference = input - quantized; + float weight = difference / STEP_SIZE; + + return VALUES[position] + weight * (VALUES[position + 1] - VALUES[position]); + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java index 8c5c8fa15f84..86f188e288e1 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java +++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java @@ -34,7 +34,7 @@ public class Constants { // For debugging, this enables us to create mock recents tasks public static final boolean EnableSystemServicesProxy = false; // For debugging, this defines the number of mock recents packages to create - public static final int SystemServicesProxyMockPackageCount = 12; + public static final int SystemServicesProxyMockPackageCount = 3; // For debugging, this defines the number of mock recents tasks to create public static final int SystemServicesProxyMockTaskCount = 75; @@ -82,16 +82,8 @@ public class Constants { } public static class TaskStackView { - public static class Animation { - public static final int TaskRemovedReshuffleDuration = 200; - public static final int SnapScrollBackDuration = 650; - public static final int FilteredCurrentViewsDuration = 150; - public static final int FilteredNewViewsDuration = 200; - public static final int UnfilteredCurrentViewsDuration = 150; - public static final int UnfilteredNewViewsDuration = 200; - } - public static final int TaskStackOverscrollRange = 150; + public static final int FilterStartDelay = 25; // The padding will be applied to the smallest dimension, and then applied to all sides public static final float StackPaddingPct = 0.15f; @@ -106,12 +98,6 @@ public class Constants { } public static class TaskView { - public static class Animation { - public static final int TaskDataUpdatedFadeDuration = 250; - public static final int TaskIconOnEnterDuration = 175; - public static final int TaskIconOnLeavingDuration = 75; - } - public static final boolean AnimateFrontTaskIconOnEnterRecents = true; public static final boolean AnimateFrontTaskIconOnLeavingRecents = true; diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java index 3d47cb6dcfd8..4a0de0bae118 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java @@ -17,11 +17,11 @@ package com.android.systemui.recents; import android.content.Context; -import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Rect; import android.util.DisplayMetrics; import android.util.TypedValue; +import com.android.systemui.R; /** A static Recents configuration for the current context @@ -34,6 +34,11 @@ public class RecentsConfiguration { public Rect systemInsets = new Rect(); public Rect displayRect = new Rect(); + public float animationPxMovementPerSecond; + + public int filteringCurrentViewsMinAnimDuration; + public int filteringNewViewsMinAnimDuration; + /** Private constructor */ private RecentsConfiguration() {} @@ -58,6 +63,12 @@ public class RecentsConfiguration { mDisplayMetrics = dm; displayRect.set(0, 0, dm.widthPixels, dm.heightPixels); + animationPxMovementPerSecond = + res.getDimensionPixelSize(R.dimen.recents_animation_movement_in_dps_per_second); + filteringCurrentViewsMinAnimDuration = + res.getInteger(R.integer.recents_filter_animate_current_views_min_duration); + filteringNewViewsMinAnimDuration = + res.getInteger(R.integer.recents_filter_animate_new_views_min_duration); } public void updateSystemInsets(Rect insets) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java index e193a95420f8..d661f287c13c 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java @@ -414,7 +414,7 @@ public class RecentsTaskLoader { // Create a new task Task task = new Task(t.persistentId, (t.id > -1), t.baseIntent, activityLabel, - activityIcon); + activityIcon, t.userId); // Preload the specified number of apps if (i >= (taskCount - preloadCount)) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java index f147fbc6f265..efcd948d830d 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java @@ -51,9 +51,9 @@ public class SystemServicesProxy { if (Constants.DebugFlags.App.EnableSystemServicesProxy) { // Create a dummy icon - mDummyIcon = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8); + mDummyIcon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); Canvas c = new Canvas(mDummyIcon); - c.drawColor(0xFFFF0000); + c.drawColor(0xFF999999); c.setBitmap(null); } } @@ -77,7 +77,7 @@ public class SystemServicesProxy { rti.id = rti.persistentId = i; rti.baseIntent = new Intent(); rti.baseIntent.setComponent(cn); - rti.description = rti.activityLabel = + rti.description = rti.activityLabel = "" + i + " - " + Long.toString(Math.abs(new Random().nextLong()), 36); if (i % 2 == 0) { rti.activityIcon = Bitmap.createBitmap(mDummyIcon); @@ -118,7 +118,7 @@ public class SystemServicesProxy { if (Constants.DebugFlags.App.EnableSystemServicesProxy) { Bitmap thumbnail = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); Canvas c = new Canvas(thumbnail); - c.drawColor(0xFF00ff00); + c.drawColor(0xff333333); c.setBitmap(null); return thumbnail; } @@ -178,7 +178,7 @@ public class SystemServicesProxy { // If we are mocking, then return a mock label if (Constants.DebugFlags.App.EnableSystemServicesProxy) { - return new ColorDrawable(0xFFff0000); + return new ColorDrawable(0xFF666666); } return info.loadIcon(mPm); diff --git a/packages/SystemUI/src/com/android/systemui/recents/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/Utilities.java index 9048cba930a7..4a1b3b2749b5 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/Utilities.java +++ b/packages/SystemUI/src/com/android/systemui/recents/Utilities.java @@ -20,6 +20,19 @@ import android.graphics.Rect; /* Common code */ public class Utilities { + /** + * Calculates a consistent animation duration (ms) for all animations depending on the movement + * of the object being animated. + */ + public static int calculateTranslationAnimationDuration(int distancePx) { + return calculateTranslationAnimationDuration(distancePx, 100); + } + public static int calculateTranslationAnimationDuration(int distancePx, int minDuration) { + RecentsConfiguration config = RecentsConfiguration.getInstance(); + return Math.max(minDuration, (int) (1000f /* ms/s */ * + (Math.abs(distancePx) / config.animationPxMovementPerSecond))); + } + /** Scales a rect about its centroid */ public static void scaleRectAboutCenter(Rect r, float scale) { if (scale != 1.0f) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java index ed2ab2a5b91f..a0ff3b7dda8c 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java @@ -65,15 +65,17 @@ public class Task { public Bitmap activityIcon; public Bitmap thumbnail; public boolean isActive; + public int userId; TaskCallbacks mCb; public Task(int id, boolean isActive, Intent intent, String activityTitle, - Bitmap activityIcon) { + Bitmap activityIcon, int userId) { this.key = new TaskKey(id, intent); this.activityLabel = activityTitle; this.activityIcon = activityIcon; this.isActive = isActive; + this.userId = userId; } /** Set the callbacks */ diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java index cb5279490ff0..1ebe2317883e 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java @@ -257,9 +257,9 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV try { if (opts != null) { getContext().startActivityAsUser(i, opts.toBundle(), - UserHandle.CURRENT); + new UserHandle(task.userId)); } else { - getContext().startActivityAsUser(i, UserHandle.CURRENT); + getContext().startActivityAsUser(i, new UserHandle(task.userId)); } } catch (ActivityNotFoundException anfe) { Console.logError(getContext(), "Could not start Activity"); diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java index dfd608cb4342..fa06764e9b3e 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -35,11 +35,11 @@ import android.view.ViewParent; import android.widget.FrameLayout; import android.widget.OverScroller; import com.android.systemui.R; +import com.android.systemui.recents.BakedBezierInterpolator; import com.android.systemui.recents.Console; import com.android.systemui.recents.Constants; import com.android.systemui.recents.RecentsConfiguration; import com.android.systemui.recents.RecentsTaskLoader; -import com.android.systemui.recents.SystemServicesProxy; import com.android.systemui.recents.Utilities; import com.android.systemui.recents.model.Task; import com.android.systemui.recents.model.TaskStack; @@ -76,6 +76,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal OverScroller mScroller; ObjectAnimator mScrollAnimator; + // Filtering + AnimatorSet mFilterChildrenAnimator; + // Optimizations int mHwLayersRefCount; int mStackViewsAnimationDuration; @@ -180,7 +183,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal */ private ArrayList<TaskViewTransform> getStackTransforms(ArrayList<Task> tasks, int stackScroll, - int[] visibleRangeOut) { + int[] visibleRangeOut, + boolean boundTranslationsToRect) { // XXX: Optimization: Use binary search to find the visible range ArrayList<TaskViewTransform> taskTransforms = new ArrayList<TaskViewTransform>(); @@ -196,6 +200,10 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } lastVisibleIndex = i; } + + if (boundTranslationsToRect) { + transform.translationY = Math.min(transform.translationY, mRect.bottom); + } } if (visibleRangeOut != null) { visibleRangeOut[0] = firstVisibleIndex; @@ -219,7 +227,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal int stackScroll = getStackScroll(); ArrayList<Task> tasks = mStack.getTasks(); ArrayList<TaskViewTransform> taskTransforms = getStackTransforms(tasks, stackScroll, - visibleRange); + visibleRange, false); // Update the visible state of all the tasks int taskCount = tasks.size(); @@ -286,7 +294,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } /** Animates the stack scroll into bounds */ - ObjectAnimator animateBoundScroll(int duration) { + ObjectAnimator animateBoundScroll() { int curScroll = getStackScroll(); int newScroll = Math.max(mMinScroll, Math.min(mMaxScroll, curScroll)); if (newScroll != curScroll) { @@ -298,16 +306,18 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal abortBoundScrollAnimation(); // Start a new scroll animation - animateScroll(curScroll, newScroll, duration); + animateScroll(curScroll, newScroll); mScrollAnimator.start(); } return mScrollAnimator; } /** Animates the stack scroll */ - void animateScroll(int curScroll, int newScroll, int duration) { + void animateScroll(int curScroll, int newScroll) { mScrollAnimator = ObjectAnimator.ofInt(this, "stackScroll", curScroll, newScroll); - mScrollAnimator.setDuration(duration); + mScrollAnimator.setDuration(Utilities.calculateTranslationAnimationDuration(newScroll - + curScroll, 250)); + mScrollAnimator.setInterpolator(BakedBezierInterpolator.INSTANCE); mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { @@ -625,7 +635,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } updateMinMaxScroll(true); - requestSynchronizeStackViewsWithModel(Constants.Values.TaskStackView.Animation.TaskRemovedReshuffleDuration); + int movement = (int) (Constants.Values.TaskStackView.StackOverlapPct * mTaskRect.height()); + requestSynchronizeStackViewsWithModel(Utilities.calculateTranslationAnimationDuration(movement)); } @Override @@ -635,20 +646,22 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal // (filtered) stack // XXX: Use HW Layers - // Stash the scroll for us to restore to when we unfilter + // Stash the scroll and filtered task for us to restore to when we unfilter mStashedScroll = getStackScroll(); // Compute the transforms of the items in the current stack final ArrayList<TaskViewTransform> curTaskTransforms = - getStackTransforms(curStack, mStashedScroll, null); + getStackTransforms(curStack, mStashedScroll, null, true); - // Bound the new stack scroll + // Scroll the item to the top of the stack (sans-peek) rect so that we can see it better updateMinMaxScroll(false); + float overlapHeight = Constants.Values.TaskStackView.StackOverlapPct * mTaskRect.height(); + setStackScrollRaw((int) (newStack.indexOfTask(filteredTask) * overlapHeight)); boundScrollRaw(); - // Compute the transforms of the items in the new stack + // Compute the transforms of the items in the new stack after setting the new scroll final ArrayList<TaskViewTransform> taskTransforms = - getStackTransforms(mStack.getTasks(), getStackScroll(), null); + getStackTransforms(mStack.getTasks(), getStackScroll(), null, true); // Animate all of the existing views on screen either out of view (if they are not visible // in the new stack) or to their final positions in the new stack @@ -656,13 +669,16 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal final ArrayList<Task> tasks = mStack.getTasks(); ArrayList<Animator> childViewAnims = new ArrayList<Animator>(); int childCount = getChildCount(); + int movement = 0; for (int i = 0; i < childCount; i++) { TaskView tv = (TaskView) getChildAt(i); Task task = tv.getTask(); TaskViewTransform toTransform; int taskIndex = tasks.indexOf(task); - if ((taskIndex < 0) || !taskTransforms.get(taskIndex).visible) { - // Compose a new transform that animates the task view out of view + + boolean willBeInvisible = (taskIndex < 0) || !taskTransforms.get(taskIndex).visible; + if (willBeInvisible) { + // Compose a new transform that fades and slides the task out of view TaskViewTransform fromTransform = curTaskTransforms.get(curStack.indexOf(task)); toTransform = new TaskViewTransform(fromTransform); tv.updateViewPropertiesToTaskTransform(null, fromTransform, 0); @@ -671,23 +687,49 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal childrenToReturnToPool.add(tv); } else { toTransform = taskTransforms.get(taskIndex); + + // Use the movement of the visible views to calculate the duration of the animation + movement = Math.max(movement, + Math.abs(toTransform.translationY - (int) tv.getTranslationY())); } childViewAnims.add(tv.getAnimatorToTaskTransform(toTransform)); } - AnimatorSet childViewAnimSet = new AnimatorSet(); - childViewAnimSet.setDuration( - Constants.Values.TaskStackView.Animation.FilteredCurrentViewsDuration); - childViewAnimSet.addListener(new AnimatorListenerAdapter() { + // Cancel the previous animation + if (mFilterChildrenAnimator != null) { + mFilterChildrenAnimator.cancel(); + mFilterChildrenAnimator.removeAllListeners(); + } + + // Create a new animation for the existing children + final RecentsConfiguration config = RecentsConfiguration.getInstance(); + mFilterChildrenAnimator = new AnimatorSet(); + mFilterChildrenAnimator.setDuration( + Utilities.calculateTranslationAnimationDuration(movement, + config.filteringCurrentViewsMinAnimDuration)); + mFilterChildrenAnimator.setInterpolator(BakedBezierInterpolator.INSTANCE); + mFilterChildrenAnimator.addListener(new AnimatorListenerAdapter() { + boolean isCancelled; + + @Override + public void onAnimationCancel(Animator animation) { + isCancelled = true; + } + @Override public void onAnimationEnd(Animator animation) { + if (isCancelled) return; + // Return all the removed children to the view pool for (TaskView tv : childrenToReturnToPool) { mViewPool.returnViewToPool(tv); } // For views that are not already visible, animate them in + ArrayList<Animator> newViewsAnims = new ArrayList<Animator>(); int taskCount = tasks.size(); + int movement = 0; + int offset = 0; for (int i = 0; i < taskCount; i++) { Task task = tasks.get(i); TaskViewTransform toTransform = taskTransforms.get(i); @@ -697,20 +739,38 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal TaskView tv = getChildViewForTask(task); if (tv == null) { tv = mViewPool.pickUpViewFromPool(task, task); + // Compose a new transform that fades and slides the new task in + fromTransform = new TaskViewTransform(toTransform); + tv.prepareTaskTransformForFilterTaskHidden(fromTransform); + tv.updateViewPropertiesToTaskTransform(null, fromTransform, 0); - // Animate from the current position to the new position - tv.prepareTaskTransformForFilterTaskVisible(fromTransform); - tv.updateViewPropertiesToTaskTransform(fromTransform, - toTransform, - Constants.Values.TaskStackView.Animation.FilteredNewViewsDuration); + Animator anim = tv.getAnimatorToTaskTransform(toTransform); + anim.setStartDelay(offset * + Constants.Values.TaskStackView.FilterStartDelay); + newViewsAnims.add(anim); + + // Use the movement of the newly visible views to calculate the duration + // of the animation + movement = Math.max(movement, Math.abs(toTransform.translationY - + fromTransform.translationY)); + offset++; } } + + // Animate the new views in + mFilterChildrenAnimator = new AnimatorSet(); + mFilterChildrenAnimator.setDuration( + Utilities.calculateTranslationAnimationDuration(movement, + config.filteringNewViewsMinAnimDuration)); + mFilterChildrenAnimator.setInterpolator(BakedBezierInterpolator.INSTANCE); + mFilterChildrenAnimator.playTogether(newViewsAnims); + mFilterChildrenAnimator.start(); } invalidate(); } }); - childViewAnimSet.playTogether(childViewAnims); - childViewAnimSet.start(); + mFilterChildrenAnimator.playTogether(childViewAnims); + mFilterChildrenAnimator.start(); } @Override @@ -718,16 +778,16 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal // Compute the transforms of the items in the current stack final int curScroll = getStackScroll(); final ArrayList<TaskViewTransform> curTaskTransforms = - getStackTransforms(curStack, curScroll, null); + getStackTransforms(curStack, curScroll, null, true); // Restore the stashed scroll updateMinMaxScroll(false); setStackScrollRaw(mStashedScroll); boundScrollRaw(); - // Compute the transforms of the items in the new stack + // Compute the transforms of the items in the new stack after restoring the stashed scroll final ArrayList<TaskViewTransform> taskTransforms = - getStackTransforms(mStack.getTasks(), getStackScroll(), null); + getStackTransforms(mStack.getTasks(), getStackScroll(), null, true); // Animate all of the existing views out of view (if they are not in the visible range in // the new stack) or to their final positions in the new stack @@ -735,29 +795,55 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal final ArrayList<Task> tasks = mStack.getTasks(); ArrayList<Animator> childViewAnims = new ArrayList<Animator>(); int childCount = getChildCount(); + int movement = 0; for (int i = 0; i < childCount; i++) { TaskView tv = (TaskView) getChildAt(i); Task task = tv.getTask(); int taskIndex = tasks.indexOf(task); - TaskViewTransform transform; + TaskViewTransform toTransform; // If the view is no longer visible, then we should just animate it out - if (taskIndex < 0 || !taskTransforms.get(taskIndex).visible) { - transform = new TaskViewTransform(curTaskTransforms.get(curStack.indexOf(task))); - tv.prepareTaskTransformForFilterTaskVisible(transform); + boolean willBeInvisible = taskIndex < 0 || !taskTransforms.get(taskIndex).visible; + if (willBeInvisible) { + toTransform = new TaskViewTransform(taskTransforms.get(taskIndex)); + tv.prepareTaskTransformForFilterTaskVisible(toTransform); childrenToRemove.add(tv); } else { - transform = taskTransforms.get(taskIndex); + toTransform = taskTransforms.get(taskIndex); + // Use the movement of the visible views to calculate the duration of the animation + movement = Math.max(movement, Math.abs(toTransform.translationY - + (int) tv.getTranslationY())); } - childViewAnims.add(tv.getAnimatorToTaskTransform(transform)); + + Animator anim = tv.getAnimatorToTaskTransform(toTransform); + childViewAnims.add(anim); + } + + // Cancel the previous animation + if (mFilterChildrenAnimator != null) { + mFilterChildrenAnimator.cancel(); + mFilterChildrenAnimator.removeAllListeners(); } - AnimatorSet childViewAnimSet = new AnimatorSet(); - childViewAnimSet.setDuration( - Constants.Values.TaskStackView.Animation.UnfilteredCurrentViewsDuration); - childViewAnimSet.addListener(new AnimatorListenerAdapter() { + // Create a new animation for the existing children + final RecentsConfiguration config = RecentsConfiguration.getInstance(); + mFilterChildrenAnimator = new AnimatorSet(); + mFilterChildrenAnimator.setDuration( + Utilities.calculateTranslationAnimationDuration(movement, + config.filteringCurrentViewsMinAnimDuration)); + mFilterChildrenAnimator.setInterpolator(BakedBezierInterpolator.INSTANCE); + mFilterChildrenAnimator.addListener(new AnimatorListenerAdapter() { + boolean isCancelled; + + @Override + public void onAnimationCancel(Animator animation) { + isCancelled = true; + } + @Override public void onAnimationEnd(Animator animation) { + if (isCancelled) return; + // Return all the removed children to the view pool for (TaskView tv : childrenToRemove) { mViewPool.returnViewToPool(tv); @@ -768,8 +854,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal // For views that are not already visible, animate them in ArrayList<Animator> newViewAnims = new ArrayList<Animator>(); - AnimatorSet newViewAnimSet = new AnimatorSet(); int taskCount = tasks.size(); + int movement = 0; int offset = 0; for (int i = 0; i < taskCount; i++) { Task task = tasks.get(i); @@ -780,34 +866,46 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal // For views that are not already visible, animate them in tv = mViewPool.pickUpViewFromPool(task, task); - // Animate in this new view + // Compose a new transform to fade and slide the new task in TaskViewTransform fromTransform = new TaskViewTransform(toTransform); tv.prepareTaskTransformForFilterTaskHidden(fromTransform); tv.updateViewPropertiesToTaskTransform(null, fromTransform, 0); - newViewAnims.add(tv.getAnimatorToTaskTransform(toTransform)); + + Animator anim = tv.getAnimatorToTaskTransform(toTransform); + anim.setStartDelay(offset * + Constants.Values.TaskStackView.FilterStartDelay); + newViewAnims.add(anim); + // Use the movement of the newly visible views to calculate the duration + // of the animation + movement = Math.max(movement, + Math.abs(toTransform.translationY - fromTransform.translationY)); offset++; } } } // Run the animation - newViewAnimSet.setDuration( - Constants.Values.TaskStackView.Animation.UnfilteredNewViewsDuration); - newViewAnimSet.playTogether(newViewAnims); - newViewAnimSet.addListener(new AnimatorListenerAdapter() { + mFilterChildrenAnimator = new AnimatorSet(); + mFilterChildrenAnimator.setDuration( + Utilities.calculateTranslationAnimationDuration(movement, + config.filteringNewViewsMinAnimDuration)); + mFilterChildrenAnimator.playTogether(newViewAnims); + mFilterChildrenAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { // Decrement the hw layers ref count decHwLayersRefCount("unfilteredNewViews"); } }); - newViewAnimSet.start(); - + mFilterChildrenAnimator.start(); invalidate(); } }); - childViewAnimSet.playTogether(childViewAnims); - childViewAnimSet.start(); + mFilterChildrenAnimator.playTogether(childViewAnims); + mFilterChildrenAnimator.start(); + + // Clear the saved vars + mStashedScroll = 0; } /**** ViewPoolConsumer Implementation ****/ @@ -1056,7 +1154,7 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: { // Animate the scroll back if we've cancelled - mSv.animateBoundScroll(Constants.Values.TaskStackView.Animation.SnapScrollBackDuration); + mSv.animateBoundScroll(); // Disable HW layers if (mIsScrolling) { mSv.decHwLayersRefCount("stackScroll"); @@ -1186,7 +1284,7 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { } else if (mSv.isScrollOutOfBounds()) { // Animate the scroll back into bounds // XXX: Make this animation a function of the velocity OR distance - mSv.animateBoundScroll(Constants.Values.TaskStackView.Animation.SnapScrollBackDuration); + mSv.animateBoundScroll(); } if (mIsScrolling) { @@ -1220,7 +1318,7 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { if (mSv.isScrollOutOfBounds()) { // Animate the scroll back into bounds // XXX: Make this animation a function of the velocity OR distance - mSv.animateBoundScroll(Constants.Values.TaskStackView.Animation.SnapScrollBackDuration); + mSv.animateBoundScroll(); } mActivePointerId = INACTIVE_POINTER_ID; mIsScrolling = false; diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java index 2c27d440f33c..e99fecb022a8 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java @@ -20,20 +20,24 @@ import android.animation.Animator; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.content.Context; +import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.Path; import android.graphics.Rect; import android.graphics.RectF; import android.util.AttributeSet; import android.view.View; -import android.view.animation.AccelerateDecelerateInterpolator; -import android.view.animation.DecelerateInterpolator; import android.widget.FrameLayout; import com.android.systemui.R; +import com.android.systemui.recents.BakedBezierInterpolator; import com.android.systemui.recents.Constants; import com.android.systemui.recents.RecentsConfiguration; +import com.android.systemui.recents.Utilities; import com.android.systemui.recents.model.Task; +import java.util.Random; + /* A task view */ public class TaskView extends FrameLayout implements View.OnClickListener, Task.TaskCallbacks { @@ -131,7 +135,7 @@ public class TaskView extends FrameLayout implements View.OnClickListener, Task. .scaleY(toTransform.scale) .alpha(toTransform.alpha) .setDuration(duration) - .setInterpolator(new AccelerateDecelerateInterpolator()) + .setInterpolator(BakedBezierInterpolator.INSTANCE) .withLayer() .start(); } else { @@ -190,7 +194,8 @@ public class TaskView extends FrameLayout implements View.OnClickListener, Task. .translationX(0) .translationY(0) .setStartDelay(235) - .setDuration(Constants.Values.TaskView.Animation.TaskIconOnEnterDuration) + .setInterpolator(BakedBezierInterpolator.INSTANCE) + .setDuration(Utilities.calculateTranslationAnimationDuration(translate)) .withLayer() .start(); } @@ -206,8 +211,8 @@ public class TaskView extends FrameLayout implements View.OnClickListener, Task. .translationX(translate / 2) .translationY(-translate) .setStartDelay(0) - .setDuration(Constants.Values.TaskView.Animation.TaskIconOnLeavingDuration) - .setInterpolator(new DecelerateInterpolator()) + .setInterpolator(BakedBezierInterpolator.INSTANCE) + .setDuration(Utilities.calculateTranslationAnimationDuration(translate)) .withLayer() .withEndAction(r) .start(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index f34903696982..844b96480d87 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -970,7 +970,11 @@ public abstract class BaseStatusBar extends SystemUI implements if (mPanelSlightlyVisible != visible) { mPanelSlightlyVisible = visible; try { - mBarService.onPanelRevealed(); + if (visible) { + mBarService.onPanelRevealed(); + } else { + mBarService.onPanelHidden(); + } } catch (RemoteException ex) { // Won't fail unless the world has ended. } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java index f1c8e0158fca..89da08f40834 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java @@ -17,6 +17,8 @@ package com.android.systemui.statusbar; import android.content.Context; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; import android.util.AttributeSet; import android.util.Log; import android.view.View; @@ -35,11 +37,14 @@ public class SignalClusterView static final boolean DEBUG = false; static final String TAG = "SignalClusterView"; + static final PorterDuffColorFilter PROBLEM_FILTER + = new PorterDuffColorFilter(0xffab653b, PorterDuff.Mode.SRC_ATOP); NetworkController mNC; private boolean mWifiVisible = false; private int mWifiStrengthId = 0; + private boolean mInetProblem; private boolean mMobileVisible = false; private int mMobileStrengthId = 0, mMobileTypeId = 0; private boolean mIsAirplaneMode = false; @@ -96,19 +101,22 @@ public class SignalClusterView } @Override - public void setWifiIndicators(boolean visible, int strengthIcon, String contentDescription) { + public void setWifiIndicators(boolean visible, int strengthIcon, boolean problem, + String contentDescription) { mWifiVisible = visible; mWifiStrengthId = strengthIcon; + mInetProblem = problem; mWifiDescription = contentDescription; apply(); } @Override - public void setMobileDataIndicators(boolean visible, int strengthIcon, + public void setMobileDataIndicators(boolean visible, int strengthIcon, boolean problem, int typeIcon, String contentDescription, String typeContentDescription) { mMobileVisible = visible; mMobileStrengthId = strengthIcon; + mInetProblem = problem; mMobileTypeId = typeIcon; mMobileDescription = contentDescription; mMobileTypeDescription = typeContentDescription; @@ -158,13 +166,17 @@ public class SignalClusterView apply(); } + private void applyInetProblem(ImageView iv) { + iv.setColorFilter(mInetProblem ? PROBLEM_FILTER : null); + } + // Run after each indicator change. private void apply() { if (mWifiGroup == null) return; if (mWifiVisible) { mWifi.setImageResource(mWifiStrengthId); - + applyInetProblem(mWifi); mWifiGroup.setContentDescription(mWifiDescription); mWifiGroup.setVisibility(View.VISIBLE); } else { @@ -179,7 +191,7 @@ public class SignalClusterView if (mMobileVisible && !mIsAirplaneMode) { mMobile.setImageResource(mMobileStrengthId); mMobileType.setImageResource(mMobileTypeId); - + applyInetProblem(mMobile); mMobileGroup.setContentDescription(mMobileTypeDescription + " " + mMobileDescription); mMobileGroup.setVisibility(View.VISIBLE); } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java index 3c8af3007b00..20fb225c8382 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java @@ -529,7 +529,6 @@ public class PanelView extends FrameLayout { switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: - mTracking = true; if (mHandleView != null) { mHandleView.setPressed(true); // catch the press state change @@ -561,6 +560,7 @@ public class PanelView extends FrameLayout { if (h < -mTouchSlop) { mInitialOffsetOnTouch = mExpandedHeight; mInitialTouchY = y; + mTracking = true; return true; } } @@ -793,7 +793,7 @@ public class PanelView extends FrameLayout { } public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println(String.format("[PanelView(%s): expandedHeight=%f maxPanelHeight=%f closing=%s" + pw.println(String.format("[PanelView(%s): expandedHeight=%f maxPanelHeight=%d closing=%s" + " tracking=%s rubberbanding=%s justPeeked=%s peekAnim=%s%s timeAnim=%s%s" + "]", this.getClass().getSimpleName(), diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java index 09f1695aa30a..92c008ebbbd8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java @@ -126,6 +126,7 @@ public class NetworkController extends BroadcastReceiver implements DemoMode { private int mConnectedNetworkType = ConnectivityManager.TYPE_NONE; private String mConnectedNetworkTypeName; private int mInetCondition = 0; + private int mLastInetCondition = 0; private static final int INET_CONDITION_THRESHOLD = 50; private boolean mAirplaneMode = false; @@ -156,9 +157,9 @@ public class NetworkController extends BroadcastReceiver implements DemoMode { boolean mDataAndWifiStacked = false; public interface SignalCluster { - void setWifiIndicators(boolean visible, int strengthIcon, + void setWifiIndicators(boolean visible, int strengthIcon, boolean problem, String contentDescription); - void setMobileDataIndicators(boolean visible, int strengthIcon, + void setMobileDataIndicators(boolean visible, int strengthIcon, boolean problem, int typeIcon, String contentDescription, String typeContentDescription); void setIsAirplaneMode(boolean is, int airplaneIcon); } @@ -288,6 +289,7 @@ public class NetworkController extends BroadcastReceiver implements DemoMode { // only show wifi in the cluster if connected or if wifi-only mWifiEnabled && (mWifiConnected || !mHasMobileDataFeature), mWifiIconId, + mInetCondition == 0, mContentDescriptionWifi); if (mIsWimaxEnabled && mWimaxConnected) { @@ -295,6 +297,7 @@ public class NetworkController extends BroadcastReceiver implements DemoMode { cluster.setMobileDataIndicators( true, mAlwaysShowCdmaRssi ? mPhoneSignalIconId : mWimaxIconId, + mInetCondition == 0, mDataTypeIconId, mContentDescriptionWimax, mContentDescriptionDataType); @@ -303,6 +306,7 @@ public class NetworkController extends BroadcastReceiver implements DemoMode { cluster.setMobileDataIndicators( mHasMobileDataFeature, mShowPhoneRSSIForData ? mPhoneSignalIconId : mDataSignalIconId, + mInetCondition == 0, mDataTypeIconId, mContentDescriptionPhoneSignal, mContentDescriptionDataType); @@ -1145,6 +1149,7 @@ public class NetworkController extends BroadcastReceiver implements DemoMode { if (mLastPhoneSignalIconId != mPhoneSignalIconId || mLastWifiIconId != mWifiIconId + || mLastInetCondition != mInetCondition || mLastWimaxIconId != mWimaxIconId || mLastDataTypeIconId != mDataTypeIconId || mLastAirplaneMode != mAirplaneMode @@ -1179,6 +1184,10 @@ public class NetworkController extends BroadcastReceiver implements DemoMode { mLastWifiIconId = mWifiIconId; } + if (mLastInetCondition != mInetCondition) { + mLastInetCondition = mInetCondition; + } + // the wimax icon on phones if (mLastWimaxIconId != mWimaxIconId) { mLastWimaxIconId = mWimaxIconId; @@ -1424,6 +1433,7 @@ public class NetworkController extends BroadcastReceiver implements DemoMode { cluster.setWifiIndicators( show, iconId, + mDemoInetCondition == 0, "Demo"); } } @@ -1456,6 +1466,7 @@ public class NetworkController extends BroadcastReceiver implements DemoMode { cluster.setMobileDataIndicators( show, iconId, + mDemoInetCondition == 0, mDemoDataTypeIconId, "Demo", "Demo"); diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java index 6e025bb1541d..c6972b118d6f 100644 --- a/policy/src/com/android/internal/policy/impl/GlobalActions.java +++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java @@ -268,7 +268,7 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac if (Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.BUGREPORT_IN_POWER_MENU, 0) != 0 && isCurrentUserOwner()) { mItems.add( - new SinglePressAction(com.android.internal.R.drawable.stat_sys_adb, + new SinglePressAction(com.android.internal.R.drawable.ic_lock_bugreport, R.string.global_action_bug_report) { public void onPress() { diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java index f1db904c6c26..79ed8667af99 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java @@ -25,6 +25,8 @@ import static android.view.WindowManager.LayoutParams.*; import android.animation.Animator; import android.animation.ObjectAnimator; import android.app.ActivityOptions; +import android.os.Looper; +import android.transition.Fade; import android.transition.Scene; import android.transition.Transition; import android.transition.TransitionInflater; @@ -109,7 +111,6 @@ import android.widget.TextView; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collection; -import java.util.List; import java.util.Map; /** @@ -253,8 +254,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { private ActivityOptions mActivityOptions; private SceneTransitionListener mSceneTransitionListener; - private boolean mTriggerEarly = true; + private boolean mAllowEnterOverlap = true; + private boolean mAllowExitOverlap = true; private Map<String, String> mSharedElementsMap; + private ArrayList<View> mTransitioningViews; static class WindowManagerHolder { static final IWindowManager sWindowManager = IWindowManager.Stub.asInterface( @@ -4081,14 +4084,26 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } @Override - public void setTransitionOptions(ActivityOptions options, SceneTransitionListener listener) { + public void setTransitionOptions(Bundle options, SceneTransitionListener listener) { mSceneTransitionListener = listener; - mActivityOptions = options; + ActivityOptions activityOptions = null; + if (options != null) { + activityOptions = new ActivityOptions(options); + if (activityOptions.getAnimationType() != ActivityOptions.ANIM_SCENE_TRANSITION) { + activityOptions = null; + } + } + mActivityOptions = activityOptions; + } + + @Override + public void setAllowOverlappingEnterTransition(boolean allow) { + mAllowEnterOverlap = allow; } @Override - public void setTriggerEarlyEnterTransition(boolean triggerEarly) { - mTriggerEarly = triggerEarly; + public void setAllowOverlappingExitTransition(boolean allow) { + mAllowExitOverlap = allow; } @Override @@ -4097,7 +4112,50 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } @Override - public Bundle startExitTransition(ActivityOptions activityOptions) { + public void restoreViewVisibilityAfterTransitionToCallee() { + if (mTransitioningViews != null) { + setViewVisibility(mTransitioningViews, View.VISIBLE); + } + } + + @Override + public void startExitTransitionToCaller(final Runnable onTransitionEnd) { + Transition transition; + if (mContentScene == null || mTransitionManager == null || mActivityOptions == null + || (transition = mTransitionManager.getEnterTransition(mContentScene)) == null) { + onTransitionEnd.run(); + return; + } + if (mAllowExitOverlap) { + TransitionSet transitionSet = new TransitionSet(); + transitionSet.addTransition(transition); + Fade fade = new Fade(); + transitionSet.addTransition(fade); + transition = transitionSet; + } + + final ArrayMap<String, View> sharedElements = new ArrayMap<String, View>(); + mapSharedElements(sharedElements); + final Bundle sharedElementArgs = new Bundle(); + captureTerminalSharedElementState(sharedElements, sharedElementArgs); + + final ArrayList<View> transitioningViews = new ArrayList<View>(); + mDecor.captureTransitioningViews(transitioningViews); + transitioningViews.removeAll(sharedElements.values()); + + mSceneTransitionListener.convertToTranslucent(); + transition = transition.clone(); + Rect epicenter = calcEpicenter(sharedElements, mActivityOptions.getSharedElementNames()); + transition.setEpicenterCallback(new FixedEpicenterCallback(epicenter)); + ExitSceneBack exitScene = + new ExitSceneBack(onTransitionEnd, sharedElementArgs, sharedElements.values()); + exitScene.start(transition); + mTransitionManager.beginDelayedTransition(mDecor, transition); + setViewVisibility(transitioningViews, View.INVISIBLE); + } + + @Override + public Bundle startExitTransitionToCallee(Bundle options) { if (mContentScene == null) { return null; } @@ -4106,16 +4164,15 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { return null; } + ActivityOptions activityOptions = new ActivityOptions(options); ArrayMap<String, View> sharedElements = findSharedElements(activityOptions); // Find exiting Views and shared elements - final ArrayList<View> transitioningViews = new ArrayList<View>(); - mDecor.captureTransitioningViews(transitioningViews); - transitioningViews.removeAll(sharedElements.values()); + ArrayList<View> transitioningViews = captureTransitioningViews(sharedElements.values()); - Transition exitTransition = cloneAndSetTransitionTargets(transition, + Transition exitTransition = addTransitionTargets(transition, transitioningViews, true); - Transition sharedElementTransition = cloneAndSetTransitionTargets(transition, + Transition sharedElementTransition = addTransitionTargets(transition, transitioningViews, false); // transitionSet is the total exit transition, including hero animation. @@ -4123,8 +4180,12 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { transitionSet.addTransition(exitTransition); transitionSet.addTransition(sharedElementTransition); + Rect epicenter = calcEpicenter(sharedElements, activityOptions.getSharedElementNames()); + FixedEpicenterCallback epicenterCallback = new FixedEpicenterCallback(epicenter); + transitionSet.setEpicenterCallback(epicenterCallback); + updateExitActivityOptions(activityOptions, sharedElements, - sharedElementTransition, exitTransition); + sharedElementTransition, transitioningViews, exitTransition, epicenterCallback); // Start exiting the Views that need to exit TransitionManager.beginDelayedTransition(mDecor, transitionSet); @@ -4133,6 +4194,14 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { return activityOptions.toBundle(); } + private ArrayList<View> captureTransitioningViews(Collection<View> sharedElements) { + mTransitioningViews = new ArrayList<View>(); + mDecor.captureTransitioningViews(mTransitioningViews); + ArrayList<View> transitioningViews = (ArrayList<View>) mTransitioningViews.clone(); + transitioningViews.removeAll(sharedElements); + return transitioningViews; + } + private ArrayMap<String, View> findSharedElements(ActivityOptions activityOptions) { ArrayMap<String, View> sharedElements = new ArrayMap<String, View>(); mDecor.findSharedElements(sharedElements); @@ -4149,9 +4218,18 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { return sharedElements; } + private static void runOnUiThread(Handler handler, Runnable runnable) { + if (handler.getLooper() != Looper.myLooper()) { + handler.post(runnable); + } else { + runnable.run(); + } + } + private void updateExitActivityOptions(ActivityOptions activityOptions, final Map<String, View> sharedElements, Transition sharedElementTransition, - Transition exitTransition) { + final ArrayList<View> transitioningViews, Transition exitTransition, + final Transition.EpicenterCallback epicenterCallback) { // Schedule capturing of the shared element state final Bundle sharedElementArgs = new Bundle(); @@ -4159,30 +4237,88 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { ActivityOptions.SharedElementSource sharedElementSource = new ActivityOptions.SharedElementSource() { + private Handler mHandler = new Handler(); + @Override public Bundle getSharedElementExitState() { return sharedElementArgs; } @Override - public void acceptedSharedElements(ArrayList<String> sharedElementNames) { + public void acceptedSharedElements(final ArrayList<String> sharedElementNames) { if (sharedElementNames.size() == sharedElements.size()) { return; // They were all accepted } - Transition transition = mTransitionManager.getExitTransition(mContentScene).clone(); - TransitionManager.beginDelayedTransition(mDecor, transition); - for (String name: sharedElements.keySet()) { - if (!sharedElementNames.contains(name)) { - sharedElements.get(name).setVisibility(View.INVISIBLE); + runOnUiThread(mHandler, new Runnable() { + @Override + public void run() { + Transition transition = mTransitionManager.getExitTransition(mContentScene); + transition = transition.clone(); + transition.setEpicenterCallback(epicenterCallback); + TransitionManager.beginDelayedTransition(mDecor, transition); + for (String name : sharedElements.keySet()) { + if (!sharedElementNames.contains(name)) { + sharedElements.get(name).setVisibility(View.INVISIBLE); + } + } + sharedElements.keySet().retainAll(sharedElementNames); } - } - sharedElements.keySet().retainAll(sharedElementNames); + }); } @Override public void hideSharedElements() { if (sharedElements != null) { - setViewVisibility(sharedElements.values(), View.INVISIBLE); + runOnUiThread(mHandler, new Runnable() { + @Override + public void run() { + setViewVisibility(sharedElements.values(), View.INVISIBLE); + } + }); + } + } + + @Override + public void restore(final Bundle sharedElementState) { + runOnUiThread(mHandler, new Runnable() { + @Override + public void run() { + mTransitioningViews = null; + Transition transition = mTransitionManager.getExitTransition(mContentScene); + transition = transition.clone(); + transition.setEpicenterCallback(epicenterCallback); + setSharedElementState(sharedElements, sharedElementState); + setViewVisibility(sharedElements.values(), View.VISIBLE); + if (mSceneTransitionListener != null) { + mSceneTransitionListener.sharedElementStart(transition); + mDecor.getViewTreeObserver().addOnPreDrawListener( + new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + mDecor.getViewTreeObserver().removeOnPreDrawListener(this); + mSceneTransitionListener.sharedElementEnd(); + return true; + } + }); + } + TransitionManager.beginDelayedTransition(mDecor, transition); + setViewVisibility(transitioningViews, View.VISIBLE); + for (View sharedElement: sharedElements.values()) { + sharedElement.requestLayout(); + } + } + }); + } + + @Override + public void prepareForRestore() { + if (mTransitioningViews != null) { + runOnUiThread(mHandler, new Runnable() { + @Override + public void run() { + setViewVisibility(mTransitioningViews, View.INVISIBLE); + } + }); } } }; @@ -4207,22 +4343,18 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { }); } - private static Transition cloneAndSetTransitionTargets(Transition transition, - List<View> views, boolean add) { - transition = transition.clone(); - if (!transition.getTargetIds().isEmpty() || !transition.getTargets().isEmpty()) { - TransitionSet set = new TransitionSet(); - set.addTransition(transition); - transition = set; - } + private static Transition addTransitionTargets(Transition transition, Collection<View> views, + boolean add) { + TransitionSet set = new TransitionSet(); + set.addTransition(transition.clone()); for (View view: views) { if (add) { - transition.addTarget(view); + set.addTarget(view); } else { - transition.excludeTarget(view, true); + set.excludeTarget(view, true); } } - return transition; + return set; } private static void setViewVisibility(Collection<View> views, int visibility) { @@ -4231,6 +4363,14 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } } + private static void setSharedElementState(Map<String, View> sharedElements, + Bundle sharedElementState) { + int[] tempLoc = new int[2]; + for (Map.Entry<String, View> entry: sharedElements.entrySet()) { + setSharedElementState(entry.getValue(), entry.getKey(), sharedElementState, tempLoc); + } + } + /** * Sets the captured values from a previous * {@link #captureSharedElementState(android.view.View, String, android.os.Bundle, int[])} @@ -4247,23 +4387,27 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { return; } - int x = sharedElementBundle.getInt(KEY_SCREEN_X); - view.getLocationOnScreen(tempLoc); - int offsetX = x - tempLoc[0]; - view.offsetLeftAndRight(offsetX); + float z = sharedElementBundle.getFloat(KEY_TRANSLATION_Z); + view.setTranslationZ(z); + int x = sharedElementBundle.getInt(KEY_SCREEN_X); + int y = sharedElementBundle.getInt(KEY_SCREEN_Y); int width = sharedElementBundle.getInt(KEY_WIDTH); - view.setRight(view.getLeft() + width); + int height = sharedElementBundle.getInt(KEY_HEIGHT); - int y = sharedElementBundle.getInt(KEY_SCREEN_Y); - int offsetY = y - tempLoc[1]; - view.offsetTopAndBottom(offsetY); + int widthSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY); + int heightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY); + view.measure(widthSpec, heightSpec); - int height = sharedElementBundle.getInt(KEY_HEIGHT); - view.setBottom(view.getTop() + height); + ViewGroup parent = (ViewGroup) view.getParent(); + parent.getLocationOnScreen(tempLoc); + int left = x - tempLoc[0]; + int top = y - tempLoc[1]; + int right = left + width; + int bottom = top + height; + view.layout(left, top, right, bottom); - float z = sharedElementBundle.getFloat(KEY_TRANSLATION_Z); - view.setTranslationZ(z); + view.requestLayout(); } /** @@ -4297,6 +4441,124 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { transitionArgs.putBundle(name, sharedElementBundle); } + private void mapSharedElements(ArrayMap<String, View> sharedElements) { + ArrayList<String> sharedElementNames = mActivityOptions.getSharedElementNames(); + if (sharedElementNames != null) { + mDecor.findSharedElements(sharedElements); + if (mSharedElementsMap != null) { + for (Map.Entry<String, String> entry : mSharedElementsMap.entrySet()) { + View sharedElement = sharedElements.remove(entry.getValue()); + if (sharedElement != null) { + sharedElements.put(entry.getKey(), sharedElement); + } + } + } + sharedElements.keySet().retainAll(sharedElementNames); + } + } + + private static Rect calcEpicenter(ArrayMap<String, View> sharedElements, + ArrayList<String> sharedElementNames) { + if (sharedElementNames != null) { + for (String name: sharedElementNames) { + if (name.startsWith("android:")) { + return null; + } + View view = sharedElements.get(name); + if (view != null) { + int[] loc = new int[2]; + view.getLocationOnScreen(loc); + int left = loc[0] + Math.round(view.getTranslationX()); + int top = loc[1] + Math.round(view.getTranslationY()); + int right = left + view.getWidth(); + int bottom = top + view.getHeight(); + return new Rect(left, top, right, bottom); + } + } + } + return null; + } + + private class ExitSceneBack extends Transition.TransitionListenerAdapter implements + Animator.AnimatorListener { + private boolean mExitTransitionComplete; + private boolean mBackgroundFadeComplete; + private boolean mOnCompleteExecuted; + private boolean mSharedElementTransitioned; + private Runnable mOnComplete; + private Bundle mSharedElementArgs; + private Collection<View> mSharedElements; + + public ExitSceneBack(Runnable onComplete, Bundle sharedElementArgs, + Collection<View> sharedElements) { + mOnComplete = onComplete; + mSharedElementArgs = sharedElementArgs; + mSharedElements = sharedElements; + } + + public void start(Transition exitTransition) { + if (mActivityOptions != null) { + mActivityOptions.dispatchPrepareRestore(); + } + exitTransition.addListener(this); + Drawable background = mDecor.getBackground(); + if (background != null) { + ObjectAnimator animator = ObjectAnimator.ofInt(background, "alpha", 0); + animator.addListener(this); + animator.start(); + } else { + mBackgroundFadeComplete = true; + startCalledActivityEnter(); + } + } + + @Override + public void onTransitionEnd(Transition transition) { + transition.removeListener(this); + mExitTransitionComplete = true; + notifyComplete(); + if (!mAllowExitOverlap) { + startCalledActivityEnter(); + } + } + + private void notifyComplete() { + if (mExitTransitionComplete && mBackgroundFadeComplete + && mSharedElementTransitioned && !mOnCompleteExecuted) { + mOnComplete.run(); + mSceneTransitionListener.nullPendingTransition(); + mOnCompleteExecuted = true; + } + } + + @Override + public void onAnimationStart(Animator animation) { + } + + @Override + public void onAnimationEnd(Animator animation) { + mBackgroundFadeComplete = true; + if (mAllowExitOverlap) { + startCalledActivityEnter(); + } + } + + @Override + public void onAnimationCancel(Animator animation) { + } + + @Override + public void onAnimationRepeat(Animator animation) { + } + + private void startCalledActivityEnter() { + mActivityOptions.dispatchRestore(mSharedElementArgs); + setViewVisibility(mSharedElements, View.INVISIBLE); + mSharedElementTransitioned = true; + notifyComplete(); + } + } + /** * Provides code for handling the Activity transition entering scene. * When the first scene is laid out (onPreDraw), it makes views invisible. @@ -4317,6 +4579,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { private boolean mEnterTransitionStarted; private ArrayMap<String, View> mSharedElementTargets = new ArrayMap<String, View>(); private ArrayList<View> mEnteringViews = new ArrayList<View>(); + private Transition.EpicenterCallback mEpicenterCallback; public EnterScene() { mSceneTransitionListener.nullPendingTransition(); @@ -4335,33 +4598,25 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (!mEnterTransitionStarted && mSceneTransitionListener != null) { mEnterTransitionStarted = true; mDecor.captureTransitioningViews(mEnteringViews); - ArrayList<String> sharedElementNames = mActivityOptions.getSharedElementNames(); - if (sharedElementNames != null) { - mDecor.findSharedElements(mSharedElementTargets); - if (mSharedElementsMap != null) { - for (Map.Entry<String, String> entry : mSharedElementsMap.entrySet()) { - View sharedElement = mSharedElementTargets.remove(entry.getValue()); - if (sharedElement != null) { - mSharedElementTargets.put(entry.getKey(), sharedElement); - } - } - } - mSharedElementTargets.keySet().retainAll(sharedElementNames); - mEnteringViews.removeAll(mSharedElementTargets.values()); - } + mapSharedElements(mSharedElementTargets); + mEnteringViews.removeAll(mSharedElementTargets.values()); + Rect epicenter = calcEpicenter(mSharedElementTargets, + mActivityOptions.getSharedElementNames()); + mEpicenterCallback = new FixedEpicenterCallback(epicenter); setViewVisibility(mEnteringViews, View.INVISIBLE); setViewVisibility(mSharedElementTargets.values(), View.INVISIBLE); - if (mTriggerEarly) { + if (mAllowEnterOverlap) { beginEnterScene(); } observer.addOnPreDrawListener(this); + return false; } else { mHandler.postDelayed(this, MAX_TRANSITION_START_WAIT); mActivityOptions.dispatchSceneTransitionStarted(this, new ArrayList<String>(mSharedElementTargets.keySet())); + return !mSharedElementReadyReceived; } - return true; } public void start() { @@ -4375,43 +4630,48 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } @Override - public void sharedElementTransitionComplete(Bundle transitionArgs) { + public void sharedElementTransitionComplete(final Bundle transitionArgs) { if (!mSharedElementReadyReceived) { mSharedElementReadyReceived = true; mHandler.removeCallbacks(this); mHandler.postDelayed(this, MAX_TRANSITION_FINISH_WAIT); if (!mSharedElementTargets.isEmpty()) { - Transition transition = getTransitionManager().getEnterTransition( - mContentScene); - if (transition == null) { - transition = TransitionManager.getDefaultTransition(); - } - transition = transition.clone(); - if (transitionArgs == null) { - TransitionManager.beginDelayedTransition(mDecor, transition); - setViewVisibility(mSharedElementTargets.values(), View.VISIBLE); - } else { - int[] tempLoc = new int[2]; - for (Map.Entry<String, View> entry: mSharedElementTargets.entrySet()) { - setSharedElementState(entry.getValue(), entry.getKey(), transitionArgs, - tempLoc); + runOnUiThread(mHandler, new Runnable() { + @Override + public void run() { + Transition transition = getTransitionManager().getEnterTransition( + mContentScene); + if (transition == null) { + transition = TransitionManager.getDefaultTransition(); + } + transition = addTransitionTargets(transition, + mSharedElementTargets.values(), + true); + transition.setEpicenterCallback(mEpicenterCallback); + if (transitionArgs == null) { + TransitionManager.beginDelayedTransition(mDecor, transition); + setViewVisibility(mSharedElementTargets.values(), View.VISIBLE); + } else { + mSceneTransitionListener.sharedElementStart(transition); + setSharedElementState(mSharedElementTargets, transitionArgs); + setViewVisibility(mSharedElementTargets.values(), View.VISIBLE); + mDecor.getViewTreeObserver().addOnPreDrawListener( + new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + mDecor.getViewTreeObserver() + .removeOnPreDrawListener(this); + mSceneTransitionListener.sharedElementEnd(); + mActivityOptions.dispatchSharedElementsReady(); + return true; + } + }); + TransitionManager.beginDelayedTransition(mDecor, transition); + } } - setViewVisibility(mSharedElementTargets.values(), View.VISIBLE); - mSceneTransitionListener.sharedElementStart(transition); - mDecor.getViewTreeObserver().addOnPreDrawListener( - new ViewTreeObserver.OnPreDrawListener() { - @Override - public boolean onPreDraw() { - mDecor.getViewTreeObserver().removeOnPreDrawListener(this); - mSceneTransitionListener.sharedElementEnd(); - mActivityOptions.dispatchSharedElementsReady(); - return true; - } - }); - TransitionManager.beginDelayedTransition(mDecor, transition); - } + }); } - if (mTriggerEarly) { + if (mAllowEnterOverlap) { fadeInBackground(); } } @@ -4436,9 +4696,14 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { mAllDone = true; sharedElementTransitionComplete(null); mHandler.removeCallbacks(this); - if (!mTriggerEarly) { - beginEnterScene(); - fadeInBackground(); + if (!mAllowEnterOverlap) { + runOnUiThread(mHandler, new Runnable() { + @Override + public void run() { + beginEnterScene(); + fadeInBackground(); + } + }); } } @@ -4462,10 +4727,25 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { private void beginEnterScene() { Transition transition = getTransitionManager().getEnterTransition(mContentScene); if (transition == null) { - transition = TransitionManager.getDefaultTransition().clone(); + transition = TransitionManager.getDefaultTransition(); } + transition = addTransitionTargets(transition, mEnteringViews, true); + transition.setEpicenterCallback(mEpicenterCallback); TransitionManager.beginDelayedTransition(mDecor, transition); setViewVisibility(mEnteringViews, View.VISIBLE); } } + + private static class FixedEpicenterCallback extends Transition.EpicenterCallback { + private Rect mEpicenter; + + public FixedEpicenterCallback(Rect epicenter) { + mEpicenter = epicenter; + } + + @Override + public Rect getEpicenter(Transition transition) { + return mEpicenter; + } + }; } diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java index 57c2f929bb20..0b688b6fbf9f 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerService.java +++ b/services/backup/java/com/android/server/backup/BackupManagerService.java @@ -4690,7 +4690,11 @@ public class BackupManagerService extends IBackupManager.Stub { // ----- Restore handling ----- - private boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) { + static boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) { + if (target == null) { + return false; + } + // If the target resides on the system partition, we allow it to restore // data from the like-named package in a restore set even if the signatures // do not match. (Unlike general applications, those flashed to the system diff --git a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java index 495da886fa65..39f2441f3b55 100644 --- a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java +++ b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java @@ -19,15 +19,19 @@ package com.android.server.backup; import android.app.backup.BackupAgent; import android.app.backup.BackupDataInput; import android.app.backup.BackupDataOutput; +import android.content.ComponentName; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.Signature; import android.os.Build; import android.os.ParcelFileDescriptor; import android.util.Slog; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; @@ -42,6 +46,8 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.Objects; + /** * We back up the signatures of each package so that during a system restore, * we can verify that the app whose data we think we have matches the app @@ -58,6 +64,9 @@ public class PackageManagerBackupAgent extends BackupAgent { // is stored using the package name as a key) private static final String GLOBAL_METADATA_KEY = "@meta@"; + // key under which we store the identity of the user's chosen default home app + private static final String DEFAULT_HOME_KEY = "@home@"; + private List<PackageInfo> mAllPackages; private PackageManager mPackageManager; // version & signature info of each app in a restore set @@ -68,7 +77,15 @@ public class PackageManagerBackupAgent extends BackupAgent { private final HashSet<String> mExisting = new HashSet<String>(); private int mStoredSdkVersion; private String mStoredIncrementalVersion; + private ComponentName mStoredHomeComponent; + private long mStoredHomeVersion; + private Signature[] mStoredHomeSigs; + private boolean mHasMetadata; + private ComponentName mRestoredHome; + private long mRestoredHomeVersion; + private String mRestoredHomeInstaller; + private Signature[] mRestoredHomeSignatures; public class Metadata { public int versionCode; @@ -136,7 +153,50 @@ public class PackageManagerBackupAgent extends BackupAgent { mExisting.clear(); } + long homeVersion = 0; + Signature[] homeSigs = null; + PackageInfo homeInfo = null; + String homeInstaller = null; + ComponentName home = getPreferredHomeComponent(); + if (home != null) { + try { + homeInfo = mPackageManager.getPackageInfo(home.getPackageName(), + PackageManager.GET_SIGNATURES); + homeInstaller = mPackageManager.getInstallerPackageName(home.getPackageName()); + homeVersion = homeInfo.versionCode; + homeSigs = homeInfo.signatures; + } catch (NameNotFoundException e) { + Slog.w(TAG, "Can't access preferred home info"); + // proceed as though there were no preferred home set + home = null; + } + } + try { + // We need to push a new preferred-home-app record if: + // 1. the version of the home app has changed since our last backup; + // 2. the home app [or absence] we now use differs from the prior state, + // OR 3. it looks like we use the same home app + version as before, but + // the signatures don't match so we treat them as different apps. + final boolean needHomeBackup = (homeVersion != mStoredHomeVersion) + || Objects.equals(home, mStoredHomeComponent) + || (home != null + && !BackupManagerService.signaturesMatch(mStoredHomeSigs, homeInfo)); + if (needHomeBackup) { + if (DEBUG) { + Slog.i(TAG, "Home preference changed; backing up new state " + home); + } + if (home != null) { + outputBufferStream.writeUTF(home.flattenToString()); + outputBufferStream.writeLong(homeVersion); + outputBufferStream.writeUTF(homeInstaller); + writeSignatureArray(outputBufferStream, homeSigs); + writeEntity(data, DEFAULT_HOME_KEY, outputBuffer.toByteArray()); + } else { + data.writeEntityHeader(DEFAULT_HOME_KEY, -1); + } + } + /* * Global metadata: * @@ -146,6 +206,7 @@ public class PackageManagerBackupAgent extends BackupAgent { * String incremental -- the incremental release name of the OS stored in * the backup set. */ + outputBuffer.reset(); if (!mExisting.contains(GLOBAL_METADATA_KEY)) { if (DEBUG) Slog.v(TAG, "Storing global metadata key"); outputBufferStream.writeInt(Build.VERSION.SDK_INT); @@ -238,7 +299,7 @@ public class PackageManagerBackupAgent extends BackupAgent { } // Finally, write the new state blob -- just the list of all apps we handled - writeStateFile(mAllPackages, newState); + writeStateFile(mAllPackages, home, homeVersion, homeSigs, newState); } private static void writeEntity(BackupDataOutput data, String key, byte[] bytes) @@ -286,6 +347,19 @@ public class PackageManagerBackupAgent extends BackupAgent { + " (" + mStoredIncrementalVersion + " vs " + Build.VERSION.INCREMENTAL + ")"); } + } else if (key.equals(DEFAULT_HOME_KEY)) { + String cn = inputBufferStream.readUTF(); + mRestoredHome = ComponentName.unflattenFromString(cn); + mRestoredHomeVersion = inputBufferStream.readLong(); + mRestoredHomeInstaller = inputBufferStream.readUTF(); + mRestoredHomeSignatures = readSignatureArray(inputBufferStream); + if (DEBUG) { + Slog.i(TAG, " read preferred home app " + mRestoredHome + + " version=" + mRestoredHomeVersion + + " installer=" + mRestoredHomeVersion + + " sig=" + mRestoredHomeVersion); + } + } else { // it's a file metadata record int versionCode = inputBufferStream.readInt(); @@ -365,18 +439,34 @@ public class PackageManagerBackupAgent extends BackupAgent { mStateVersions.clear(); mStoredSdkVersion = 0; mStoredIncrementalVersion = null; + mStoredHomeComponent = null; + mStoredHomeVersion = 0; + mStoredHomeSigs = null; // The state file is just the list of app names we have stored signatures for // with the exception of the metadata block, to which is also appended the // version numbers corresponding with the last time we wrote this PM block. // If they mismatch the current system, we'll re-store the metadata key. FileInputStream instream = new FileInputStream(stateFile.getFileDescriptor()); - DataInputStream in = new DataInputStream(instream); + BufferedInputStream inbuffer = new BufferedInputStream(instream); + DataInputStream in = new DataInputStream(inbuffer); - int bufSize = 256; - byte[] buf = new byte[bufSize]; try { String pkg = in.readUTF(); + + // First comes the preferred home app data, if any, headed by the DEFAULT_HOME_KEY tag + if (pkg.equals(DEFAULT_HOME_KEY)) { + // flattened component name, version, signature of the home app + mStoredHomeComponent = ComponentName.unflattenFromString(in.readUTF()); + mStoredHomeVersion = in.readLong(); + mStoredHomeSigs = readSignatureArray(in); + + pkg = in.readUTF(); // set up for the next block of state + } else { + // else no preferred home app on the ancestral device - fall through to the rest + } + + // After (possible) home app data comes the global metadata block if (pkg.equals(GLOBAL_METADATA_KEY)) { mStoredSdkVersion = in.readInt(); mStoredIncrementalVersion = in.readUTF(); @@ -386,7 +476,7 @@ public class PackageManagerBackupAgent extends BackupAgent { return; } - // The global metadata was first; now read all the apps + // The global metadata was last; now read all the apps while (true) { pkg = in.readUTF(); int versionCode = in.readInt(); @@ -401,13 +491,28 @@ public class PackageManagerBackupAgent extends BackupAgent { } } + private ComponentName getPreferredHomeComponent() { + return mPackageManager.getHomeActivities(new ArrayList<ResolveInfo>()); + } + // Util: write out our new backup state file - private void writeStateFile(List<PackageInfo> pkgs, ParcelFileDescriptor stateFile) { + private void writeStateFile(List<PackageInfo> pkgs, ComponentName preferredHome, + long homeVersion, Signature[] homeSignatures, ParcelFileDescriptor stateFile) { FileOutputStream outstream = new FileOutputStream(stateFile.getFileDescriptor()); - DataOutputStream out = new DataOutputStream(outstream); + BufferedOutputStream outbuf = new BufferedOutputStream(outstream); + DataOutputStream out = new DataOutputStream(outbuf); + // by the time we get here we know we've done all our backing up try { - // by the time we get here we know we've stored the global metadata record + // If we remembered a preferred home app, record that + if (preferredHome != null) { + out.writeUTF(DEFAULT_HOME_KEY); + out.writeUTF(preferredHome.flattenToString()); + out.writeLong(homeVersion); + writeSignatureArray(out, homeSignatures); + } + + // Conclude with the metadata block out.writeUTF(GLOBAL_METADATA_KEY); out.writeInt(Build.VERSION.SDK_INT); out.writeUTF(Build.VERSION.INCREMENTAL); @@ -417,9 +522,10 @@ public class PackageManagerBackupAgent extends BackupAgent { out.writeUTF(pkg.packageName); out.writeInt(pkg.versionCode); } + + out.flush(); } catch (IOException e) { Slog.e(TAG, "Unable to write package manager state file!"); - return; } } } diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags index 0f78c9bd7eaf..976893475b60 100644 --- a/services/core/java/com/android/server/EventLogTags.logtags +++ b/services/core/java/com/android/server/EventLogTags.logtags @@ -58,7 +58,11 @@ option java_package com.android.server 2751 notification_cancel (uid|1|5),(pid|1|5),(pkg|3),(id|1|5),(tag|3),(userid|1|5),(required_flags|1),(forbidden_flags|1),(reason|1|5),(listener|3) # when someone tries to cancel all of the notifications for a particular package 2752 notification_cancel_all (uid|1|5),(pid|1|5),(pkg|3),(userid|1|5),(required_flags|1),(forbidden_flags|1),(reason|1|5),(listener|3) - +# when the notification panel is shown +# Note: New tag range starts here since 2753+ have been used below. +27500 notification_panel_revealed +# when the notification panel is hidden +27501 notification_panel_hidden # --------------------------- # Watchdog.java diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java index 94f699fccd65..f59edc7f83e3 100644 --- a/services/core/java/com/android/server/UiModeManagerService.java +++ b/services/core/java/com/android/server/UiModeManagerService.java @@ -71,6 +71,7 @@ final class UiModeManagerService extends SystemService { private boolean mCarModeKeepsScreenOn; private boolean mDeskModeKeepsScreenOn; private boolean mTelevision; + private boolean mWatch; private boolean mComputedNightMode; int mCurUiMode = 0; @@ -176,6 +177,7 @@ final class UiModeManagerService extends SystemService { PackageManager.FEATURE_TELEVISION) || context.getPackageManager().hasSystemFeature( PackageManager.FEATURE_LEANBACK); + mWatch = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH); mNightMode = Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.UI_NIGHT_MODE, UiModeManager.MODE_NIGHT_AUTO); @@ -339,8 +341,12 @@ final class UiModeManagerService extends SystemService { } private void updateConfigurationLocked() { - int uiMode = mTelevision ? Configuration.UI_MODE_TYPE_TELEVISION : mDefaultUiModeType; - if (mCarModeEnabled) { + int uiMode = mDefaultUiModeType; + if (mTelevision) { + uiMode = Configuration.UI_MODE_TYPE_TELEVISION; + } else if (mWatch) { + uiMode = Configuration.UI_MODE_TYPE_WATCH; + } else if (mCarModeEnabled) { uiMode = Configuration.UI_MODE_TYPE_CAR; } else if (isDeskDockState(mDockState)) { uiMode = Configuration.UI_MODE_TYPE_DESK; diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 33b12c571671..d596472b5eb5 100644..100755 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -522,13 +522,6 @@ final class ActivityRecord { if (fullscreen == toOpaque) { return false; } - AttributeCache.Entry ent = - AttributeCache.instance().get(packageName, realTheme, styleable.Window, userId); - if (ent == null - || !ent.array.getBoolean(styleable.Window_windowIsTranslucent, false) - || ent.array.getBoolean(styleable.Window_windowIsFloating, false)) { - return false; - } // Keep track of the number of fullscreen activities in this task. task.numFullscreen += toOpaque ? +1 : -1; @@ -956,8 +949,8 @@ final class ActivityRecord { // for another app to start, then we have paused dispatching // for this activity. ActivityRecord r = this; - final ActivityStack stack = task.stack; if (r.waitingVisible) { + final ActivityStack stack = mStackSupervisor.getFocusedStack(); // Hmmm, who might we be waiting for? r = stack.mResumedActivity; if (r == null) { diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java index 4c8dcc363689..e0591a284f09 100644 --- a/services/core/java/com/android/server/notification/NotificationDelegate.java +++ b/services/core/java/com/android/server/notification/NotificationDelegate.java @@ -29,5 +29,6 @@ public interface NotificationDelegate { String pkg, String tag, int id, int uid, int initialPid, String message, int userId); void onPanelRevealed(); + void onPanelHidden(); boolean allowDisable(int what, IBinder token, String pkg); } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 3b6d2886df7f..8be2410fbeee 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -966,6 +966,7 @@ public class NotificationManagerService extends SystemService { @Override public void onPanelRevealed() { + EventLogTags.writeNotificationPanelRevealed(); synchronized (mNotificationList) { // sound mSoundNotification = null; @@ -998,6 +999,11 @@ public class NotificationManagerService extends SystemService { } @Override + public void onPanelHidden() { + EventLogTags.writeNotificationPanelHidden(); + } + + @Override public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id, int uid, int initialPid, String message, int userId) { Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id @@ -1915,8 +1921,8 @@ public class NotificationManagerService extends SystemService { mNotificationList.add(r); mUsageStats.registerPostedByApp(r); } else { - old = mNotificationList.remove(index); - mNotificationList.add(index, r); + old = mNotificationList.get(index); + mNotificationList.set(index, r); mUsageStats.registerUpdatedByApp(r); // Make sure we don't lose the foreground service state. if (old != null) { diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 80c3c8e6483d..b85a50608f68 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -406,6 +406,7 @@ public final class PowerManagerService extends com.android.server.SystemService private static native void nativeReleaseSuspendBlocker(String name); private static native void nativeSetInteractive(boolean enable); private static native void nativeSetAutoSuspend(boolean enable); + private static native void nativeSendPowerHint(int hintId, int data); public PowerManagerService(Context context) { super(context); @@ -2548,6 +2549,12 @@ public final class PowerManagerService extends com.android.server.SystemService } @Override // Binder call + public void powerHint(int hintId, int data) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); + nativeSendPowerHint(hintId, data); + } + + @Override // Binder call public void acquireWakeLock(IBinder lock, int flags, String tag, String packageName, WorkSource ws, String historyTag) { if (lock == null) { diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 4ce02c1d07c7..e4b5f3ae0d41 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -535,6 +535,17 @@ public class StatusBarManagerService extends IStatusBarService.Stub } @Override + public void onPanelHidden() throws RemoteException { + enforceStatusBarService(); + long identity = Binder.clearCallingIdentity(); + try { + mNotificationDelegate.onPanelHidden(); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override public void onNotificationClick(String pkg, String tag, int id, int userId) { enforceStatusBarService(); final int callingUid = Binder.getCallingUid(); diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index e1069aecaeb4..b3247ff2f94f 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -150,7 +150,7 @@ static void loadSystemIconAsSprite(JNIEnv* env, jobject contextObj, int32_t styl status_t status = android_view_PointerIcon_loadSystemIcon(env, contextObj, style, &pointerIcon); if (!status) { - pointerIcon.bitmap.copyTo(&outSpriteIcon->bitmap, SkBitmap::kARGB_8888_Config); + pointerIcon.bitmap.copyTo(&outSpriteIcon->bitmap, kNative_8888_SkColorType); outSpriteIcon->hotSpotX = pointerIcon.hotSpotX; outSpriteIcon->hotSpotY = pointerIcon.hotSpotY; } @@ -1297,7 +1297,7 @@ static void nativeVibrate(JNIEnv* env, size_t patternSize = env->GetArrayLength(patternObj); if (patternSize > MAX_VIBRATE_PATTERN_SIZE) { - ALOGI("Skipped requested vibration because the pattern size is %d " + ALOGI("Skipped requested vibration because the pattern size is %zu " "which is more than the maximum supported size of %d.", patternSize, MAX_VIBRATE_PATTERN_SIZE); return; // limit to reasonable size diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp index 151e134ee3ba..dbf5439a9aff 100644 --- a/services/core/jni/com_android_server_power_PowerManagerService.cpp +++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp @@ -189,6 +189,18 @@ static void nativeSetAutoSuspend(JNIEnv *env, jclass clazz, jboolean enable) { } } +static void nativeSendPowerHint(JNIEnv *env, jclass clazz, jint hintId, jint data) { + int data_param = data; + + if (gPowerModule && gPowerModule->powerHint) { + if(data) + gPowerModule->powerHint(gPowerModule, (power_hint_t)hintId, &data_param); + else { + gPowerModule->powerHint(gPowerModule, (power_hint_t)hintId, NULL); + } + } +} + // ---------------------------------------------------------------------------- static JNINativeMethod gPowerManagerServiceMethods[] = { @@ -205,6 +217,8 @@ static JNINativeMethod gPowerManagerServiceMethods[] = { (void*) nativeSetInteractive }, { "nativeSetAutoSuspend", "(Z)V", (void*) nativeSetAutoSuspend }, + { "nativeSendPowerHint", "(II)V", + (void*) nativeSendPowerHint }, }; #define FIND_CLASS(var, className) \ diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp index 38bfa00d7564..e0dab785bc47 100644 --- a/tools/aapt/AaptAssets.cpp +++ b/tools/aapt/AaptAssets.cpp @@ -1342,6 +1342,11 @@ bool AaptGroupEntry::getUiModeTypeName(const char* name, (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) | ResTable_config::UI_MODE_TYPE_APPLIANCE; return true; + } else if (strcmp(name, "watch") == 0) { + if (out) out->uiMode = + (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) + | ResTable_config::UI_MODE_TYPE_WATCH; + return true; } return false; diff --git a/tools/layoutlib/bridge/src/android/content/res/AssetManager_Delegate.java b/tools/layoutlib/bridge/src/android/content/res/AssetManager_Delegate.java new file mode 100644 index 000000000000..914a359e3171 --- /dev/null +++ b/tools/layoutlib/bridge/src/android/content/res/AssetManager_Delegate.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.res; + +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; + +/** + * Delegate used to provide implementation of a select few native methods of {@link AssetManager} + * <p/> + * Through the layoutlib_create tool, the original native methods of AssetManager have been replaced + * by calls to methods of the same name in this delegate class. + * + */ +public class AssetManager_Delegate { + + @LayoutlibDelegate + /*package*/ static long newTheme(AssetManager manager) { + return Resources_Theme_Delegate.getDelegateManager() + .addNewDelegate(new Resources_Theme_Delegate()); + } + + @LayoutlibDelegate + /*package*/ static void deleteTheme(AssetManager manager, long theme) { + Resources_Theme_Delegate.getDelegateManager().removeJavaReferenceFor(theme); + } + + @LayoutlibDelegate + /*package*/ static void applyThemeStyle(long theme, int styleRes, boolean force) { + Resources_Theme_Delegate.getDelegateManager().getDelegate(theme).force = force; + } +} diff --git a/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java b/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java index c9d615c6ec6f..31d1594edc0a 100644 --- a/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java +++ b/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java @@ -16,7 +16,13 @@ package android.content.res; +import com.android.annotations.Nullable; +import com.android.ide.common.rendering.api.ResourceReference; +import com.android.ide.common.rendering.api.StyleResourceValue; +import com.android.layoutlib.bridge.android.BridgeContext; +import com.android.layoutlib.bridge.impl.DelegateManager; import com.android.layoutlib.bridge.impl.RenderSessionImpl; +import com.android.resources.ResourceType; import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import android.content.res.Resources.NotFoundException; @@ -25,7 +31,7 @@ import android.util.AttributeSet; import android.util.TypedValue; /** - * Delegate used to provide new implementation of a select few methods of {@link Resources$Theme} + * Delegate used to provide new implementation of a select few methods of {@link Resources.Theme} * * Through the layoutlib_create tool, the original methods of Theme have been replaced * by calls to methods of the same name in this delegate class. @@ -33,11 +39,28 @@ import android.util.TypedValue; */ public class Resources_Theme_Delegate { + // Whether to use the Theme.mThemeResId as primary theme. + boolean force; + + // ---- delegate manager ---- + + private static final DelegateManager<Resources_Theme_Delegate> sManager = + new DelegateManager<Resources_Theme_Delegate>(Resources_Theme_Delegate.class); + + public static DelegateManager<Resources_Theme_Delegate> getDelegateManager() { + return sManager; + } + + // ---- delegate methods. ---- + @LayoutlibDelegate /*package*/ static TypedArray obtainStyledAttributes( Resources thisResources, Theme thisTheme, int[] attrs) { - return RenderSessionImpl.getCurrentContext().obtainStyledAttributes(attrs); + boolean changed = setupResources(thisTheme); + TypedArray ta = RenderSessionImpl.getCurrentContext().obtainStyledAttributes(attrs); + restoreResources(changed); + return ta; } @LayoutlibDelegate @@ -45,15 +68,21 @@ public class Resources_Theme_Delegate { Resources thisResources, Theme thisTheme, int resid, int[] attrs) throws NotFoundException { - return RenderSessionImpl.getCurrentContext().obtainStyledAttributes(resid, attrs); + boolean changed = setupResources(thisTheme); + TypedArray ta = RenderSessionImpl.getCurrentContext().obtainStyledAttributes(resid, attrs); + restoreResources(changed); + return ta; } @LayoutlibDelegate /*package*/ static TypedArray obtainStyledAttributes( Resources thisResources, Theme thisTheme, AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes) { - return RenderSessionImpl.getCurrentContext().obtainStyledAttributes( - set, attrs, defStyleAttr, defStyleRes); + boolean changed = setupResources(thisTheme); + TypedArray ta = RenderSessionImpl.getCurrentContext().obtainStyledAttributes(set, attrs, + defStyleAttr, defStyleRes); + restoreResources(changed); + return ta; } @LayoutlibDelegate @@ -61,7 +90,45 @@ public class Resources_Theme_Delegate { Resources thisResources, Theme thisTheme, int resid, TypedValue outValue, boolean resolveRefs) { - return RenderSessionImpl.getCurrentContext().resolveThemeAttribute( + boolean changed = setupResources(thisTheme); + boolean found = RenderSessionImpl.getCurrentContext().resolveThemeAttribute( resid, outValue, resolveRefs); + restoreResources(changed); + return found; + } + + // ---- private helper methods ---- + + private static boolean setupResources(Theme thisTheme) { + Resources_Theme_Delegate themeDelegate = sManager.getDelegate(thisTheme.getNativeTheme()); + StyleResourceValue style = resolveStyle(thisTheme.getAppliedStyleResId()); + if (style != null) { + RenderSessionImpl.getCurrentContext().getRenderResources() + .applyStyle(style, themeDelegate.force); + return true; + } + return false; + } + + private static void restoreResources(boolean changed) { + if (changed) { + RenderSessionImpl.getCurrentContext().getRenderResources().clearStyles(); + } + } + + @Nullable + private static StyleResourceValue resolveStyle(int nativeResid) { + if (nativeResid == 0) { + return null; + } + BridgeContext context = RenderSessionImpl.getCurrentContext(); + ResourceReference theme = context.resolveId(nativeResid); + if (theme.isFramework()) { + return (StyleResourceValue) context.getRenderResources() + .getFrameworkResource(ResourceType.STYLE, theme.getName()); + } else { + return (StyleResourceValue) context.getRenderResources() + .getProjectResource(ResourceType.STYLE, theme.getName()); + } } } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java index b9294ab3c0f9..9ee2f60add7e 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java @@ -16,6 +16,7 @@ package com.android.layoutlib.bridge.android; +import com.android.annotations.Nullable; import com.android.ide.common.rendering.api.ILayoutPullParser; import com.android.ide.common.rendering.api.IProjectCallback; import com.android.ide.common.rendering.api.LayoutLog; @@ -109,7 +110,7 @@ public final class BridgeContext extends Context { // maps for dynamically generated id representing style objects (StyleResourceValue) private Map<Integer, StyleResourceValue> mDynamicIdToStyleMap; private Map<StyleResourceValue, Integer> mStyleToDynamicIdMap; - private int mDynamicIdGenerator = 0x01030000; // Base id for framework R.style + private int mDynamicIdGenerator = 0x02030000; // Base id for R.style in custom namespace // cache for TypedArray generated from IStyleResourceValue object private Map<int[], Map<Integer, TypedArray>> mTypedArrayCache; @@ -315,6 +316,11 @@ public final class BridgeContext extends Context { } } + // The base value for R.style is 0x01030000 and the custom style is 0x02030000. + // So, if the second byte is 03, it's probably a style. + if ((id >> 16 & 0xFF) == 0x03) { + return getStyleByDynamicId(id); + } return null; } @@ -455,7 +461,10 @@ public final class BridgeContext extends Context { @Override public final TypedArray obtainStyledAttributes(int[] attrs) { - return createStyleBasedTypedArray(mRenderResources.getCurrentTheme(), attrs); + // No style is specified here, so create the typed array based on the default theme + // and the styles already applied to it. A null value of style indicates that the default + // theme should be used. + return createStyleBasedTypedArray(null, attrs); } @Override @@ -723,11 +732,13 @@ public final class BridgeContext extends Context { /** * Creates a {@link BridgeTypedArray} by filling the values defined by the int[] with the - * values found in the given style. + * values found in the given style. If no style is specified, the default theme, along with the + * styles applied to it are used. + * * @see #obtainStyledAttributes(int, int[]) */ - private BridgeTypedArray createStyleBasedTypedArray(StyleResourceValue style, int[] attrs) - throws Resources.NotFoundException { + private BridgeTypedArray createStyleBasedTypedArray(@Nullable StyleResourceValue style, + int[] attrs) throws Resources.NotFoundException { List<Pair<String, Boolean>> attributes = searchAttrs(attrs); @@ -740,8 +751,14 @@ public final class BridgeContext extends Context { if (attribute != null) { // look for the value in the given style - ResourceValue resValue = mRenderResources.findItemInStyle(style, - attribute.getFirst(), attribute.getSecond()); + ResourceValue resValue; + if (style != null) { + resValue = mRenderResources.findItemInStyle(style, attribute.getFirst(), + attribute.getSecond()); + } else { + resValue = mRenderResources.findItemInTheme(attribute.getFirst(), + attribute.getSecond()); + } if (resValue != null) { // resolve it to make sure there are no references left. @@ -756,7 +773,6 @@ public final class BridgeContext extends Context { return ta; } - /** * The input int[] attrs is a list of attributes. The returns a list of information about * each attributes. The information is (name, isFramework) diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java index 3414b3e13db8..b9f2ed9689ff 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java @@ -51,6 +51,11 @@ public class BridgePowerManager implements IPowerManager { } @Override + public void powerHint(int hintId, int data) { + // pass for now. + } + + @Override public void crash(String arg0) throws RemoteException { // pass for now. } diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java index 42360388f53b..e0c05de6299f 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java @@ -126,6 +126,9 @@ public final class CreateInfo implements ICreateInfo { "android.content.res.Resources$Theme#obtainStyledAttributes", "android.content.res.Resources$Theme#resolveAttribute", "android.content.res.Resources#localeToLanguageTag", + "android.content.res.AssetManager#newTheme", + "android.content.res.AssetManager#deleteTheme", + "android.content.res.AssetManager#applyThemeStyle", "android.content.res.TypedArray#getValueAt", "android.graphics.BitmapFactory#finishDecode", "android.os.Handler#sendMessageAtTime", |