1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php 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 com.android.ide.eclipse.adt.internal.editors.layout.gle2; 18 19 import com.android.ide.common.rendering.api.Capability; 20 import com.android.ide.common.rendering.api.DataBindingItem; 21 import com.android.ide.common.rendering.api.MergeCookie; 22 import com.android.ide.common.rendering.api.ViewInfo; 23 import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor; 24 import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor; 25 import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor; 26 import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode; 27 import com.android.utils.Pair; 28 29 import org.eclipse.swt.graphics.Rectangle; 30 31 import java.util.ArrayList; 32 import java.util.Arrays; 33 import java.util.Collections; 34 import java.util.HashSet; 35 import java.util.Iterator; 36 import java.util.LinkedList; 37 import java.util.List; 38 import java.util.Set; 39 40 import junit.framework.TestCase; 41 42 @SuppressWarnings("javadoc") 43 public class CanvasViewInfoTest extends TestCase { 44 createDesc(String name, String fqn, boolean hasChildren)45 public static ViewElementDescriptor createDesc(String name, String fqn, boolean hasChildren) { 46 if (hasChildren) { 47 return new ViewElementDescriptor(name, name, fqn, "", "", new AttributeDescriptor[0], 48 new AttributeDescriptor[0], new ElementDescriptor[1], false); 49 } else { 50 return new ViewElementDescriptor(name, fqn); 51 } 52 } 53 createNode(UiViewElementNode parent, String fqn, boolean hasChildren)54 public static UiViewElementNode createNode(UiViewElementNode parent, String fqn, 55 boolean hasChildren) { 56 String name = fqn.substring(fqn.lastIndexOf('.') + 1); 57 ViewElementDescriptor descriptor = createDesc(name, fqn, hasChildren); 58 if (parent == null) { 59 // All node hierarchies should be wrapped inside a document node at the root 60 parent = new UiViewElementNode(createDesc("doc", "doc", true)); 61 } 62 return (UiViewElementNode) parent.appendNewUiChild(descriptor); 63 } 64 createNode(String fqn, boolean hasChildren)65 public static UiViewElementNode createNode(String fqn, boolean hasChildren) { 66 return createNode(null, fqn, hasChildren); 67 } 68 testNormalCreate()69 public void testNormalCreate() throws Exception { 70 normal(true); 71 } 72 testNormalCreateLayoutLib5()73 public void testNormalCreateLayoutLib5() throws Exception { 74 normal(false); 75 } 76 normal(boolean layoutlib5)77 private void normal(boolean layoutlib5) { 78 79 // Normal view hierarchy, no null keys anywhere 80 81 UiViewElementNode rootNode = createNode("android.widget.LinearLayout", true); 82 ViewInfo root = new ViewInfo("LinearLayout", rootNode, 10, 10, 100, 100); 83 UiViewElementNode child1Node = createNode(rootNode, "android.widget.Button", false); 84 ViewInfo child1 = new ViewInfo("Button", child1Node, 0, 0, 50, 20); 85 UiViewElementNode child2Node = createNode(rootNode, "android.widget.Button", false); 86 ViewInfo child2 = new ViewInfo("Button", child2Node, 0, 20, 70, 25); 87 root.setChildren(Arrays.asList(child1, child2)); 88 89 CanvasViewInfo rootView = CanvasViewInfo.create(root, layoutlib5).getFirst(); 90 assertNotNull(rootView); 91 assertEquals("LinearLayout", rootView.getName()); 92 assertEquals(new Rectangle(10, 10, 89, 89), rootView.getAbsRect()); 93 assertEquals(new Rectangle(10, 10, 89, 89), rootView.getSelectionRect()); 94 assertNull(rootView.getParent()); 95 assertSame(rootView.getUiViewNode(), rootNode); 96 assertEquals(2, rootView.getChildren().size()); 97 CanvasViewInfo childView1 = rootView.getChildren().get(0); 98 CanvasViewInfo childView2 = rootView.getChildren().get(1); 99 100 assertEquals("Button", childView1.getName()); 101 assertSame(rootView, childView1.getParent()); 102 assertEquals(new Rectangle(10, 10, 49, 19), childView1.getAbsRect()); 103 assertEquals(new Rectangle(10, 10, 49, 19), childView1.getSelectionRect()); 104 assertSame(childView1.getUiViewNode(), child1Node); 105 106 assertEquals("Button", childView2.getName()); 107 assertSame(rootView, childView2.getParent()); 108 assertEquals(new Rectangle(10, 30, 69, 4), childView2.getAbsRect()); 109 assertEquals(new Rectangle(10, 30, 69, 5), childView2.getSelectionRect()); 110 assertSame(childView2.getUiViewNode(), child2Node); 111 } 112 testShowIn()113 public void testShowIn() throws Exception { 114 showIn(false); 115 } 116 testShowInLayoutLib5()117 public void testShowInLayoutLib5() throws Exception { 118 showIn(true); 119 } 120 showIn(boolean layoutlib5)121 public void showIn(boolean layoutlib5) throws Exception { 122 123 // Test rendering of "Show Included In" (included content rendered 124 // within an outer content that has null keys) 125 126 ViewInfo root = new ViewInfo("LinearLayout", null, 10, 10, 100, 100); 127 ViewInfo child1 = new ViewInfo("CheckBox", null, 0, 0, 50, 20); 128 UiViewElementNode child2Node = createNode("android.widget.RelativeLayout", true); 129 ViewInfo child2 = new ViewInfo("RelativeLayout", child2Node, 0, 20, 70, 25); 130 root.setChildren(Arrays.asList(child1, child2)); 131 UiViewElementNode child21Node = createNode("android.widget.Button", false); 132 ViewInfo child21 = new ViewInfo("RadioButton", child21Node, 0, 20, 70, 25); 133 child2.setChildren(Arrays.asList(child21)); 134 135 CanvasViewInfo rootView = CanvasViewInfo.create(root, layoutlib5).getFirst(); 136 assertNotNull(rootView); 137 assertEquals("LinearLayout", rootView.getName()); 138 assertEquals(new Rectangle(10, 10, 89, 89), rootView.getAbsRect()); 139 assertEquals(new Rectangle(10, 10, 89, 89), rootView.getSelectionRect()); 140 assertNull(rootView.getParent()); 141 assertNull(rootView.getUiViewNode()); 142 assertEquals(1, rootView.getChildren().size()); 143 CanvasViewInfo includedView = rootView.getChildren().get(0); 144 145 assertEquals("RelativeLayout", includedView.getName()); 146 assertSame(rootView, includedView.getParent()); 147 assertEquals(new Rectangle(10, 30, 69, 4), includedView.getAbsRect()); 148 assertEquals(new Rectangle(10, 30, 69, 5), includedView.getSelectionRect()); 149 assertSame(includedView.getUiViewNode(), child2Node); 150 151 CanvasViewInfo grandChild = includedView.getChildren().get(0); 152 assertNotNull(grandChild); 153 assertEquals("RadioButton", grandChild.getName()); 154 assertSame(child21Node, grandChild.getUiViewNode()); 155 assertEquals(new Rectangle(10, 50, 69, 4), grandChild.getAbsRect()); 156 assertEquals(new Rectangle(10, 50, 69, 5), grandChild.getSelectionRect()); 157 } 158 testIncludeTag()159 public void testIncludeTag() throws Exception { 160 boolean layoutlib5 = true; 161 162 // Test rendering of included views on layoutlib 5+ (e.g. has <include> tag) 163 164 UiViewElementNode rootNode = createNode("android.widget.LinearLayout", true); 165 ViewInfo root = new ViewInfo("LinearLayout", rootNode, 10, 10, 100, 100); 166 UiViewElementNode child1Node = createNode(rootNode, "android.widget.Button", false); 167 ViewInfo child1 = new ViewInfo("CheckBox", child1Node, 0, 0, 50, 20); 168 UiViewElementNode child2Node = createNode(rootNode, "include", true); 169 ViewInfo child2 = new ViewInfo("RelativeLayout", child2Node, 0, 20, 70, 25); 170 root.setChildren(Arrays.asList(child1, child2)); 171 ViewInfo child21 = new ViewInfo("RadioButton", null, 0, 20, 70, 25); 172 child2.setChildren(Arrays.asList(child21)); 173 174 CanvasViewInfo rootView = CanvasViewInfo.create(root, layoutlib5).getFirst(); 175 assertNotNull(rootView); 176 assertEquals("LinearLayout", rootView.getName()); 177 assertEquals(new Rectangle(10, 10, 89, 89), rootView.getAbsRect()); 178 assertEquals(new Rectangle(10, 10, 89, 89), rootView.getSelectionRect()); 179 assertNull(rootView.getParent()); 180 assertSame(rootNode, rootView.getUiViewNode()); 181 assertEquals(2, rootView.getChildren().size()); 182 183 CanvasViewInfo childView1 = rootView.getChildren().get(0); 184 CanvasViewInfo includedView = rootView.getChildren().get(1); 185 186 assertEquals("CheckBox", childView1.getName()); 187 assertSame(rootView, childView1.getParent()); 188 assertEquals(new Rectangle(10, 10, 49, 19), childView1.getAbsRect()); 189 assertEquals(new Rectangle(10, 10, 49, 19), childView1.getSelectionRect()); 190 assertSame(childView1.getUiViewNode(), child1Node); 191 192 assertEquals("RelativeLayout", includedView.getName()); 193 assertSame(rootView, includedView.getParent()); 194 assertEquals(new Rectangle(10, 30, 69, 4), includedView.getAbsRect()); 195 assertEquals(new Rectangle(10, 30, 69, 5), includedView.getSelectionRect()); 196 assertSame(includedView.getUiViewNode(), child2Node); 197 assertEquals(0, includedView.getChildren().size()); 198 } 199 testNoIncludeTag()200 public void testNoIncludeTag() throws Exception { 201 boolean layoutlib5 = false; 202 203 // Test rendering of included views on layoutlib 4- (e.g. no <include> tag cookie 204 // in view info) 205 206 UiViewElementNode rootNode = createNode("android.widget.LinearLayout", true); 207 ViewInfo root = new ViewInfo("LinearLayout", rootNode, 10, 10, 100, 100); 208 UiViewElementNode child1Node = createNode(rootNode, "android.widget.Button", false); 209 ViewInfo child1 = new ViewInfo("CheckBox", child1Node, 0, 0, 50, 20); 210 UiViewElementNode child2Node = createNode(rootNode, "include", true); 211 ViewInfo child2 = new ViewInfo("RelativeLayout", null /* layoutlib 4 */, 0, 20, 70, 25); 212 root.setChildren(Arrays.asList(child1, child2)); 213 ViewInfo child21 = new ViewInfo("RadioButton", null, 0, 20, 70, 25); 214 child2.setChildren(Arrays.asList(child21)); 215 216 CanvasViewInfo rootView = CanvasViewInfo.create(root, layoutlib5).getFirst(); 217 assertNotNull(rootView); 218 assertEquals("LinearLayout", rootView.getName()); 219 assertEquals(new Rectangle(10, 10, 89, 89), rootView.getAbsRect()); 220 assertEquals(new Rectangle(10, 10, 89, 89), rootView.getSelectionRect()); 221 assertNull(rootView.getParent()); 222 assertSame(rootNode, rootView.getUiViewNode()); 223 assertEquals(2, rootView.getChildren().size()); 224 225 CanvasViewInfo childView1 = rootView.getChildren().get(0); 226 CanvasViewInfo includedView = rootView.getChildren().get(1); 227 228 assertEquals("CheckBox", childView1.getName()); 229 assertSame(rootView, childView1.getParent()); 230 assertEquals(new Rectangle(10, 10, 49, 19), childView1.getAbsRect()); 231 assertEquals(new Rectangle(10, 10, 49, 19), childView1.getSelectionRect()); 232 assertSame(childView1.getUiViewNode(), child1Node); 233 234 assertEquals("RelativeLayout", includedView.getName()); 235 assertSame(rootView, includedView.getParent()); 236 assertEquals(new Rectangle(10, 30, 69, 4), includedView.getAbsRect()); 237 assertEquals(new Rectangle(10, 30, 69, 5), includedView.getSelectionRect()); 238 assertSame(includedView.getUiViewNode(), child2Node); 239 assertEquals(0, includedView.getChildren().size()); 240 } 241 testMergeMatching()242 public void testMergeMatching() throws Exception { 243 boolean layoutlib5 = false; 244 245 // Test rendering of MULTIPLE included views or when there is no simple match 246 // between view info and ui element node children 247 248 UiViewElementNode rootNode = createNode("android.widget.LinearLayout", true); 249 ViewInfo root = new ViewInfo("LinearLayout", rootNode, 10, 10, 100, 100); 250 UiViewElementNode child1Node = createNode(rootNode, "android.widget.Button", false); 251 ViewInfo child1 = new ViewInfo("CheckBox", child1Node, 0, 0, 50, 20); 252 UiViewElementNode multiChildNode1 = createNode(rootNode, "foo", true); 253 UiViewElementNode multiChildNode2 = createNode(rootNode, "bar", true); 254 ViewInfo child2 = new ViewInfo("RelativeLayout", null, 0, 20, 70, 25); 255 ViewInfo child3 = new ViewInfo("AbsoluteLayout", null, 10, 40, 50, 15); 256 root.setChildren(Arrays.asList(child1, child2, child3)); 257 ViewInfo child21 = new ViewInfo("RadioButton", null, 0, 20, 70, 25); 258 child2.setChildren(Arrays.asList(child21)); 259 260 CanvasViewInfo rootView = CanvasViewInfo.create(root, layoutlib5).getFirst(); 261 assertNotNull(rootView); 262 assertEquals("LinearLayout", rootView.getName()); 263 assertEquals(new Rectangle(10, 10, 89, 89), rootView.getAbsRect()); 264 assertEquals(new Rectangle(10, 10, 89, 89), rootView.getSelectionRect()); 265 assertNull(rootView.getParent()); 266 assertTrue(rootView.isRoot()); 267 assertSame(rootNode, rootView.getUiViewNode()); 268 assertEquals(3, rootView.getChildren().size()); 269 270 CanvasViewInfo childView1 = rootView.getChildren().get(0); 271 assertFalse(childView1.isRoot()); 272 CanvasViewInfo includedView1 = rootView.getChildren().get(1); 273 assertFalse(includedView1.isRoot()); 274 CanvasViewInfo includedView2 = rootView.getChildren().get(2); 275 assertFalse(includedView1.isRoot()); 276 277 assertEquals("CheckBox", childView1.getName()); 278 assertSame(rootView, childView1.getParent()); 279 assertEquals(new Rectangle(10, 10, 49, 19), childView1.getAbsRect()); 280 assertEquals(new Rectangle(10, 10, 49, 19), childView1.getSelectionRect()); 281 assertSame(childView1.getUiViewNode(), child1Node); 282 283 assertEquals("RelativeLayout", includedView1.getName()); 284 assertSame(multiChildNode1, includedView1.getUiViewNode()); 285 assertEquals("foo", includedView1.getUiViewNode().getDescriptor().getXmlName()); 286 assertSame(multiChildNode2, includedView2.getUiViewNode()); 287 assertEquals("AbsoluteLayout", includedView2.getName()); 288 assertEquals("bar", includedView2.getUiViewNode().getDescriptor().getXmlName()); 289 assertSame(rootView, includedView1.getParent()); 290 assertSame(rootView, includedView2.getParent()); 291 assertEquals(new Rectangle(10, 30, 69, 4), includedView1.getAbsRect()); 292 assertEquals(new Rectangle(10, 30, 69, 5), includedView1.getSelectionRect()); 293 assertEquals(new Rectangle(20, 50, 39, -26), includedView2.getAbsRect()); 294 assertEquals(new Rectangle(20, 35, 39, 5), includedView2.getSelectionRect()); 295 assertEquals(0, includedView1.getChildren().size()); 296 assertEquals(0, includedView2.getChildren().size()); 297 } 298 testMerge()299 public void testMerge() throws Exception { 300 boolean layoutlib5 = false; 301 302 // Test rendering of MULTIPLE included views or when there is no simple match 303 // between view info and ui element node children 304 305 UiViewElementNode rootNode = createNode("android.widget.LinearLayout", true); 306 ViewInfo root = new ViewInfo("LinearLayout", rootNode, 10, 10, 100, 100); 307 UiViewElementNode child1Node = createNode(rootNode, "android.widget.Button", false); 308 ViewInfo child1 = new ViewInfo("CheckBox", child1Node, 0, 0, 50, 20); 309 UiViewElementNode multiChildNode = createNode(rootNode, "foo", true); 310 ViewInfo child2 = new ViewInfo("RelativeLayout", null, 0, 20, 70, 25); 311 ViewInfo child3 = new ViewInfo("AbsoluteLayout", null, 10, 40, 50, 15); 312 root.setChildren(Arrays.asList(child1, child2, child3)); 313 ViewInfo child21 = new ViewInfo("RadioButton", null, 0, 20, 70, 25); 314 child2.setChildren(Arrays.asList(child21)); 315 316 CanvasViewInfo rootView = CanvasViewInfo.create(root, layoutlib5).getFirst(); 317 assertNotNull(rootView); 318 assertEquals("LinearLayout", rootView.getName()); 319 assertEquals(new Rectangle(10, 10, 89, 89), rootView.getAbsRect()); 320 assertEquals(new Rectangle(10, 10, 89, 89), rootView.getSelectionRect()); 321 assertNull(rootView.getParent()); 322 assertSame(rootNode, rootView.getUiViewNode()); 323 assertEquals(2, rootView.getChildren().size()); 324 325 CanvasViewInfo childView1 = rootView.getChildren().get(0); 326 CanvasViewInfo includedView = rootView.getChildren().get(1); 327 328 assertEquals("CheckBox", childView1.getName()); 329 assertSame(rootView, childView1.getParent()); 330 assertEquals(new Rectangle(10, 10, 49, 19), childView1.getAbsRect()); 331 assertEquals(new Rectangle(10, 10, 49, 19), childView1.getSelectionRect()); 332 assertSame(childView1.getUiViewNode(), child1Node); 333 334 assertEquals("RelativeLayout", includedView.getName()); 335 assertSame(rootView, includedView.getParent()); 336 assertEquals(new Rectangle(10, 30, 69, 4), includedView.getAbsRect()); 337 assertEquals(new Rectangle(10, 30, 69, 5), includedView.getSelectionRect()); 338 assertEquals(0, includedView.getChildren().size()); 339 assertSame(multiChildNode, includedView.getUiViewNode()); 340 } 341 testInsertMerge()342 public void testInsertMerge() throws Exception { 343 boolean layoutlib5 = false; 344 345 // Test rendering of MULTIPLE included views or when there is no simple match 346 // between view info and ui element node children 347 348 UiViewElementNode mergeNode = createNode("merge", true); 349 UiViewElementNode rootNode = createNode(mergeNode, "android.widget.Button", false); 350 ViewInfo root = new ViewInfo("Button", rootNode, 10, 10, 100, 100); 351 352 CanvasViewInfo rootView = CanvasViewInfo.create(root, layoutlib5).getFirst(); 353 assertNotNull(rootView); 354 assertEquals("merge", rootView.getName()); 355 assertSame(rootView.getUiViewNode(), mergeNode); 356 assertEquals(new Rectangle(10, 10, 89, 89), rootView.getAbsRect()); 357 assertEquals(new Rectangle(10, 10, 89, 89), rootView.getSelectionRect()); 358 assertNull(rootView.getParent()); 359 assertSame(mergeNode, rootView.getUiViewNode()); 360 assertEquals(1, rootView.getChildren().size()); 361 362 CanvasViewInfo childView1 = rootView.getChildren().get(0); 363 364 assertEquals("Button", childView1.getName()); 365 assertSame(rootView, childView1.getParent()); 366 assertEquals(new Rectangle(10, 10, 89, 89), childView1.getAbsRect()); 367 assertEquals(new Rectangle(10, 10, 89, 89), childView1.getSelectionRect()); 368 assertSame(childView1.getUiViewNode(), rootNode); 369 } 370 testUnmatchedMissing()371 public void testUnmatchedMissing() throws Exception { 372 boolean layoutlib5 = false; 373 374 UiViewElementNode rootNode = createNode("android.widget.LinearLayout", true); 375 ViewInfo root = new ViewInfo("LinearLayout", rootNode, 0, 0, 100, 100); 376 List<ViewInfo> children = new ArrayList<ViewInfo>(); 377 // Should be matched up with corresponding node: 378 Set<Integer> missingKeys = new HashSet<Integer>(); 379 // Should not be matched with any views, but should get view created: 380 Set<Integer> extraKeys = new HashSet<Integer>(); 381 // Should not be matched with any nodes 382 Set<Integer> extraViews = new HashSet<Integer>(); 383 int numViews = 30; 384 missingKeys.add(0); 385 missingKeys.add(4); 386 missingKeys.add(14); 387 missingKeys.add(29); 388 extraKeys.add(9); 389 extraKeys.add(20); 390 extraKeys.add(22); 391 extraViews.add(18); 392 extraViews.add(24); 393 394 List<String> expectedViewNames = new ArrayList<String>(); 395 List<String> expectedNodeNames = new ArrayList<String>(); 396 397 for (int i = 0; i < numViews; i++) { 398 UiViewElementNode childNode = null; 399 if (!extraViews.contains(i)) { 400 childNode = createNode(rootNode, "childNode" + i, false); 401 } 402 Object cookie = missingKeys.contains(i) || extraViews.contains(i) ? null : childNode; 403 ViewInfo childView = new ViewInfo("childView" + i, cookie, 404 0, i * 20, 50, (i + 1) * 20); 405 children.add(childView); 406 407 if (!extraViews.contains(i)) { 408 expectedViewNames.add("childView" + i); 409 expectedNodeNames.add("childNode" + i); 410 } 411 412 if (extraKeys.contains(i)) { 413 createNode(rootNode, "extraNodeAt" + i, false); 414 415 expectedViewNames.add("extraNodeAt" + i); 416 expectedNodeNames.add("extraNodeAt" + i); 417 } 418 } 419 root.setChildren(children); 420 421 CanvasViewInfo rootView = CanvasViewInfo.create(root, layoutlib5).getFirst(); 422 assertNotNull(rootView); 423 424 // dump(root, 0); 425 // dump(rootView, 0); 426 427 assertEquals("LinearLayout", rootView.getName()); 428 assertNull(rootView.getParent()); 429 assertSame(rootNode, rootView.getUiViewNode()); 430 assertEquals(numViews + extraKeys.size() - extraViews.size(), rootNode.getUiChildren() 431 .size()); 432 assertEquals(numViews + extraKeys.size() - extraViews.size(), 433 rootView.getChildren().size()); 434 assertEquals(expectedViewNames.size(), rootView.getChildren().size()); 435 for (int i = 0, n = rootView.getChildren().size(); i < n; i++) { 436 CanvasViewInfo childView = rootView.getChildren().get(i); 437 String expectedViewName = expectedViewNames.get(i); 438 String expectedNodeName = expectedNodeNames.get(i); 439 assertEquals(expectedViewName, childView.getName()); 440 assertNotNull(childView.getUiViewNode()); 441 assertEquals(expectedNodeName, childView.getUiViewNode().getDescriptor().getXmlName()); 442 } 443 } 444 testMergeCookies()445 public void testMergeCookies() throws Exception { 446 boolean layoutlib5 = true; 447 448 UiViewElementNode rootNode = createNode("android.widget.LinearLayout", true); 449 ViewInfo root = new ViewInfo("LinearLayout", rootNode, 0, 0, 100, 100); 450 451 // Create the merge cookies in the opposite order to ensure that we don't 452 // apply our own logic when matching up views with nodes 453 LinkedList<MergeCookie> cookies = new LinkedList<MergeCookie>(); 454 for (int i = 0; i < 10; i++) { 455 UiViewElementNode node = createNode(rootNode, "childNode" + i, false); 456 cookies.addFirst(new MergeCookie(node)); 457 } 458 Iterator<MergeCookie> it = cookies.iterator(); 459 ArrayList<ViewInfo> children = new ArrayList<ViewInfo>(); 460 for (int i = 0; i < 10; i++) { 461 ViewInfo childView = new ViewInfo("childView" + i, it.next(), 0, i * 20, 50, 462 (i + 1) * 20); 463 children.add(childView); 464 } 465 root.setChildren(children); 466 467 CanvasViewInfo rootView = CanvasViewInfo.create(root, layoutlib5).getFirst(); 468 assertNotNull(rootView); 469 470 assertEquals("LinearLayout", rootView.getName()); 471 assertNull(rootView.getParent()); 472 assertSame(rootNode, rootView.getUiViewNode()); 473 for (int i = 0, n = rootView.getChildren().size(); i < n; i++) { 474 CanvasViewInfo childView = rootView.getChildren().get(i); 475 assertEquals("childView" + i, childView.getName()); 476 assertEquals("childNode" + (9 - i), childView.getUiViewNode().getDescriptor() 477 .getXmlName()); 478 } 479 } 480 testMergeCookies2()481 public void testMergeCookies2() throws Exception { 482 boolean layoutlib5 = true; 483 484 UiViewElementNode rootNode = createNode("android.widget.LinearLayout", true); 485 ViewInfo root = new ViewInfo("LinearLayout", rootNode, 0, 0, 100, 100); 486 487 UiViewElementNode node1 = createNode(rootNode, "childNode1", false); 488 UiViewElementNode node2 = createNode(rootNode, "childNode2", false); 489 MergeCookie cookie1 = new MergeCookie(node1); 490 MergeCookie cookie2 = new MergeCookie(node2); 491 492 // Sets alternating merge cookies and checks whether the node sibling lists are 493 // okay and merged correctly 494 495 ArrayList<ViewInfo> children = new ArrayList<ViewInfo>(); 496 for (int i = 0; i < 10; i++) { 497 Object cookie = (i % 2) == 0 ? cookie1 : cookie2; 498 ViewInfo childView = new ViewInfo("childView" + i, cookie, 0, i * 20, 50, 499 (i + 1) * 20); 500 children.add(childView); 501 } 502 root.setChildren(children); 503 504 Pair<CanvasViewInfo, List<Rectangle>> result = CanvasViewInfo.create(root, layoutlib5); 505 CanvasViewInfo rootView = result.getFirst(); 506 List<Rectangle> bounds = result.getSecond(); 507 assertNull(bounds); 508 assertNotNull(rootView); 509 510 assertEquals("LinearLayout", rootView.getName()); 511 assertNull(rootView.getParent()); 512 assertSame(rootNode, rootView.getUiViewNode()); 513 assertEquals(10, rootView.getChildren().size()); 514 assertEquals(2, rootView.getUniqueChildren().size()); 515 for (int i = 0, n = rootView.getChildren().size(); i < n; i++) { 516 CanvasViewInfo childView = rootView.getChildren().get(i); 517 assertEquals("childView" + i, childView.getName()); 518 Object cookie = (i % 2) == 0 ? node1 : node2; 519 assertSame(cookie, childView.getUiViewNode()); 520 List<CanvasViewInfo> nodeSiblings = childView.getNodeSiblings(); 521 assertEquals(5, nodeSiblings.size()); 522 } 523 List<CanvasViewInfo> nodeSiblings = rootView.getChildren().get(0).getNodeSiblings(); 524 for (int j = 0; j < 5; j++) { 525 assertEquals("childView" + (j * 2), nodeSiblings.get(j).getName()); 526 } 527 nodeSiblings = rootView.getChildren().get(1).getNodeSiblings(); 528 for (int j = 0; j < 5; j++) { 529 assertEquals("childView" + (j * 2 + 1), nodeSiblings.get(j).getName()); 530 } 531 } 532 testIncludeBounds()533 public void testIncludeBounds() throws Exception { 534 boolean layoutlib5 = true; 535 536 UiViewElementNode rootNode = createNode("android.widget.LinearLayout", true); 537 ViewInfo root = new ViewInfo("included", null, 0, 0, 100, 100); 538 539 UiViewElementNode node1 = createNode(rootNode, "childNode1", false); 540 UiViewElementNode node2 = createNode(rootNode, "childNode2", false); 541 MergeCookie cookie1 = new MergeCookie(node1); 542 MergeCookie cookie2 = new MergeCookie(node2); 543 544 // Sets alternating merge cookies and checks whether the node sibling lists are 545 // okay and merged correctly 546 547 ArrayList<ViewInfo> children = new ArrayList<ViewInfo>(); 548 for (int i = 0; i < 10; i++) { 549 Object cookie = (i % 2) == 0 ? cookie1 : cookie2; 550 ViewInfo childView = new ViewInfo("childView" + i, cookie, 0, i * 20, 50, 551 (i + 1) * 20); 552 children.add(childView); 553 } 554 root.setChildren(children); 555 556 Pair<CanvasViewInfo, List<Rectangle>> result = CanvasViewInfo.create(root, layoutlib5); 557 CanvasViewInfo rootView = result.getFirst(); 558 List<Rectangle> bounds = result.getSecond(); 559 assertNotNull(rootView); 560 561 assertEquals("included", rootView.getName()); 562 assertNull(rootView.getParent()); 563 assertNull(rootView.getUiViewNode()); 564 assertEquals(10, rootView.getChildren().size()); 565 assertEquals(2, rootView.getUniqueChildren().size()); 566 for (int i = 0, n = rootView.getChildren().size(); i < n; i++) { 567 CanvasViewInfo childView = rootView.getChildren().get(i); 568 assertEquals("childView" + i, childView.getName()); 569 Object cookie = (i % 2) == 0 ? node1 : node2; 570 assertSame(cookie, childView.getUiViewNode()); 571 List<CanvasViewInfo> nodeSiblings = childView.getNodeSiblings(); 572 assertEquals(5, nodeSiblings.size()); 573 } 574 List<CanvasViewInfo> nodeSiblings = rootView.getChildren().get(0).getNodeSiblings(); 575 for (int j = 0; j < 5; j++) { 576 assertEquals("childView" + (j * 2), nodeSiblings.get(j).getName()); 577 } 578 nodeSiblings = rootView.getChildren().get(1).getNodeSiblings(); 579 for (int j = 0; j < 5; j++) { 580 assertEquals("childView" + (j * 2 + 1), nodeSiblings.get(j).getName()); 581 } 582 583 // Only show the primary bounds as included 584 assertEquals(2, bounds.size()); 585 assertEquals(new Rectangle(0, 0, 49, 19), bounds.get(0)); 586 assertEquals(new Rectangle(0, 20, 49, 19), bounds.get(1)); 587 } 588 testIncludeBounds2()589 public void testIncludeBounds2() throws Exception { 590 includeBounds2(false); 591 } 592 testIncludeBounds2LayoutLib5()593 public void testIncludeBounds2LayoutLib5() throws Exception { 594 includeBounds2(true); 595 } 596 includeBounds2(boolean layoutlib5)597 public void includeBounds2(boolean layoutlib5) throws Exception { 598 599 UiViewElementNode rootNode = createNode("android.widget.LinearLayout", true); 600 ViewInfo root = new ViewInfo("included", null, 0, 0, 100, 100); 601 602 UiViewElementNode node1 = createNode(rootNode, "childNode1", false); 603 UiViewElementNode node2 = createNode(rootNode, "childNode2", false); 604 605 ViewInfo childView1 = new ViewInfo("childView1", node1, 0, 20, 50, 40); 606 ViewInfo childView2 = new ViewInfo("childView2", node2, 0, 40, 50, 60); 607 608 root.setChildren(Arrays.asList(childView1, childView2)); 609 610 Pair<CanvasViewInfo, List<Rectangle>> result = CanvasViewInfo.create(root, layoutlib5); 611 CanvasViewInfo rootView = result.getFirst(); 612 List<Rectangle> bounds = result.getSecond(); 613 assertNotNull(rootView); 614 615 assertEquals("included", rootView.getName()); 616 assertNull(rootView.getParent()); 617 assertNull(rootView.getUiViewNode()); 618 assertEquals(2, rootView.getChildren().size()); 619 assertEquals(2, rootView.getUniqueChildren().size()); 620 621 Rectangle bounds1 = bounds.get(0); 622 Rectangle bounds2 = bounds.get(1); 623 assertEquals(new Rectangle(0, 20, 49, 19), bounds1); 624 assertEquals(new Rectangle(0, 40, 49, 19), bounds2); 625 } 626 testCookieWorkaround()627 public void testCookieWorkaround() throws Exception { 628 UiViewElementNode rootNode = createNode("android.widget.LinearLayout", true); 629 ViewInfo root = new ViewInfo("included", null, 0, 0, 100, 100); 630 631 UiViewElementNode node2 = createNode(rootNode, "childNode2", false); 632 MergeCookie mergeCookie = new MergeCookie(root); 633 634 ViewInfo childView1 = new ViewInfo("childView1", mergeCookie, 0, 20, 50, 40); 635 ViewInfo childView2 = new ViewInfo("childView2", node2, 0, 40, 50, 60); 636 637 root.setChildren(Arrays.asList(childView1, childView2)); 638 639 Pair<CanvasViewInfo, List<Rectangle>> result = CanvasViewInfo.create(root, true); 640 CanvasViewInfo rootView = result.getFirst(); 641 List<Rectangle> bounds = result.getSecond(); 642 assertNotNull(rootView); 643 644 assertEquals("included", rootView.getName()); 645 assertNull(rootView.getParent()); 646 assertNull(rootView.getUiViewNode()); 647 // childView1 should have been removed since it has the wrong merge cookie 648 assertEquals(1, rootView.getChildren().size()); 649 assertEquals(1, rootView.getUniqueChildren().size()); 650 651 Rectangle bounds1 = bounds.get(0); 652 assertEquals(new Rectangle(0, 40, 49, 19), bounds1); 653 } 654 testGestureOverlayView()655 public void testGestureOverlayView() throws Exception { 656 boolean layoutlib5 = true; 657 658 // Test rendering of included views on layoutlib 5+ (e.g. has <include> tag) 659 660 UiViewElementNode rootNode = createNode("android.gesture.GestureOverlayView", true); 661 UiViewElementNode childNode = createNode(rootNode, "android.widget.LinearLayout", false); 662 UiViewElementNode grandChildNode = createNode(childNode, "android.widget.Button", false); 663 ViewInfo root = new ViewInfo("GestureOverlayView", rootNode, 10, 10, 100, 100); 664 ViewInfo child = new ViewInfo("LinearLayout", childNode, 0, 0, 50, 20); 665 root.setChildren(Collections.singletonList(child)); 666 ViewInfo grandChild = new ViewInfo("Button", grandChildNode, 0, 20, 70, 25); 667 child.setChildren(Collections.singletonList(grandChild)); 668 CanvasViewInfo rootView = CanvasViewInfo.create(root, layoutlib5).getFirst(); 669 assertNotNull(rootView); 670 assertEquals("GestureOverlayView", rootView.getName()); 671 672 assertTrue(rootView.isRoot()); 673 assertNull(rootView.getParent()); 674 assertSame(rootNode, rootView.getUiViewNode()); 675 assertEquals(1, rootView.getChildren().size()); 676 677 CanvasViewInfo childView = rootView.getChildren().get(0); 678 assertEquals("LinearLayout", childView.getName()); 679 680 // This should also be a root for the special case that the root is 681 assertTrue(childView.isRoot()); 682 683 assertEquals(1, childView.getChildren().size()); 684 CanvasViewInfo grandChildView = childView.getChildren().get(0); 685 assertEquals("Button", grandChildView.getName()); 686 assertFalse(grandChildView.isRoot()); 687 } 688 testListView()689 public void testListView() throws Exception { 690 // For ListViews we get AdapterItemReferences as cookies. Ensure that this 691 // works properly. 692 // 693 // android.widget.FrameLayout [0,50,320,480] <FrameLayout> 694 // android.widget.ListView [0,0,320,430] <ListView> 695 // android.widget.LinearLayout [0,0,320,17] SessionParams$AdapterItemReference 696 // android.widget.TextView [0,0,73,17] 697 // android.widget.LinearLayout [0,18,320,35] SessionParams$AdapterItemReference 698 // android.widget.TextView [0,0,73,17] 699 // android.widget.LinearLayout [0,36,320,53] SessionParams$AdapterItemReference 700 // android.widget.TextView [0,0,73,17] 701 // ... 702 703 UiViewElementNode rootNode = createNode("FrameLayout", true); 704 UiViewElementNode childNode = createNode(rootNode, "ListView", false); 705 /*UiViewElementNode grandChildNode =*/ createNode(childNode, "LinearLayout", false); 706 /*UiViewElementNode greatGrandChildNode =*/ createNode(childNode, "TextView", false); 707 DataBindingItem dataBindingItem = new DataBindingItem("foo"); 708 709 ViewInfo root = new ViewInfo("FrameLayout", rootNode, 0, 50, 320, 480); 710 ViewInfo child = new ViewInfo("ListView", childNode, 0, 0, 320, 430); 711 root.setChildren(Collections.singletonList(child)); 712 ViewInfo grandChild = new ViewInfo("LinearLayout", dataBindingItem, 0, 0, 320, 17); 713 child.setChildren(Collections.singletonList(grandChild)); 714 ViewInfo greatGrandChild = new ViewInfo("Button", null, 0, 0, 73, 17); 715 grandChild.setChildren(Collections.singletonList(greatGrandChild)); 716 CanvasViewInfo rootView = CanvasViewInfo.create(root, true /*layoutlib5*/).getFirst(); 717 assertNotNull(rootView); 718 719 assertEquals("FrameLayout", rootView.getName()); 720 assertEquals(1, rootView.getChildren().size()); 721 assertSame(rootNode, rootView.getUiViewNode()); 722 723 CanvasViewInfo childView = rootView.getChildren().get(0); 724 assertEquals("ListView", childView.getName()); 725 assertEquals(0, childView.getChildren().size()); 726 assertSame(childNode, childView.getUiViewNode()); 727 } 728 729 /** 730 * Dumps out the given {@link ViewInfo} hierarchy to standard out. 731 * Useful during development. 732 * 733 * @param graphicalEditor the editor associated with this hierarchy 734 * @param root the root of the {@link ViewInfo} hierarchy 735 */ dump(GraphicalEditorPart graphicalEditor, ViewInfo root)736 public static void dump(GraphicalEditorPart graphicalEditor, ViewInfo root) { 737 System.out.println("\n\nRendering:"); 738 boolean supportsEmbedding = graphicalEditor.renderingSupports(Capability.EMBEDDED_LAYOUT); 739 System.out.println("Supports Embedded Layout=" + supportsEmbedding); 740 System.out.println("Rendering context=" + graphicalEditor.getIncludedWithin()); 741 dump(root, 0); 742 } 743 744 /** Helper for {@link #dump(GraphicalEditorPart, ViewInfo)} */ dump(ViewInfo info, int depth)745 public static void dump(ViewInfo info, int depth) { 746 StringBuilder sb = new StringBuilder(); 747 for (int i = 0; i < depth; i++) { 748 sb.append(" "); 749 } 750 sb.append(info.getClassName()); 751 sb.append(" ["); 752 sb.append(info.getLeft()); 753 sb.append(","); 754 sb.append(info.getTop()); 755 sb.append(","); 756 sb.append(info.getRight()); 757 sb.append(","); 758 sb.append(info.getBottom()); 759 sb.append("] "); 760 Object cookie = info.getCookie(); 761 if (cookie instanceof UiViewElementNode) { 762 sb.append(" "); 763 UiViewElementNode node = (UiViewElementNode) cookie; 764 sb.append("<"); 765 sb.append(node.getDescriptor().getXmlName()); 766 sb.append("> "); 767 } else if (cookie != null) { 768 sb.append(" cookie=" + cookie); 769 } 770 771 System.out.println(sb.toString()); 772 773 for (ViewInfo child : info.getChildren()) { 774 dump(child, depth + 1); 775 } 776 } 777 778 /** Helper for {@link #dump(GraphicalEditorPart, ViewInfo)} */ dump(CanvasViewInfo info, int depth)779 public static void dump(CanvasViewInfo info, int depth) { 780 StringBuilder sb = new StringBuilder(); 781 for (int i = 0; i < depth; i++) { 782 sb.append(" "); 783 } 784 sb.append(info.getName()); 785 sb.append(" ["); 786 sb.append(info.getAbsRect()); 787 sb.append("], node="); 788 sb.append(info.getUiViewNode()); 789 790 System.out.println(sb.toString()); 791 792 for (CanvasViewInfo child : info.getChildren()) { 793 dump(child, depth + 1); 794 } 795 } 796 797 } 798