From b402cb3c032ea10beb941284b49a9667ee7abe1e Mon Sep 17 00:00:00 2001 From: Material Design Team Date: Tue, 3 May 2022 05:46:22 -0400 Subject: [PATCH] [M3][Color] Fix resources being re-harmonized when wrapContextIfAvailable() is used. PiperOrigin-RevId: 446147925 (cherry picked from commit f0f07c5cf207e82bcbf05636a0919a09597a44b3) --- .../material/color/HarmonizedColors.java | 66 +++++++++++-------- .../color/HarmonizedColorsOptions.java | 6 +- .../color/res/values/theme_overlay.xml | 3 + 3 files changed, 45 insertions(+), 30 deletions(-) diff --git a/lib/java/com/google/android/material/color/HarmonizedColors.java b/lib/java/com/google/android/material/color/HarmonizedColors.java index 1f96007d1ec..f4f8c8029a7 100644 --- a/lib/java/com/google/android/material/color/HarmonizedColors.java +++ b/lib/java/com/google/android/material/color/HarmonizedColors.java @@ -16,8 +16,10 @@ package com.google.android.material.color; +import com.google.android.material.R; + import android.content.Context; -import android.content.ContextWrapper; +import android.content.res.Configuration; import android.content.res.TypedArray; import android.content.res.loader.ResourcesLoader; import android.os.Build.VERSION; @@ -68,9 +70,11 @@ public static void applyToContextIfAvailable( if (!isHarmonizedColorAvailable()) { return; } - int themeOverlay = options.getThemeOverlayResourceId(); + Map colorReplacementMap = + createHarmonizedColorReplacementMap(context, options); + int themeOverlay = options.getThemeOverlayResourceId(/* defaultThemeOverlay= */ 0); - if (addResourcesLoaderToContext(context, options) && themeOverlay != 0) { + if (addResourcesLoaderToContext(context, colorReplacementMap) && themeOverlay != 0) { ThemeUtils.applyThemeOverlay(context, themeOverlay); } } @@ -93,13 +97,19 @@ public static Context wrapContextIfAvailable( if (!isHarmonizedColorAvailable()) { return context; } - int themeOverlay = options.getThemeOverlayResourceId(); - Context newContext = - themeOverlay == 0 - ? new ContextWrapper(context) - : new ContextThemeWrapper(context, themeOverlay); - - return addResourcesLoaderToContext(newContext, options) ? newContext : context; + // Retrieve colors from original context passed in before the resources are overridden below. + Map colorReplacementMap = + createHarmonizedColorReplacementMap(context, options); + // Empty themeOverlay is used as default to prevent ContextThemeWrapper uses the default theme + // of the application to wrap Context. + int themeOverlay = + options.getThemeOverlayResourceId(R.style.ThemeOverlay_Material3_HarmonizedColors_Empty); + ContextThemeWrapper themeWrapper = new ContextThemeWrapper(context, themeOverlay); + // Because ContextThemeWrapper does not provide a new set of resources, override config to + // retrieve the new set of resources and to keep the original context's resources intact. + themeWrapper.applyOverrideConfiguration(new Configuration()); + + return addResourcesLoaderToContext(themeWrapper, colorReplacementMap) ? themeWrapper : context; } /** @@ -115,31 +125,18 @@ public static boolean isHarmonizedColorAvailable() { return VERSION.SDK_INT >= VERSION_CODES.R; } - @RequiresApi(api = VERSION_CODES.R) - private static boolean addResourcesLoaderToContext( - Context context, HarmonizedColorsOptions options) { - ResourcesLoader resourcesLoader = - ColorResourcesLoaderCreator.create( - context, createHarmonizedColorReplacementMap(context, options)); - if (resourcesLoader != null) { - context.getResources().addLoaders(resourcesLoader); - return true; - } - return false; - } - @RequiresApi(api = VERSION_CODES.LOLLIPOP) private static Map createHarmonizedColorReplacementMap( - Context context, HarmonizedColorsOptions options) { + Context originalContext, HarmonizedColorsOptions options) { Map colorReplacementMap = new HashMap<>(); int colorToHarmonizeWith = - MaterialColors.getColor(context, options.getColorAttributeToHarmonizeWith(), TAG); + MaterialColors.getColor(originalContext, options.getColorAttributeToHarmonizeWith(), TAG); // Harmonize color resources. for (int colorResourceId : options.getColorResourceIds()) { int harmonizedColor = MaterialColors.harmonize( - ContextCompat.getColor(context, colorResourceId), colorToHarmonizeWith); + ContextCompat.getColor(originalContext, colorResourceId), colorToHarmonizeWith); colorReplacementMap.put(colorResourceId, harmonizedColor); } @@ -151,10 +148,11 @@ private static Map createHarmonizedColorReplacementMap( // is not provided, look up resources value the theme attributes point to and // harmonize directly. int themeOverlay = colorAttributes.getThemeOverlay(); - TypedArray themeAttributesTypedArray = context.obtainStyledAttributes(attributes); + TypedArray themeAttributesTypedArray = originalContext.obtainStyledAttributes(attributes); TypedArray themeOverlayAttributesTypedArray = themeOverlay != 0 - ? new ContextThemeWrapper(context, themeOverlay).obtainStyledAttributes(attributes) + ? new ContextThemeWrapper(originalContext, themeOverlay) + .obtainStyledAttributes(attributes) : null; addHarmonizedColorAttributesToReplacementMap( colorReplacementMap, @@ -171,6 +169,18 @@ private static Map createHarmonizedColorReplacementMap( return colorReplacementMap; } + @RequiresApi(api = VERSION_CODES.R) + private static boolean addResourcesLoaderToContext( + Context context, Map colorReplacementMap) { + ResourcesLoader resourcesLoader = + ColorResourcesLoaderCreator.create(context, colorReplacementMap); + if (resourcesLoader != null) { + context.getResources().addLoaders(resourcesLoader); + return true; + } + return false; + } + // TypedArray.getType() requires API >= 21. @RequiresApi(api = VERSION_CODES.LOLLIPOP) private static void addHarmonizedColorAttributesToReplacementMap( diff --git a/lib/java/com/google/android/material/color/HarmonizedColorsOptions.java b/lib/java/com/google/android/material/color/HarmonizedColorsOptions.java index f90a24d83a3..9f933b95f19 100644 --- a/lib/java/com/google/android/material/color/HarmonizedColorsOptions.java +++ b/lib/java/com/google/android/material/color/HarmonizedColorsOptions.java @@ -127,7 +127,9 @@ public HarmonizedColorsOptions build() { } @StyleRes - int getThemeOverlayResourceId() { - return this.colorAttributes != null ? colorAttributes.getThemeOverlay() : 0; + int getThemeOverlayResourceId(@StyleRes int defaultThemeOverlay) { + return (colorAttributes != null && colorAttributes.getThemeOverlay() != 0) + ? colorAttributes.getThemeOverlay() + : defaultThemeOverlay; } } diff --git a/lib/java/com/google/android/material/color/res/values/theme_overlay.xml b/lib/java/com/google/android/material/color/res/values/theme_overlay.xml index 6d072da4704..b6ef1fc8bbc 100644 --- a/lib/java/com/google/android/material/color/res/values/theme_overlay.xml +++ b/lib/java/com/google/android/material/color/res/values/theme_overlay.xml @@ -23,4 +23,7 @@ @color/material_harmonized_color_on_error_container + +