1515 */
1616package com .google .android .material .motion ;
1717
18- import static androidx .annotation .RestrictTo .Scope .LIBRARY_GROUP ;
19-
2018import android .animation .TimeInterpolator ;
2119import android .content .Context ;
2220import android .util .TypedValue ;
21+ import android .view .animation .AnimationUtils ;
2322import androidx .annotation .AttrRes ;
2423import androidx .annotation .NonNull ;
25- import androidx .annotation .RestrictTo ;
2624import androidx .core .graphics .PathParser ;
2725import androidx .core .view .animation .PathInterpolatorCompat ;
2826import com .google .android .material .resources .MaterialAttributes ;
2927
30- /**
31- * A utility class for motion system functions.
32- *
33- * @hide
34- */
35- @ RestrictTo (LIBRARY_GROUP )
28+ /** A utility class for motion system functions. */
3629public class MotionUtils {
3730
3831 // Constants corresponding to motionEasing* theme attr values.
@@ -43,61 +36,96 @@ public class MotionUtils {
4336
4437 private MotionUtils () {}
4538
39+ /**
40+ * Resolve a duration from a material duration theme attribute.
41+ *
42+ * @param context the context from where the theme attribute will be resolved.
43+ * @param attrResId the {@code motionDuration*} theme attribute to resolve
44+ * @param defaultDuration the duration to be returned if unable to resolve {@code attrResId}
45+ * @return the resolved {@code int} duration which {@code attrResId} points to or the {@code
46+ * defaultDuration} if resolution was unsuccessful.
47+ */
4648 public static int resolveThemeDuration (
4749 @ NonNull Context context , @ AttrRes int attrResId , int defaultDuration ) {
4850 return MaterialAttributes .resolveInteger (context , attrResId , defaultDuration );
4951 }
5052
53+ /**
54+ * Load an interpolator from a material easing theme attribute.
55+ *
56+ * @param context context from where the theme attribute will be resolved
57+ * @param attrResId the {@code motionEasing*} theme attribute to resolve
58+ * @param defaultInterpolator the interpolator to be returned if unable to resolve {@code
59+ * attrResId}.
60+ * @return the resolved {@link TimeInterpolator} which {@code attrResId} points to or the {@code
61+ * defaultInterpolator} if resolution was unsuccessful.
62+ */
5163 @ NonNull
5264 public static TimeInterpolator resolveThemeInterpolator (
5365 @ NonNull Context context ,
5466 @ AttrRes int attrResId ,
5567 @ NonNull TimeInterpolator defaultInterpolator ) {
5668 TypedValue easingValue = new TypedValue ();
57- if (context .getTheme ().resolveAttribute (attrResId , easingValue , true )) {
58- if (easingValue .type != TypedValue .TYPE_STRING ) {
59- throw new IllegalArgumentException ("Motion easing theme attribute must be a string" );
60- }
69+ if (!context .getTheme ().resolveAttribute (attrResId , easingValue , true )) {
70+ return defaultInterpolator ;
71+ }
6172
62- String easingString = String .valueOf (easingValue .string );
73+ if (easingValue .type != TypedValue .TYPE_STRING ) {
74+ throw new IllegalArgumentException (
75+ "Motion easing theme attribute must be an @interpolator resource for"
76+ + " ?attr/motionEasing*Interpolator attributes or a string for"
77+ + " ?attr/motionEasing* attributes." );
78+ }
6379
64- if (isEasingType (easingString , EASING_TYPE_CUBIC_BEZIER )) {
65- String controlPointsString = getEasingContent (easingString , EASING_TYPE_CUBIC_BEZIER );
66- String [] controlPoints = controlPointsString .split ("," );
67- if (controlPoints .length != 4 ) {
68- throw new IllegalArgumentException (
69- "Motion easing theme attribute must have 4 control points if using bezier curve"
70- + " format; instead got: "
71- + controlPoints .length );
72- }
80+ String easingString = String .valueOf (easingValue .string );
81+ if (isLegacyEasingAttribute (easingString )) {
82+ return getLegacyThemeInterpolator (easingString );
83+ }
84+
85+ return AnimationUtils .loadInterpolator (context , easingValue .resourceId );
86+ }
7387
74- float controlX1 = getControlPoint (controlPoints , 0 );
75- float controlY1 = getControlPoint (controlPoints , 1 );
76- float controlX2 = getControlPoint (controlPoints , 2 );
77- float controlY2 = getControlPoint (controlPoints , 3 );
78- return PathInterpolatorCompat .create (controlX1 , controlY1 , controlX2 , controlY2 );
79- } else if (isEasingType (easingString , EASING_TYPE_PATH )) {
80- String path = getEasingContent (easingString , EASING_TYPE_PATH );
81- return PathInterpolatorCompat .create (PathParser .createPathFromPathData (path ));
82- } else {
83- throw new IllegalArgumentException ("Invalid motion easing type: " + easingString );
88+ private static TimeInterpolator getLegacyThemeInterpolator (String easingString ) {
89+ if (isLegacyEasingType (easingString , EASING_TYPE_CUBIC_BEZIER )) {
90+ String controlPointsString = getLegacyEasingContent (easingString , EASING_TYPE_CUBIC_BEZIER );
91+ String [] controlPoints = controlPointsString .split ("," );
92+ if (controlPoints .length != 4 ) {
93+ throw new IllegalArgumentException (
94+ "Motion easing theme attribute must have 4 control points if using bezier curve"
95+ + " format; instead got: "
96+ + controlPoints .length );
8497 }
98+
99+ float controlX1 = getLegacyControlPoint (controlPoints , 0 );
100+ float controlY1 = getLegacyControlPoint (controlPoints , 1 );
101+ float controlX2 = getLegacyControlPoint (controlPoints , 2 );
102+ float controlY2 = getLegacyControlPoint (controlPoints , 3 );
103+ return PathInterpolatorCompat .create (controlX1 , controlY1 , controlX2 , controlY2 );
104+ } else if (isLegacyEasingType (easingString , EASING_TYPE_PATH )) {
105+ String path = getLegacyEasingContent (easingString , EASING_TYPE_PATH );
106+ return PathInterpolatorCompat .create (PathParser .createPathFromPathData (path ));
107+ } else {
108+ throw new IllegalArgumentException ("Invalid motion easing type: " + easingString );
85109 }
86- return defaultInterpolator ;
87110 }
88111
89- private static boolean isEasingType (String easingString , String easingType ) {
112+ private static boolean isLegacyEasingAttribute (String easingString ) {
113+ return isLegacyEasingType (easingString , EASING_TYPE_CUBIC_BEZIER )
114+ || isLegacyEasingType (easingString , EASING_TYPE_PATH );
115+ }
116+
117+ private static boolean isLegacyEasingType (String easingString , String easingType ) {
90118 return easingString .startsWith (easingType + EASING_TYPE_FORMAT_START )
91119 && easingString .endsWith (EASING_TYPE_FORMAT_END );
92120 }
93121
94- private static String getEasingContent (String easingString , String easingType ) {
122+ private static String getLegacyEasingContent (String easingString , String easingType ) {
95123 return easingString .substring (
96124 easingType .length () + EASING_TYPE_FORMAT_START .length (),
97125 easingString .length () - EASING_TYPE_FORMAT_END .length ());
98126 }
99127
100- private static float getControlPoint (String [] controlPoints , int index ) {
128+ private static float getLegacyControlPoint (String [] controlPoints , int index ) {
101129 float controlPoint = Float .parseFloat (controlPoints [index ]);
102130 if (controlPoint < 0 || controlPoint > 1 ) {
103131 throw new IllegalArgumentException (
0 commit comments