1ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes/* 2ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes * Copyright (C) 2016 The Android Open Source Project 3ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes * 4ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes * Licensed under the Apache License, Version 2.0 (the "License"); 5ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes * you may not use this file except in compliance with the License. 6ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes * You may obtain a copy of the License at 7ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes * 8ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes * http://www.apache.org/licenses/LICENSE-2.0 9ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes * 10ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes * Unless required by applicable law or agreed to in writing, software 11ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes * distributed under the License is distributed on an "AS IS" BASIS, 12ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes * See the License for the specific language governing permissions and 14ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes * limitations under the License 15ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes */ 16ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes 17ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banespackage android.support.v7.app; 18ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes 19c69882cb9b130902c1554ef5d3e3b06d776cd796Alan Viveretteimport android.support.annotation.RequiresApi; 20ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banesimport android.content.res.Resources; 21ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banesimport android.os.Build; 22ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banesimport android.support.annotation.NonNull; 23ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banesimport android.util.Log; 24ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banesimport android.util.LongSparseArray; 25ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes 26ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banesimport java.lang.reflect.Field; 27ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banesimport java.util.Map; 28ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes 29ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banesclass ResourcesFlusher { 30ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes private static final String TAG = "ResourcesFlusher"; 31ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes 32ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes private static Field sDrawableCacheField; 33ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes private static boolean sDrawableCacheFieldFetched; 34ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes 35ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes private static Class sThemedResourceCacheClazz; 36ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes private static boolean sThemedResourceCacheClazzFetched; 37ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes 38ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes private static Field sThemedResourceCache_mUnthemedEntriesField; 39ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes private static boolean sThemedResourceCache_mUnthemedEntriesFieldFetched; 40ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes 41ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes private static Field sResourcesImplField; 42ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes private static boolean sResourcesImplFieldFetched; 43ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes 44ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes static boolean flush(@NonNull final Resources resources) { 45d38b36c6370fe2aa448b7b824e8fb399fb3f0b97Alan Viverette if (Build.VERSION.SDK_INT >= 24) { 46ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes return flushNougats(resources); 47d38b36c6370fe2aa448b7b824e8fb399fb3f0b97Alan Viverette } else if (Build.VERSION.SDK_INT >= 23) { 48ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes return flushMarshmallows(resources); 49d38b36c6370fe2aa448b7b824e8fb399fb3f0b97Alan Viverette } else if (Build.VERSION.SDK_INT >= 21) { 50ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes return flushLollipops(resources); 51ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } 52ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes return false; 53ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } 54ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes 55c69882cb9b130902c1554ef5d3e3b06d776cd796Alan Viverette @RequiresApi(21) 56ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes private static boolean flushLollipops(@NonNull final Resources resources) { 57ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes if (!sDrawableCacheFieldFetched) { 58ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes try { 59ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes sDrawableCacheField = Resources.class.getDeclaredField("mDrawableCache"); 60ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes sDrawableCacheField.setAccessible(true); 61ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } catch (NoSuchFieldException e) { 62ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes Log.e(TAG, "Could not retrieve Resources#mDrawableCache field", e); 63ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } 64ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes sDrawableCacheFieldFetched = true; 65ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } 66ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes if (sDrawableCacheField != null) { 67ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes Map drawableCache = null; 68ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes try { 69ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes drawableCache = (Map) sDrawableCacheField.get(resources); 70ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } catch (IllegalAccessException e) { 71ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes Log.e(TAG, "Could not retrieve value from Resources#mDrawableCache", e); 72ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } 73ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes if (drawableCache != null) { 74ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes drawableCache.clear(); 75ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes return true; 76ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } 77ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } 78ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes return false; 79ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } 80ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes 81c69882cb9b130902c1554ef5d3e3b06d776cd796Alan Viverette @RequiresApi(23) 82ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes private static boolean flushMarshmallows(@NonNull final Resources resources) { 83ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes if (!sDrawableCacheFieldFetched) { 84ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes try { 85ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes sDrawableCacheField = Resources.class.getDeclaredField("mDrawableCache"); 86ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes sDrawableCacheField.setAccessible(true); 87ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } catch (NoSuchFieldException e) { 88ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes Log.e(TAG, "Could not retrieve Resources#mDrawableCache field", e); 89ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } 90ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes sDrawableCacheFieldFetched = true; 91ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } 92ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes 93ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes Object drawableCache = null; 94ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes if (sDrawableCacheField != null) { 95ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes try { 96ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes drawableCache = sDrawableCacheField.get(resources); 97ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } catch (IllegalAccessException e) { 98ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes Log.e(TAG, "Could not retrieve value from Resources#mDrawableCache", e); 99ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } 100ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } 101ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes 102ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes if (drawableCache == null) { 103ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes // If there is no drawable cache, there's nothing to flush... 104ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes return false; 105ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } 106ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes 107ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes return drawableCache != null && flushThemedResourcesCache(drawableCache); 108ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } 109ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes 110c69882cb9b130902c1554ef5d3e3b06d776cd796Alan Viverette @RequiresApi(24) 111ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes private static boolean flushNougats(@NonNull final Resources resources) { 112ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes if (!sResourcesImplFieldFetched) { 113ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes try { 114ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes sResourcesImplField = Resources.class.getDeclaredField("mResourcesImpl"); 115ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes sResourcesImplField.setAccessible(true); 116ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } catch (NoSuchFieldException e) { 117ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes Log.e(TAG, "Could not retrieve Resources#mResourcesImpl field", e); 118ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } 119ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes sResourcesImplFieldFetched = true; 120ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } 121ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes 122ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes if (sResourcesImplField == null) { 123ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes // If the mResourcesImpl field isn't available, bail out now 124ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes return false; 125ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } 126ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes 127ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes Object resourcesImpl = null; 128ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes try { 129ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes resourcesImpl = sResourcesImplField.get(resources); 130ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } catch (IllegalAccessException e) { 131ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes Log.e(TAG, "Could not retrieve value from Resources#mResourcesImpl", e); 132ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } 133ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes 134ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes if (resourcesImpl == null) { 135ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes // If there is no impl instance, bail out now 136ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes return false; 137ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } 138ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes 139ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes if (!sDrawableCacheFieldFetched) { 140ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes try { 141ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes sDrawableCacheField = resourcesImpl.getClass().getDeclaredField("mDrawableCache"); 142ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes sDrawableCacheField.setAccessible(true); 143ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } catch (NoSuchFieldException e) { 144ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes Log.e(TAG, "Could not retrieve ResourcesImpl#mDrawableCache field", e); 145ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } 146ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes sDrawableCacheFieldFetched = true; 147ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } 148ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes 149ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes Object drawableCache = null; 150ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes if (sDrawableCacheField != null) { 151ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes try { 152ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes drawableCache = sDrawableCacheField.get(resourcesImpl); 153ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } catch (IllegalAccessException e) { 154ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes Log.e(TAG, "Could not retrieve value from ResourcesImpl#mDrawableCache", e); 155ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } 156ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } 157ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes 158ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes return drawableCache != null && flushThemedResourcesCache(drawableCache); 159ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } 160ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes 161c69882cb9b130902c1554ef5d3e3b06d776cd796Alan Viverette @RequiresApi(16) 162ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes private static boolean flushThemedResourcesCache(@NonNull final Object cache) { 163ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes if (!sThemedResourceCacheClazzFetched) { 164ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes try { 165ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes sThemedResourceCacheClazz = Class.forName("android.content.res.ThemedResourceCache"); 166ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } catch (ClassNotFoundException e) { 167ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes Log.e(TAG, "Could not find ThemedResourceCache class", e); 168ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } 169ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes sThemedResourceCacheClazzFetched = true; 170ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } 171ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes 172ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes if (sThemedResourceCacheClazz == null) { 173ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes // If the ThemedResourceCache class isn't available, bail out now 174ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes return false; 175ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } 176ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes 177ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes if (!sThemedResourceCache_mUnthemedEntriesFieldFetched) { 178ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes try { 179ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes sThemedResourceCache_mUnthemedEntriesField = 180ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes sThemedResourceCacheClazz.getDeclaredField("mUnthemedEntries"); 181ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes sThemedResourceCache_mUnthemedEntriesField.setAccessible(true); 182ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } catch (NoSuchFieldException ee) { 183ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes Log.e(TAG, "Could not retrieve ThemedResourceCache#mUnthemedEntries field", ee); 184ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } 185ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes sThemedResourceCache_mUnthemedEntriesFieldFetched = true; 186ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } 187ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes 188ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes if (sThemedResourceCache_mUnthemedEntriesField == null) { 189ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes // Didn't get mUnthemedEntries field, bail out... 190ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes return false; 191ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } 192ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes 193ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes LongSparseArray unthemedEntries = null; 194ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes try { 195ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes unthemedEntries = (LongSparseArray) 196ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes sThemedResourceCache_mUnthemedEntriesField.get(cache); 197ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } catch (IllegalAccessException e) { 198ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes Log.e(TAG, "Could not retrieve value from ThemedResourceCache#mUnthemedEntries", e); 199ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } 200ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes 201ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes if (unthemedEntries != null) { 202ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes unthemedEntries.clear(); 203ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes return true; 204ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } 205ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes return false; 206ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes } 207ef780d248167e51db5ff3b98ee11d62343c09ec3Chris Banes} 208