1 package org.robolectric.shadows; 2 3 import static com.google.common.truth.Truth.assertThat; 4 import static org.robolectric.Robolectric.buildActivity; 5 6 import android.app.Activity; 7 import android.content.res.Resources; 8 import android.content.res.Resources.Theme; 9 import android.content.res.TypedArray; 10 import android.content.res.XmlResourceParser; 11 import android.graphics.drawable.ColorDrawable; 12 import android.os.Bundle; 13 import android.util.AttributeSet; 14 import android.util.Xml; 15 import android.view.View; 16 import android.widget.Button; 17 import androidx.test.core.app.ApplicationProvider; 18 import androidx.test.ext.junit.runners.AndroidJUnit4; 19 import org.junit.After; 20 import org.junit.Before; 21 import org.junit.Test; 22 import org.junit.runner.RunWith; 23 import org.robolectric.R; 24 import org.robolectric.Robolectric; 25 import org.robolectric.android.controller.ActivityController; 26 import org.xmlpull.v1.XmlPullParser; 27 28 @RunWith(AndroidJUnit4.class) 29 public class ShadowThemeTest { 30 private Resources resources; 31 32 @Before 33 public void setUp() throws Exception { 34 resources = ApplicationProvider.getApplicationContext().getResources(); 35 } 36 37 @After 38 public void tearDown() { 39 ShadowLegacyAssetManager.strictErrors = false; 40 } 41 42 @Test public void whenExplicitlySetOnActivity_afterSetContentView_activityGetsThemeFromActivityInManifest() throws Exception { 43 TestActivity activity = buildActivity(TestActivityWithAnotherTheme.class).create().get(); 44 activity.setTheme(R.style.Theme_Robolectric); 45 Button theButton = (Button) activity.findViewById(R.id.button); 46 ColorDrawable background = (ColorDrawable) theButton.getBackground(); 47 assertThat(background.getColor()).isEqualTo(0xffff0000); 48 } 49 50 @Test public void whenExplicitlySetOnActivity_beforeSetContentView_activityUsesNewTheme() throws Exception { 51 ActivityController<TestActivityWithAnotherTheme> activityController = buildActivity(TestActivityWithAnotherTheme.class); 52 TestActivity activity = activityController.get(); 53 activity.setTheme(R.style.Theme_Robolectric); 54 activityController.create(); 55 Button theButton = (Button) activity.findViewById(R.id.button); 56 ColorDrawable background = (ColorDrawable) theButton.getBackground(); 57 assertThat(background.getColor()).isEqualTo(0xff00ff00); 58 } 59 60 @Test public void whenSetOnActivityInManifest_activityGetsThemeFromActivityInManifest() throws Exception { 61 TestActivity activity = buildActivity(TestActivityWithAnotherTheme.class).create().get(); 62 Button theButton = (Button) activity.findViewById(R.id.button); 63 ColorDrawable background = (ColorDrawable) theButton.getBackground(); 64 assertThat(background.getColor()).isEqualTo(0xffff0000); 65 } 66 67 @Test public void whenNotSetOnActivityInManifest_activityGetsThemeFromApplicationInManifest() throws Exception { 68 TestActivity activity = buildActivity(TestActivity.class).create().get(); 69 Button theButton = (Button) activity.findViewById(R.id.button); 70 ColorDrawable background = (ColorDrawable) theButton.getBackground(); 71 assertThat(background.getColor()).isEqualTo(0xff00ff00); 72 } 73 74 @Test public void shouldResolveReferencesThatStartWithAQuestionMark() throws Exception { 75 TestActivity activity = buildActivity(TestActivityWithAnotherTheme.class).create().get(); 76 Button theButton = (Button) activity.findViewById(R.id.button); 77 assertThat(theButton.getMinWidth()).isEqualTo(8); 78 assertThat(theButton.getMinHeight()).isEqualTo(8); 79 } 80 81 @Test public void forStylesWithImplicitParents_shouldInheritValuesNotDefinedInChild() throws Exception { 82 Resources.Theme theme = resources.newTheme(); 83 theme.applyStyle(R.style.Theme_Robolectric_EmptyParent, true); 84 assertThat(theme.obtainStyledAttributes(new int[] {R.attr.string1}).hasValue(0)).isFalse(); 85 } 86 87 @Test public void shouldApplyParentStylesFromAttrs() throws Exception { 88 Resources.Theme theme = resources.newTheme(); 89 theme.applyStyle(R.style.SimpleParent, true); 90 assertThat(theme.obtainStyledAttributes(new int[] {R.attr.parent_string}).getString(0)) 91 .isEqualTo("parent string"); 92 } 93 94 @Test public void applyStyle_shouldOverrideParentAttrs() throws Exception { 95 Resources.Theme theme = resources.newTheme(); 96 theme.applyStyle(R.style.SimpleChildWithOverride, true); 97 assertThat(theme.obtainStyledAttributes(new int[] {R.attr.parent_string}).getString(0)) 98 .isEqualTo("parent string overridden by child"); 99 } 100 101 @Test public void applyStyle_shouldOverrideImplicitParentAttrs() throws Exception { 102 Resources.Theme theme = resources.newTheme(); 103 theme.applyStyle(R.style.SimpleParent_ImplicitChild, true); 104 assertThat(theme.obtainStyledAttributes(new int[] {R.attr.parent_string}).getString(0)) 105 .isEqualTo("parent string overridden by child"); 106 } 107 108 @Test public void applyStyle_shouldInheritParentAttrs() throws Exception { 109 Resources.Theme theme = resources.newTheme(); 110 theme.applyStyle(R.style.SimpleChildWithAdditionalAttributes, true); 111 assertThat(theme.obtainStyledAttributes(new int[] {R.attr.child_string}).getString(0)) 112 .isEqualTo("child string"); 113 assertThat(theme.obtainStyledAttributes(new int[] {R.attr.parent_string}).getString(0)) 114 .isEqualTo("parent string"); 115 } 116 117 @Test 118 public void setTo_shouldCopyAllAttributesToEmptyTheme() throws Exception { 119 Resources.Theme theme1 = resources.newTheme(); 120 theme1.applyStyle(R.style.Theme_Robolectric, false); 121 assertThat(theme1.obtainStyledAttributes(new int[]{R.attr.string1}).getString(0)) 122 .isEqualTo("string 1 from Theme.Robolectric"); 123 124 Resources.Theme theme2 = resources.newTheme(); 125 theme2.setTo(theme1); 126 127 assertThat(theme2.obtainStyledAttributes(new int[]{R.attr.string1}).getString(0)) 128 .isEqualTo("string 1 from Theme.Robolectric"); 129 } 130 131 @Test 132 public void setTo_whenDestThemeIsModified_sourceThemeShouldNotMutate() throws Exception { 133 Resources.Theme sourceTheme = resources.newTheme(); 134 sourceTheme.applyStyle(R.style.StyleA, false); 135 136 Resources.Theme destTheme = resources.newTheme(); 137 destTheme.setTo(sourceTheme); 138 destTheme.applyStyle(R.style.StyleB, true); 139 140 assertThat(destTheme.obtainStyledAttributes(new int[]{R.attr.string1}).getString(0)) 141 .isEqualTo("string 1 from style B"); 142 assertThat(sourceTheme.obtainStyledAttributes(new int[]{R.attr.string1}).getString(0)) 143 .isEqualTo("string 1 from style A"); 144 } 145 146 @Test 147 public void setTo_whenSourceThemeIsModified_destThemeShouldNotMutate() throws Exception { 148 Resources.Theme sourceTheme = resources.newTheme(); 149 sourceTheme.applyStyle(R.style.StyleA, false); 150 151 Resources.Theme destTheme = resources.newTheme(); 152 destTheme.setTo(sourceTheme); 153 sourceTheme.applyStyle(R.style.StyleB, true); 154 155 assertThat(destTheme.obtainStyledAttributes(new int[]{R.attr.string1}).getString(0)) 156 .isEqualTo("string 1 from style A"); 157 assertThat(sourceTheme.obtainStyledAttributes(new int[]{R.attr.string1}).getString(0)) 158 .isEqualTo("string 1 from style B"); 159 } 160 161 @Test 162 public void applyStyle_withForceFalse_shouldApplyButNotOverwriteExistingAttributeValues() throws Exception { 163 Resources.Theme theme = resources.newTheme(); 164 theme.applyStyle(R.style.StyleA, false); 165 assertThat(theme.obtainStyledAttributes(new int[]{R.attr.string1}).getString(0)) 166 .isEqualTo("string 1 from style A"); 167 168 theme.applyStyle(R.style.StyleB, false); 169 assertThat(theme.obtainStyledAttributes(new int[]{R.attr.string1}).getString(0)) 170 .isEqualTo("string 1 from style A"); 171 } 172 173 @Test 174 public void applyStyle_withForceTrue_shouldApplyAndOverwriteExistingAttributeValues() throws Exception { 175 Resources.Theme theme = resources.newTheme(); 176 theme.applyStyle(R.style.StyleA, false); 177 assertThat(theme.obtainStyledAttributes(new int[]{R.attr.string1}).getString(0)) 178 .isEqualTo("string 1 from style A"); 179 180 theme.applyStyle(R.style.StyleB, true); 181 assertThat(theme.obtainStyledAttributes(new int[]{R.attr.string1}).getString(0)) 182 .isEqualTo("string 1 from style B"); 183 } 184 185 @Test 186 public void whenStyleSpecifiesAttr_obtainStyledAttribute_findsCorrectValue() throws Exception { 187 Resources.Theme theme = resources.newTheme(); 188 theme.applyStyle(R.style.Theme_Robolectric, false); 189 theme.applyStyle(R.style.Theme_ThemeContainingStyleReferences, true); 190 191 assertThat(theme.obtainStyledAttributes( 192 Robolectric.buildAttributeSet().setStyleAttribute("?attr/styleReference").build(), 193 new int[]{R.attr.string2}, 0, 0).getString(0)) 194 .isEqualTo("string 2 from YetAnotherStyle"); 195 196 assertThat(theme.obtainStyledAttributes( 197 Robolectric.buildAttributeSet().setStyleAttribute("?styleReference").build(), 198 new int[]{R.attr.string2}, 0, 0).getString(0)) 199 .isEqualTo("string 2 from YetAnotherStyle"); 200 } 201 202 @Test 203 public void xml_whenStyleSpecifiesAttr_obtainStyledAttribute_findsCorrectValue() throws Exception { 204 Resources.Theme theme = resources.newTheme(); 205 theme.applyStyle(R.style.Theme_Robolectric, false); 206 theme.applyStyle(R.style.Theme_ThemeContainingStyleReferences, true); 207 208 assertThat(theme.obtainStyledAttributes(getFirstElementAttrSet(R.xml.temp), 209 new int[]{R.attr.string2}, 0, 0).getString(0)) 210 .isEqualTo("string 2 from YetAnotherStyle"); 211 212 assertThat(theme.obtainStyledAttributes(getFirstElementAttrSet(R.xml.temp_parent), 213 new int[]{R.attr.string2}, 0, 0).getString(0)) 214 .isEqualTo("string 2 from YetAnotherStyle"); 215 } 216 217 @Test 218 public void whenAttrSetAttrSpecifiesAttr_obtainStyledAttribute_returnsItsValue() throws Exception { 219 Resources.Theme theme = resources.newTheme(); 220 theme.applyStyle(R.style.Theme_Robolectric, false); 221 theme.applyStyle(R.style.Theme_ThemeContainingStyleReferences, true); 222 223 assertThat(theme.obtainStyledAttributes( 224 Robolectric.buildAttributeSet().addAttribute(R.attr.string2, "?attr/string1").build(), 225 new int[]{R.attr.string2}, 0, 0).getString(0)) 226 .isEqualTo("string 1 from Theme.Robolectric"); 227 } 228 229 public static class TestActivity extends Activity { 230 @Override protected void onCreate(Bundle savedInstanceState) { 231 super.onCreate(savedInstanceState); 232 setContentView(R.layout.styles_button_layout); 233 } 234 } 235 236 @Test 237 public void dimenRef() throws Exception { 238 AttributeSet attributeSet = Robolectric.buildAttributeSet() 239 .addAttribute(android.R.attr.layout_height, "@dimen/test_px_dimen") 240 .build(); 241 TypedArray typedArray = resources.newTheme().obtainStyledAttributes( 242 attributeSet, new int[]{android.R.attr.layout_height}, 0, 0); 243 assertThat(typedArray.getDimensionPixelSize(0, -1)).isEqualTo(15); 244 } 245 246 @Test 247 public void dimenRefRef() throws Exception { 248 AttributeSet attributeSet = Robolectric.buildAttributeSet() 249 .addAttribute(android.R.attr.layout_height, "@dimen/ref_to_px_dimen") 250 .build(); 251 TypedArray typedArray = resources.newTheme().obtainStyledAttributes( 252 attributeSet, new int[]{android.R.attr.layout_height}, 0, 0); 253 assertThat(typedArray.getDimensionPixelSize(0, -1)).isEqualTo(15); 254 } 255 256 @Test public void obtainStyledAttributes_shouldFindAttributeInDefaultStyle() throws Exception { 257 Theme theme = resources.newTheme(); 258 TypedArray typedArray = theme.obtainStyledAttributes(R.style.StyleA, new int[]{R.attr.string1}); 259 assertThat(typedArray.getString(0)).isEqualTo("string 1 from style A"); 260 } 261 262 @Test public void shouldApplyFromStyleAttribute() throws Exception { 263 TestWithStyleAttrActivity activity = buildActivity(TestWithStyleAttrActivity.class).create().get(); 264 View button = activity.findViewById(R.id.button); 265 assertThat(button.getLayoutParams().width).isEqualTo(42); // comes via style attr 266 } 267 268 //////////////////////////// 269 270 private XmlResourceParser getFirstElementAttrSet(int resId) throws Exception { 271 XmlResourceParser xml = resources.getXml(resId); 272 assertThat(xml.next()).isEqualTo(XmlPullParser.START_DOCUMENT); 273 assertThat(xml.nextTag()).isEqualTo(XmlPullParser.START_TAG); 274 return (XmlResourceParser) Xml.asAttributeSet(xml); 275 } 276 277 public static class TestActivityWithAnotherTheme extends TestActivity { 278 } 279 280 public static class TestWithStyleAttrActivity extends Activity { 281 @Override protected void onCreate(Bundle savedInstanceState) { 282 super.onCreate(savedInstanceState); 283 setContentView(R.layout.styles_button_with_style_layout); 284 } 285 } 286 } 287