1 /* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.view.cts; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertFalse; 21 import static org.junit.Assert.assertNotNull; 22 import static org.junit.Assert.assertNull; 23 import static org.junit.Assert.assertSame; 24 import static org.junit.Assert.assertTrue; 25 import static org.junit.Assert.fail; 26 27 import android.content.ComponentName; 28 import android.content.Context; 29 import android.content.pm.ActivityInfo; 30 import android.content.pm.PackageManager; 31 import android.content.res.Resources; 32 import android.content.res.Resources.Theme; 33 import android.content.res.XmlResourceParser; 34 import android.support.test.InstrumentationRegistry; 35 import android.support.test.filters.SmallTest; 36 import android.support.test.runner.AndroidJUnit4; 37 import android.util.AttributeSet; 38 import android.util.TypedValue; 39 import android.util.Xml; 40 import android.view.ContextThemeWrapper; 41 import android.view.Gravity; 42 import android.view.InflateException; 43 import android.view.LayoutInflater; 44 import android.view.LayoutInflater.Factory; 45 import android.view.LayoutInflater.Filter; 46 import android.view.View; 47 import android.view.ViewGroup; 48 import android.view.cts.util.XmlUtils; 49 import android.widget.LinearLayout; 50 51 import org.junit.Before; 52 import org.junit.Test; 53 import org.junit.runner.RunWith; 54 import org.xmlpull.v1.XmlPullParser; 55 56 @SmallTest 57 @RunWith(AndroidJUnit4.class) 58 public class LayoutInflaterTest { 59 private LayoutInflater mLayoutInflater; 60 61 private Context mContext; 62 63 private final Factory mFactory = (String name, Context context, AttributeSet attrs) -> null; 64 65 private boolean isOnLoadClass; 66 67 private final Filter mFilter = (Class clazz) -> { 68 isOnLoadClass = true; 69 return true; 70 }; 71 72 @Before 73 public void setup() { 74 mContext = InstrumentationRegistry.getTargetContext(); 75 mLayoutInflater = (LayoutInflater) mContext.getSystemService( 76 Context.LAYOUT_INFLATER_SERVICE); 77 } 78 79 @Test 80 public void testFrom() { 81 mLayoutInflater = null; 82 mLayoutInflater = LayoutInflater.from(mContext); 83 assertNotNull(mLayoutInflater); 84 mLayoutInflater = null; 85 mLayoutInflater = new MockLayoutInflater(mContext); 86 assertNotNull(mLayoutInflater); 87 88 LayoutInflater layoutInflater = new MockLayoutInflater(mLayoutInflater, 89 mContext); 90 assertNotNull(layoutInflater); 91 } 92 93 @Test 94 public void testAccessLayoutInflaterProperties() { 95 mLayoutInflater.setFilter(mFilter); 96 assertSame(mFilter, mLayoutInflater.getFilter()); 97 mLayoutInflater.setFactory(mFactory); 98 assertSame(mFactory, mLayoutInflater.getFactory()); 99 mLayoutInflater = new MockLayoutInflater(mContext); 100 assertSame(mContext, mLayoutInflater.getContext()); 101 } 102 103 private AttributeSet getAttrs() { 104 XmlResourceParser parser = null; 105 AttributeSet attrs = null; 106 ActivityInfo ai = null; 107 ComponentName mComponentName = new ComponentName(mContext, 108 MockActivity.class); 109 try { 110 ai = mContext.getPackageManager().getActivityInfo(mComponentName, 111 PackageManager.GET_META_DATA); 112 parser = ai.loadXmlMetaData(mContext.getPackageManager(), 113 "android.widget.layout"); 114 int type; 115 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 116 && type != XmlPullParser.START_TAG) { 117 } 118 String nodeName = parser.getName(); 119 if (!"alias".equals(nodeName)) { 120 throw new InflateException(); 121 } 122 int outerDepth = parser.getDepth(); 123 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 124 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 125 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 126 continue; 127 } 128 nodeName = parser.getName(); 129 if ("AbsoluteLayout".equals(nodeName)) { 130 attrs = Xml.asAttributeSet(parser); 131 } else { 132 XmlUtils.skipCurrentTag(parser); 133 } 134 } 135 } catch (Exception e) { 136 } 137 return attrs; 138 } 139 140 @Test 141 public void testCreateView() { 142 AttributeSet attrs = getAttrs(); 143 isOnLoadClass = false; 144 View view = null; 145 try { 146 view = mLayoutInflater.createView("testthrow", "com.android", attrs); 147 fail("should throw exception"); 148 } catch (InflateException e) { 149 } catch (ClassNotFoundException e) { 150 } 151 assertFalse(isOnLoadClass); 152 assertNull(view); 153 mLayoutInflater = null; 154 mLayoutInflater = LayoutInflater.from(mContext); 155 isOnLoadClass = false; 156 mLayoutInflater.setFilter((Class clazz) -> { 157 isOnLoadClass = true; 158 return false; 159 }); 160 try { 161 view = mLayoutInflater.createView("MockActivity", 162 "com.android.app.", attrs); 163 fail("should throw exception"); 164 } catch (InflateException e) { 165 } catch (ClassNotFoundException e) { 166 } 167 assertFalse(isOnLoadClass); 168 assertNull(view); 169 170 isOnLoadClass = false; 171 // allowedState is false 172 try { 173 view = mLayoutInflater.createView(MockActivity.class.getName(), 174 MockActivity.class.getPackage().toString(), attrs); 175 fail("should throw exception"); 176 } catch (InflateException e) { 177 } catch (ClassNotFoundException e) { 178 } 179 assertFalse(isOnLoadClass); 180 assertNull(view); 181 mLayoutInflater = null; 182 mLayoutInflater = LayoutInflater.from(mContext); 183 try { 184 mLayoutInflater.setFilter(null); 185 view = mLayoutInflater.createView("com.android.app.MockActivity", 186 null, attrs); 187 assertNotNull(view); 188 assertFalse(isOnLoadClass); 189 mLayoutInflater = null; 190 mLayoutInflater = LayoutInflater.from(mContext); 191 mLayoutInflater.setFilter(null); 192 193 view = mLayoutInflater.createView(MockActivity.class.getName(), 194 MockActivity.class.getPackage().toString(), attrs); 195 assertNotNull(view); 196 assertFalse(isOnLoadClass); 197 mLayoutInflater.setFilter(mFilter); 198 view = mLayoutInflater.createView(MockActivity.class.getName(), 199 MockActivity.class.getPackage().toString(), attrs); 200 assertNotNull(view); 201 assertTrue(isOnLoadClass); 202 // allowedState!=null 203 view = mLayoutInflater.createView(MockActivity.class.getName(), 204 MockActivity.class.getPackage().toString(), attrs); 205 assertNotNull(view); 206 assertTrue(isOnLoadClass); 207 } catch (InflateException e) { 208 } catch (ClassNotFoundException e) { 209 } 210 } 211 212 @Test(expected=Resources.NotFoundException.class) 213 public void testInflateInvalidId() { 214 mLayoutInflater.inflate(-1, null); 215 } 216 217 @Test 218 public void testInflate() { 219 View view = mLayoutInflater.inflate( 220 android.view.cts.R.layout.inflater_layout, null); 221 assertNotNull(view); 222 223 LinearLayout linearLayout = new LinearLayout(mContext); 224 linearLayout.setOrientation(LinearLayout.VERTICAL); 225 linearLayout.setHorizontalGravity(Gravity.LEFT); 226 linearLayout.setLayoutParams(new ViewGroup.LayoutParams( 227 ViewGroup.LayoutParams.MATCH_PARENT, 228 ViewGroup.LayoutParams.MATCH_PARENT)); 229 assertEquals(0, linearLayout.getChildCount()); 230 view = mLayoutInflater.inflate(R.layout.inflater_layout, 231 linearLayout); 232 assertNotNull(view); 233 assertEquals(1, linearLayout.getChildCount()); 234 } 235 236 @Test(expected=Resources.NotFoundException.class) 237 public void testInflateAttachToRootInvalidId() { 238 mLayoutInflater.inflate(-1, null, false); 239 } 240 241 @Test 242 public void testInflateAttachToRoot() { 243 View view = mLayoutInflater.inflate( 244 R.layout.inflater_layout, null, false); 245 assertNotNull(view); 246 247 LinearLayout linearLayout = new LinearLayout(mContext); 248 linearLayout.setOrientation(LinearLayout.VERTICAL); 249 linearLayout.setHorizontalGravity(Gravity.LEFT); 250 linearLayout.setLayoutParams(new ViewGroup.LayoutParams( 251 ViewGroup.LayoutParams.MATCH_PARENT, 252 ViewGroup.LayoutParams.MATCH_PARENT)); 253 assertEquals(0, linearLayout.getChildCount()); 254 view = mLayoutInflater.inflate(R.layout.inflater_layout, 255 linearLayout, false); 256 assertNotNull(view); 257 assertEquals(0, linearLayout.getChildCount()); 258 259 view = mLayoutInflater.inflate(R.layout.inflater_layout, 260 linearLayout, true); 261 assertNotNull(view); 262 assertEquals(1, linearLayout.getChildCount()); 263 } 264 265 @Test(expected=NullPointerException.class) 266 public void testInflateParserNullParser() { 267 mLayoutInflater.inflate(null, null); 268 } 269 270 @Test 271 public void testInflateParser() { 272 XmlResourceParser parser = mContext.getResources().getLayout(R.layout.inflater_layout); 273 View view = mLayoutInflater.inflate(parser, null); 274 assertNotNull(view); 275 276 LinearLayout linearLayout = new LinearLayout(mContext); 277 linearLayout.setOrientation(LinearLayout.VERTICAL); 278 linearLayout.setHorizontalGravity(Gravity.LEFT); 279 linearLayout.setLayoutParams(new ViewGroup.LayoutParams( 280 ViewGroup.LayoutParams.MATCH_PARENT, 281 ViewGroup.LayoutParams.MATCH_PARENT)); 282 assertEquals(0, linearLayout.getChildCount()); 283 284 try { 285 mLayoutInflater.inflate(parser, linearLayout); 286 fail("should throw exception"); 287 } catch (NullPointerException e) { 288 } 289 290 parser = mContext.getResources().getLayout(R.layout.inflater_layout); 291 view = mLayoutInflater.inflate(parser, linearLayout); 292 assertNotNull(view); 293 assertEquals(1, linearLayout.getChildCount()); 294 parser = mContext.getResources().getLayout(R.layout.inflater_layout); 295 view = mLayoutInflater.inflate(parser, linearLayout); 296 assertNotNull(view); 297 assertEquals(2, linearLayout.getChildCount()); 298 299 parser = getParser(); 300 view = mLayoutInflater.inflate(parser, linearLayout); 301 assertNotNull(view); 302 assertEquals(3, linearLayout.getChildCount()); 303 } 304 305 @Test(expected=NullPointerException.class) 306 public void testInflateParserAttachToRootNullParser() { 307 mLayoutInflater.inflate(null, null, false); 308 } 309 310 @Test 311 public void testInflateParserAttachToRoot() { 312 XmlResourceParser parser = mContext.getResources().getLayout(R.layout.inflater_layout); 313 View view = mLayoutInflater.inflate(parser, null, false); 314 assertNotNull(view); 315 316 LinearLayout linearLayout = new LinearLayout(mContext); 317 linearLayout.setOrientation(LinearLayout.VERTICAL); 318 linearLayout.setHorizontalGravity(Gravity.LEFT); 319 linearLayout.setLayoutParams(new ViewGroup.LayoutParams( 320 ViewGroup.LayoutParams.MATCH_PARENT, 321 ViewGroup.LayoutParams.MATCH_PARENT)); 322 assertEquals(0, linearLayout.getChildCount()); 323 324 try { 325 mLayoutInflater.inflate(parser, linearLayout, false); 326 fail("should throw exception"); 327 } catch (NullPointerException e) { 328 } 329 330 parser = mContext.getResources().getLayout(R.layout.inflater_layout); 331 view = mLayoutInflater.inflate(parser, linearLayout, false); 332 assertNull(view.getParent()); 333 assertNotNull(view); 334 assertEquals(0, linearLayout.getChildCount()); 335 parser = mContext.getResources().getLayout(R.layout.inflater_layout); 336 assertEquals(0, linearLayout.getChildCount()); 337 view = mLayoutInflater.inflate(parser, linearLayout, true); 338 assertNotNull(view); 339 assertNull(view.getParent()); 340 assertEquals(1, linearLayout.getChildCount()); 341 342 parser = getParser(); 343 try { 344 mLayoutInflater.inflate(parser, linearLayout, false); 345 fail("should throw exception"); 346 } catch (InflateException e) { 347 } 348 349 parser = getParser(); 350 351 view = mLayoutInflater.inflate(parser, linearLayout, true); 352 assertNotNull(view); 353 assertEquals(2, linearLayout.getChildCount()); 354 } 355 356 @Test 357 public void testOverrideTheme() { 358 View container = mLayoutInflater.inflate(R.layout.inflater_override_theme_layout, null); 359 verifyThemeType(container, "view_outer", R.id.view_outer, 1); 360 verifyThemeType(container, "view_inner", R.id.view_inner, 2); 361 verifyThemeType(container, "view_attr", R.id.view_attr, 3); 362 verifyThemeType(container, "view_include", R.id.view_include, 4); 363 verifyThemeType(container, "view_include_notheme", R.id.view_include_notheme, 5); 364 } 365 366 private void verifyThemeType(View container, String tag, int id, int type) { 367 TypedValue outValue = new TypedValue(); 368 View view = container.findViewById(id); 369 assertNotNull("Found " + tag, view); 370 Theme theme = view.getContext().getTheme(); 371 boolean resolved = theme.resolveAttribute(R.attr.themeType, outValue, true); 372 assertTrue("Resolved themeType for " + tag, resolved); 373 assertEquals(tag + " has themeType " + type, type, outValue.data); 374 } 375 376 @Test 377 public void testInflateTags() { 378 final View view = mLayoutInflater.inflate( 379 android.view.cts.R.layout.inflater_layout_tags, null); 380 assertNotNull(view); 381 382 checkViewTag(view, R.id.viewlayout_root, R.id.tag_viewlayout_root, R.string.tag1); 383 checkViewTag(view, R.id.mock_view, R.id.tag_mock_view, R.string.tag2); 384 } 385 386 private void checkViewTag(View parent, int viewId, int tagId, int valueResId) { 387 final View target = parent.findViewById(viewId); 388 assertNotNull("Found target view for " + viewId, target); 389 390 final Object tag = target.getTag(tagId); 391 assertNotNull("Tag is set", tag); 392 assertTrue("Tag is a character sequence", tag instanceof CharSequence); 393 394 final Context targetContext = target.getContext(); 395 final CharSequence expectedValue = targetContext.getString(valueResId); 396 assertEquals(tagId + " has tag " + expectedValue, expectedValue, tag); 397 } 398 399 @Test 400 public void testInclude() { 401 final Context themedContext = new ContextThemeWrapper(mContext, R.style.Test_Theme); 402 final LayoutInflater inflater = LayoutInflater.from(themedContext); 403 404 final View container = inflater.inflate(R.layout.inflater_layout_include, null); 405 assertNotNull(container.findViewById(R.id.include_layout_explicit)); 406 assertNotNull(container.findViewById(R.id.include_layout_from_attr)); 407 } 408 409 @Test(expected = InflateException.class) 410 public void testIncludeMissingAttr() { 411 final View container = mLayoutInflater.inflate(R.layout.inflater_layout_include, null); 412 assertNotNull(container.findViewById(R.id.include_layout_from_attr)); 413 } 414 415 static class MockLayoutInflater extends LayoutInflater { 416 417 public MockLayoutInflater(Context c) { 418 super(c); 419 } 420 421 public MockLayoutInflater(LayoutInflater original, Context newContext) { 422 super(original, newContext); 423 } 424 425 @Override 426 public View onCreateView(String name, AttributeSet attrs) 427 throws ClassNotFoundException { 428 return super.onCreateView(name, attrs); 429 } 430 431 @Override 432 public LayoutInflater cloneInContext(Context newContext) { 433 return null; 434 } 435 } 436 437 private XmlResourceParser getParser() { 438 XmlResourceParser parser = null; 439 ActivityInfo ai = null; 440 ComponentName mComponentName = new ComponentName(mContext, 441 MockActivity.class); 442 try { 443 ai = mContext.getPackageManager().getActivityInfo(mComponentName, 444 PackageManager.GET_META_DATA); 445 parser = ai.loadXmlMetaData(mContext.getPackageManager(), 446 "android.view.merge"); 447 } catch (Exception e) { 448 } 449 return parser; 450 } 451 } 452