diff options
Diffstat (limited to 'android/support/v7')
-rw-r--r-- | android/support/v7/app/MediaRouteButton.java | 3 | ||||
-rw-r--r-- | android/support/v7/app/MediaRouteChooserDialog.java | 6 | ||||
-rw-r--r-- | android/support/v7/app/MediaRouteControllerDialog.java | 8 | ||||
-rw-r--r-- | android/support/v7/app/MediaRouterThemeHelper.java | 110 | ||||
-rw-r--r-- | android/support/v7/util/DiffUtil.java | 67 | ||||
-rw-r--r-- | android/support/v7/widget/AppCompatTextHelper.java | 37 | ||||
-rw-r--r-- | android/support/v7/widget/TintTypedArray.java | 5 |
7 files changed, 173 insertions, 63 deletions
diff --git a/android/support/v7/app/MediaRouteButton.java b/android/support/v7/app/MediaRouteButton.java index d3f7020b..fdbcf9ad 100644 --- a/android/support/v7/app/MediaRouteButton.java +++ b/android/support/v7/app/MediaRouteButton.java @@ -121,8 +121,7 @@ public class MediaRouteButton extends View { } public MediaRouteButton(Context context, AttributeSet attrs, int defStyleAttr) { - super(MediaRouterThemeHelper.createThemedContext(context, defStyleAttr), attrs, - defStyleAttr); + super(MediaRouterThemeHelper.createThemedButtonContext(context), attrs, defStyleAttr); context = getContext(); mRouter = MediaRouter.getInstance(context); diff --git a/android/support/v7/app/MediaRouteChooserDialog.java b/android/support/v7/app/MediaRouteChooserDialog.java index 0ab2eb11..17364efb 100644 --- a/android/support/v7/app/MediaRouteChooserDialog.java +++ b/android/support/v7/app/MediaRouteChooserDialog.java @@ -92,10 +92,8 @@ public class MediaRouteChooserDialog extends AppCompatDialog { } public MediaRouteChooserDialog(Context context, int theme) { - // If we pass theme ID of 0 to AppCompatDialog, it will apply dialogTheme on the context, - // which may override our style settings. Passes our uppermost theme ID to prevent this. - super(MediaRouterThemeHelper.createThemedContext(context, theme), - theme == 0 ? MediaRouterThemeHelper.createThemeForDialog(context, theme) : theme); + super(context = MediaRouterThemeHelper.createThemedDialogContext(context, theme, false), + MediaRouterThemeHelper.createThemedDialogStyle(context)); context = getContext(); mRouter = MediaRouter.getInstance(context); diff --git a/android/support/v7/app/MediaRouteControllerDialog.java b/android/support/v7/app/MediaRouteControllerDialog.java index 4b9a17a3..d89bf21e 100644 --- a/android/support/v7/app/MediaRouteControllerDialog.java +++ b/android/support/v7/app/MediaRouteControllerDialog.java @@ -201,12 +201,8 @@ public class MediaRouteControllerDialog extends AlertDialog { } public MediaRouteControllerDialog(Context context, int theme) { - // If we pass theme ID of 0 to AppCompatDialog, it will apply dialogTheme on the context, - // which may override our style settings. Passes our uppermost theme ID to prevent this. - super(MediaRouterThemeHelper.createThemedContext(context, - MediaRouterThemeHelper.getAlertDialogResolvedTheme(context, theme)), theme == 0 - ? MediaRouterThemeHelper.createThemeForDialog(context, MediaRouterThemeHelper - .getAlertDialogResolvedTheme(context, theme)) : theme); + super(context = MediaRouterThemeHelper.createThemedDialogContext(context, theme, true), + MediaRouterThemeHelper.createThemedDialogStyle(context)); mContext = getContext(); mControllerCallback = new MediaControllerCallback(); diff --git a/android/support/v7/app/MediaRouterThemeHelper.java b/android/support/v7/app/MediaRouterThemeHelper.java index 9ef218e0..69e40ac7 100644 --- a/android/support/v7/app/MediaRouterThemeHelper.java +++ b/android/support/v7/app/MediaRouterThemeHelper.java @@ -42,47 +42,76 @@ final class MediaRouterThemeHelper { private MediaRouterThemeHelper() { } - /** - * Creates a themed context based on the explicit style resource or the parent context's default - * theme. - * <p> - * The theme which will be applied on top of the parent {@code context}'s theme is determined - * by the primary color defined in the given {@code style}, or in the parent {@code context}. + static Context createThemedButtonContext(Context context) { + // Apply base Media Router theme. + context = new ContextThemeWrapper(context, getRouterThemeId(context)); + + // Apply custom Media Router theme. + int style = getThemeResource(context, R.attr.mediaRouteTheme); + if (style != 0) { + context = new ContextThemeWrapper(context, style); + } + + return context; + } + + /* + * The following two methods are to be used in conjunction. They should be used to prepare + * the context and theme for a super class constructor (the latter method relies on the + * former method to properly prepare the context): + * super(context = createThemedDialogContext(context, theme), + * createThemedDialogStyle(context)); * - * @param context the parent context - * @param style the resource ID of the style against which to inflate this context, or - * {@code 0} to use the parent {@code context}'s default theme. - * @return The themed context. + * It will apply theme in the following order (style lookups will be done in reverse): + * 1) Current theme + * 2) Supplied theme + * 3) Base Media Router theme + * 4) Custom Media Router theme, if provided */ - static Context createThemedContext(Context context, int style) { - // First, apply dialog property overlay. - Context themedContext = - new ContextThemeWrapper(context, getStyledRouterThemeId(context, style)); - int customizedThemeId = getThemeResource(context, R.attr.mediaRouteTheme); - return customizedThemeId == 0 ? themedContext - : new ContextThemeWrapper(themedContext, customizedThemeId); + static Context createThemedDialogContext(Context context, int theme, boolean alertDialog) { + // 1) Current theme is already applied to the context + + // 2) If no theme is supplied, look it up from the context (dialogTheme/alertDialogTheme) + if (theme == 0) { + theme = getThemeResource(context, !alertDialog + ? android.support.v7.appcompat.R.attr.dialogTheme + : android.support.v7.appcompat.R.attr.alertDialogTheme); + } + // Apply it + context = new ContextThemeWrapper(context, theme); + + // 3) If a custom Media Router theme is provided then apply the base theme + if (getThemeResource(context, R.attr.mediaRouteTheme) != 0) { + context = new ContextThemeWrapper(context, getRouterThemeId(context)); + } + + return context; } + // This method should be used in conjunction with the previous method. + static int createThemedDialogStyle(Context context) { + // 4) Apply the custom Media Router theme + int theme = getThemeResource(context, R.attr.mediaRouteTheme); + if (theme == 0) { + // 3) No custom MediaRouther theme was provided so apply the base theme instead + theme = getRouterThemeId(context); + } - /** - * Creates the theme resource ID intended to be used by dialogs. - */ - static int createThemeForDialog(Context context, int style) { - int customizedThemeId = getThemeResource(context, R.attr.mediaRouteTheme); - return customizedThemeId != 0 ? customizedThemeId : getStyledRouterThemeId(context, style); + return theme; } + // END. Previous two methods should be used in conjunction. - public static int getThemeResource(Context context, int attr) { + static int getThemeResource(Context context, int attr) { TypedValue value = new TypedValue(); return context.getTheme().resolveAttribute(attr, value, true) ? value.resourceId : 0; } - public static float getDisabledAlpha(Context context) { + static float getDisabledAlpha(Context context) { TypedValue value = new TypedValue(); return context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, value, true) ? value.getFloat() : 0.5f; } - public static @ControllerColorType int getControllerColor(Context context, int style) { + static @ControllerColorType int getControllerColor(Context context, int style) { int primaryColor = getThemeColor(context, style, android.support.v7.appcompat.R.attr.colorPrimary); if (ColorUtils.calculateContrast(COLOR_WHITE_ON_DARK_BACKGROUND, primaryColor) @@ -92,7 +121,7 @@ final class MediaRouterThemeHelper { return COLOR_DARK_ON_LIGHT_BACKGROUND; } - public static int getButtonTextColor(Context context) { + static int getButtonTextColor(Context context) { int primaryColor = getThemeColor(context, 0, android.support.v7.appcompat.R.attr.colorPrimary); int backgroundColor = getThemeColor(context, 0, android.R.attr.colorBackground); @@ -104,7 +133,7 @@ final class MediaRouterThemeHelper { return primaryColor; } - public static void setMediaControlsBackgroundColor( + static void setMediaControlsBackgroundColor( Context context, View mainControls, View groupControls, boolean hasGroup) { int primaryColor = getThemeColor(context, 0, android.support.v7.appcompat.R.attr.colorPrimary); @@ -124,7 +153,7 @@ final class MediaRouterThemeHelper { groupControls.setTag(primaryDarkColor); } - public static void setVolumeSliderColor( + static void setVolumeSliderColor( Context context, MediaRouteVolumeSlider volumeSlider, View backgroundView) { int controllerColor = getControllerColor(context, 0); if (Color.alpha(controllerColor) != 0xFF) { @@ -136,23 +165,10 @@ final class MediaRouterThemeHelper { volumeSlider.setColor(controllerColor); } - // This is copied from {@link AlertDialog#resolveDialogTheme} to pre-evaluate theme in advance. - public static int getAlertDialogResolvedTheme(Context context, int themeResId) { - if (themeResId >= 0x01000000) { // start of real resource IDs. - return themeResId; - } else { - TypedValue outValue = new TypedValue(); - context.getTheme().resolveAttribute( - android.support.v7.appcompat.R.attr.alertDialogTheme, outValue, true); - return outValue.resourceId; - } - } - private static boolean isLightTheme(Context context) { TypedValue value = new TypedValue(); - return context.getTheme().resolveAttribute( - android.support.v7.appcompat.R.attr.isLightTheme, value, true) - && value.data != 0; + return context.getTheme().resolveAttribute(android.support.v7.appcompat.R.attr.isLightTheme, + value, true) && value.data != 0; } private static int getThemeColor(Context context, int style, int attr) { @@ -173,16 +189,16 @@ final class MediaRouterThemeHelper { return value.data; } - private static int getStyledRouterThemeId(Context context, int style) { + private static int getRouterThemeId(Context context) { int themeId; if (isLightTheme(context)) { - if (getControllerColor(context, style) == COLOR_DARK_ON_LIGHT_BACKGROUND) { + if (getControllerColor(context, 0) == COLOR_DARK_ON_LIGHT_BACKGROUND) { themeId = R.style.Theme_MediaRouter_Light; } else { themeId = R.style.Theme_MediaRouter_Light_DarkControlPanel; } } else { - if (getControllerColor(context, style) == COLOR_DARK_ON_LIGHT_BACKGROUND) { + if (getControllerColor(context, 0) == COLOR_DARK_ON_LIGHT_BACKGROUND) { themeId = R.style.Theme_MediaRouter_LightControlPanel; } else { themeId = R.style.Theme_MediaRouter; diff --git a/android/support/v7/util/DiffUtil.java b/android/support/v7/util/DiffUtil.java index 6302666f..ebc33f31 100644 --- a/android/support/v7/util/DiffUtil.java +++ b/android/support/v7/util/DiffUtil.java @@ -16,6 +16,7 @@ package android.support.v7.util; +import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.VisibleForTesting; import android.support.v7.widget.RecyclerView; @@ -348,6 +349,72 @@ public class DiffUtil { } /** + * Callback for calculating the diff between two non-null items in a list. + * <p> + * {@link Callback} serves two roles - list indexing, and item diffing. ItemCallback handles + * just the second of these, which allows separation of code that indexes into an array or List + * from the presentation-layer and content specific diffing code. + * + * @param <T> Type of items to compare. + */ + public abstract static class ItemCallback<T> { + /** + * Called to check whether two objects represent the same item. + * <p> + * For example, if your items have unique ids, this method should check their id equality. + * + * @param oldItem The item in the old list. + * @param newItem The item in the new list. + * @return True if the two items represent the same object or false if they are different. + * + * @see Callback#areItemsTheSame(int, int) + */ + public abstract boolean areItemsTheSame(@NonNull T oldItem, @NonNull T newItem); + + /** + * Called to check whether two items have the same data. + * <p> + * This information is used to detect if the contents of an item have changed. + * <p> + * This method to check equality instead of {@link Object#equals(Object)} so that you can + * change its behavior depending on your UI. + * <p> + * For example, if you are using DiffUtil with a + * {@link android.support.v7.widget.RecyclerView.Adapter RecyclerView.Adapter}, you should + * return whether the items' visual representations are the same. + * <p> + * This method is called only if {@link #areItemsTheSame(T, T)} returns {@code true} for + * these items. + * + * @param oldItem The item in the old list. + * @param newItem The item in the new list. + * @return True if the contents of the items are the same or false if they are different. + * + * @see Callback#areContentsTheSame(int, int) + */ + public abstract boolean areContentsTheSame(@NonNull T oldItem, @NonNull T newItem); + + /** + * When {@link #areItemsTheSame(T, T)} returns {@code true} for two items and + * {@link #areContentsTheSame(T, T)} returns false for them, this method is called to + * get a payload about the change. + * <p> + * For example, if you are using DiffUtil with {@link RecyclerView}, you can return the + * particular field that changed in the item and your + * {@link android.support.v7.widget.RecyclerView.ItemAnimator ItemAnimator} can use that + * information to run the correct animation. + * <p> + * Default implementation returns {@code null}. + * + * @see Callback#getChangePayload(int, int) + */ + @SuppressWarnings({"WeakerAccess", "unused"}) + public Object getChangePayload(@NonNull T oldItem, @NonNull T newItem) { + return null; + } + } + + /** * Snakes represent a match between two lists. It is optionally prefixed or postfixed with an * add or remove operation. See the Myers' paper for details. */ diff --git a/android/support/v7/widget/AppCompatTextHelper.java b/android/support/v7/widget/AppCompatTextHelper.java index 51510aa2..fa6196f5 100644 --- a/android/support/v7/widget/AppCompatTextHelper.java +++ b/android/support/v7/widget/AppCompatTextHelper.java @@ -29,6 +29,7 @@ import android.os.Build; import android.support.annotation.NonNull; import android.support.annotation.RequiresApi; import android.support.annotation.RestrictTo; +import android.support.v4.content.res.ResourcesCompat; import android.support.v4.widget.TextViewCompat; import android.support.v7.appcompat.R; import android.text.method.PasswordTransformationMethod; @@ -36,6 +37,8 @@ import android.util.AttributeSet; import android.util.TypedValue; import android.widget.TextView; +import java.lang.ref.WeakReference; + @RequiresApi(9) class AppCompatTextHelper { @@ -63,6 +66,7 @@ class AppCompatTextHelper { private int mStyle = Typeface.NORMAL; private Typeface mFontTypeface; + private boolean mAsyncFontPending; AppCompatTextHelper(TextView view) { mView = view; @@ -213,8 +217,23 @@ class AppCompatTextHelper { ? R.styleable.TextAppearance_android_fontFamily : R.styleable.TextAppearance_fontFamily; if (!context.isRestricted()) { + final WeakReference<TextView> textViewWeak = new WeakReference<>(mView); + ResourcesCompat.FontCallback replyCallback = new ResourcesCompat.FontCallback() { + @Override + public void onFontRetrieved(@NonNull Typeface typeface) { + onAsyncTypefaceReceived(textViewWeak, typeface); + } + + @Override + public void onFontRetrievalFailed(int reason) { + // Do nothing. + } + }; try { - mFontTypeface = a.getFont(fontFamilyId, mStyle); + // Note the callback will be triggered on the UI thread. + mFontTypeface = a.getFont(fontFamilyId, mStyle, replyCallback); + // If this call gave us an immediate result, ignore any pending callbacks. + mAsyncFontPending = mFontTypeface == null; } catch (UnsupportedOperationException | Resources.NotFoundException e) { // Expected if it is not a font resource. } @@ -222,12 +241,16 @@ class AppCompatTextHelper { if (mFontTypeface == null) { // Try with String. This is done by TextView JB+, but fails in ICS String fontFamilyName = a.getString(fontFamilyId); - mFontTypeface = Typeface.create(fontFamilyName, mStyle); + if (fontFamilyName != null) { + mFontTypeface = Typeface.create(fontFamilyName, mStyle); + } } return; } if (a.hasValue(R.styleable.TextAppearance_android_typeface)) { + // Ignore previous pending fonts + mAsyncFontPending = false; int typefaceIndex = a.getInt(R.styleable.TextAppearance_android_typeface, SANS); switch (typefaceIndex) { case SANS: @@ -245,6 +268,16 @@ class AppCompatTextHelper { } } + private void onAsyncTypefaceReceived(WeakReference<TextView> textViewWeak, Typeface typeface) { + if (mAsyncFontPending) { + mFontTypeface = typeface; + final TextView textView = textViewWeak.get(); + if (textView != null) { + textView.setTypeface(typeface, mStyle); + } + } + } + void onSetTextAppearance(Context context, int resId) { final TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, resId, R.styleable.TextAppearance); diff --git a/android/support/v7/widget/TintTypedArray.java b/android/support/v7/widget/TintTypedArray.java index 22709551..384c4615 100644 --- a/android/support/v7/widget/TintTypedArray.java +++ b/android/support/v7/widget/TintTypedArray.java @@ -106,7 +106,8 @@ public class TintTypedArray { * not a font resource. */ @Nullable - public Typeface getFont(@StyleableRes int index, int style) { + public Typeface getFont(@StyleableRes int index, int style, + @Nullable ResourcesCompat.FontCallback fontCallback) { final int resourceId = mWrapped.getResourceId(index, 0); if (resourceId == 0) { return null; @@ -114,7 +115,7 @@ public class TintTypedArray { if (mTypedValue == null) { mTypedValue = new TypedValue(); } - return ResourcesCompat.getFont(mContext, resourceId, mTypedValue, style); + return ResourcesCompat.getFont(mContext, resourceId, mTypedValue, style, fontCallback); } public int length() { |