• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.common.layout;
18 
19 import static com.android.SdkConstants.ANDROID_URI;
20 import static com.android.SdkConstants.ATTR_ID;
21 import static com.android.SdkConstants.ATTR_LAYOUT_HEIGHT;
22 import static com.android.SdkConstants.ATTR_LAYOUT_WIDTH;
23 import static com.android.SdkConstants.ATTR_ORIENTATION;
24 import static com.android.SdkConstants.VALUE_HORIZONTAL;
25 import static com.android.SdkConstants.VALUE_VERTICAL;
26 
27 import com.android.ide.common.api.DropFeedback;
28 import com.android.ide.common.api.IAttributeInfo.Format;
29 import com.android.ide.common.api.IDragElement;
30 import com.android.ide.common.api.IMenuCallback;
31 import com.android.ide.common.api.INode;
32 import com.android.ide.common.api.IViewRule;
33 import com.android.ide.common.api.Point;
34 import com.android.ide.common.api.Rect;
35 import com.android.ide.common.api.RuleAction;
36 import com.android.ide.common.api.RuleAction.NestedAction;
37 
38 import java.util.ArrayList;
39 import java.util.Arrays;
40 import java.util.Collections;
41 import java.util.List;
42 
43 /** Test the {@link LinearLayoutRule} */
44 public class LinearLayoutRuleTest extends LayoutTestBase {
45     // Utility for other tests
dragIntoEmpty(Rect dragBounds)46     protected void dragIntoEmpty(Rect dragBounds) {
47         boolean haveBounds = dragBounds.isValid();
48 
49         IViewRule rule = new LinearLayoutRule();
50 
51         INode targetNode = TestNode.create("android.widget.LinearLayout").id(
52         "@+id/LinearLayout01").bounds(new Rect(0, 0, 240, 480));
53         Point dropPoint = new Point(10, 5);
54 
55         IDragElement[] elements = TestDragElement.create(TestDragElement.create(
56                 "android.widget.Button", dragBounds).id("@+id/Button01"));
57 
58         // Enter target
59         DropFeedback feedback = rule.onDropEnter(targetNode, null/*targetView*/, elements);
60         assertNotNull(feedback);
61         assertFalse(feedback.invalidTarget);
62         assertNotNull(feedback.painter);
63 
64         feedback = rule.onDropMove(targetNode, elements, feedback, dropPoint);
65         assertNotNull(feedback);
66         assertFalse(feedback.invalidTarget);
67 
68         // Paint feedback and make sure it's what we expect
69         TestGraphics graphics = new TestGraphics();
70         assertNotNull(feedback.painter);
71         feedback.painter.paint(graphics, targetNode, feedback);
72         assertEquals(
73                 // Expect to see a recipient rectangle around the bounds of the
74                 // LinearLayout,
75                 // as well as a single vertical line as a drop preview located
76                 // along the left
77                 // edge (for this horizontal linear layout) showing insert
78                 // position at index 0,
79                 // and finally a rectangle for the bounds of the inserted button
80                 // centered over
81                 // the middle
82                 "[useStyle(DROP_RECIPIENT), "
83                         +
84                         // Bounds rectangle
85                         "drawRect(Rect[0,0,240,480]), "
86                         + "useStyle(DROP_ZONE), drawLine(1,0,1,480), "
87                         + "useStyle(DROP_ZONE_ACTIVE), " + "useStyle(DROP_PREVIEW), " +
88                         // Insert position line
89                         "drawLine(1,0,1,480)" + (haveBounds ?
90                         // Outline of dragged node centered over position line
91                         ", useStyle(DROP_PREVIEW), " + "drawRect(1,0,101,80)"
92                                 // Nothing when we don't have bounds
93                                 : "") + "]", graphics.getDrawn().toString());
94 
95         // Attempt a drop
96         assertEquals(0, targetNode.getChildren().length);
97         rule.onDropped(targetNode, elements, feedback, dropPoint);
98         assertEquals(1, targetNode.getChildren().length);
99         assertEquals("@+id/Button01", targetNode.getChildren()[0].getStringAttr(
100                 ANDROID_URI, ATTR_ID));
101     }
102 
103     // Utility for other tests
dragInto(boolean vertical, Rect dragBounds, Point dragPoint, int insertIndex, int currentIndex, String... graphicsFragments)104     protected INode dragInto(boolean vertical, Rect dragBounds, Point dragPoint,
105             int insertIndex, int currentIndex,
106             String... graphicsFragments) {
107         INode linearLayout = TestNode.create("android.widget.LinearLayout").id(
108                 "@+id/LinearLayout01").bounds(new Rect(0, 0, 240, 480)).set(ANDROID_URI,
109                 ATTR_ORIENTATION,
110                 vertical ? VALUE_VERTICAL : VALUE_HORIZONTAL)
111                 .add(
112                         TestNode.create("android.widget.Button").id("@+id/Button01").bounds(
113                                 new Rect(0, 0, 100, 80)),
114                         TestNode.create("android.widget.Button").id("@+id/Button02").bounds(
115                                 new Rect(0, 100, 100, 80)),
116                         TestNode.create("android.widget.Button").id("@+id/Button03").bounds(
117                                 new Rect(0, 200, 100, 80)),
118                         TestNode.create("android.widget.Button").id("@+id/Button04").bounds(
119                                 new Rect(0, 300, 100, 80)));
120 
121         return super.dragInto(new LinearLayoutRule(), linearLayout, dragBounds, dragPoint, null,
122                 insertIndex, currentIndex, graphicsFragments);
123     }
124 
125     // Check that the context menu registers the expected menu items
testContextMenu()126     public void testContextMenu() {
127         LinearLayoutRule rule = new LinearLayoutRule();
128         initialize(rule, "android.widget.LinearLayout");
129         INode node = TestNode.create("android.widget.Button").id("@+id/Button012");
130 
131         List<RuleAction> contextMenu = new ArrayList<RuleAction>();
132         rule.addContextMenuActions(contextMenu, node);
133         assertEquals(6, contextMenu.size());
134         assertEquals("Edit ID...", contextMenu.get(0).getTitle());
135         assertTrue(contextMenu.get(1) instanceof RuleAction.Separator);
136         assertEquals("Layout Width", contextMenu.get(2).getTitle());
137         assertEquals("Layout Height", contextMenu.get(3).getTitle());
138         assertTrue(contextMenu.get(4) instanceof RuleAction.Separator);
139         assertEquals("Other Properties", contextMenu.get(5).getTitle());
140 
141         RuleAction propertiesMenu = contextMenu.get(5);
142         assertTrue(propertiesMenu.getClass().getName(),
143                 propertiesMenu instanceof NestedAction);
144     }
145 
testContextMenuCustom()146     public void testContextMenuCustom() {
147         LinearLayoutRule rule = new LinearLayoutRule();
148         initialize(rule, "android.widget.LinearLayout");
149         INode node = TestNode.create("android.widget.LinearLayout").id("@+id/LinearLayout")
150             .set(ANDROID_URI, ATTR_LAYOUT_WIDTH, "42dip")
151             .set(ANDROID_URI, ATTR_LAYOUT_HEIGHT, "50sp");
152 
153         List<RuleAction> contextMenu = new ArrayList<RuleAction>();
154         rule.addContextMenuActions(contextMenu, node);
155         assertEquals(6, contextMenu.size());
156         assertEquals("Layout Width", contextMenu.get(2).getTitle());
157         RuleAction menuAction = contextMenu.get(2);
158         assertTrue(menuAction instanceof RuleAction.Choices);
159         RuleAction.Choices choices = (RuleAction.Choices) menuAction;
160         List<String> titles = choices.getTitles();
161         List<String> ids = choices.getIds();
162         assertEquals("Wrap Content", titles.get(0));
163         assertEquals("wrap_content", ids.get(0));
164         assertEquals("Match Parent", titles.get(1));
165         assertEquals("match_parent", ids.get(1));
166         assertEquals("42dip", titles.get(2));
167         assertEquals("42dip", ids.get(2));
168         assertEquals("42dip", choices.getCurrent());
169     }
170 
171     // Check that the context menu manipulates the orientation attribute
testOrientation()172     public void testOrientation() {
173         LinearLayoutRule rule = new LinearLayoutRule();
174         initialize(rule, "android.widget.LinearLayout");
175         TestNode node = TestNode.create("android.widget.LinearLayout").id("@+id/LinearLayout012");
176         node.putAttributeInfo(ANDROID_URI, "orientation",
177                 new TestAttributeInfo(ATTR_ORIENTATION, Format.ENUM_SET,
178                         "android.widget.LinearLayout",
179                         new String[] {"horizontal", "vertical"}, null, null));
180 
181         assertNull(node.getStringAttr(ANDROID_URI, ATTR_ORIENTATION));
182 
183         List<RuleAction> contextMenu = new ArrayList<RuleAction>();
184         rule.addContextMenuActions(contextMenu, node);
185         assertEquals(7, contextMenu.size());
186         RuleAction orientationAction = contextMenu.get(1);
187         assertEquals("Orientation", orientationAction.getTitle());
188 
189         assertTrue(orientationAction.getClass().getName(),
190                 orientationAction instanceof RuleAction.Choices);
191 
192         RuleAction.Choices choices = (RuleAction.Choices) orientationAction;
193         IMenuCallback callback = choices.getCallback();
194         callback.action(orientationAction, Collections.singletonList(node), VALUE_VERTICAL, true);
195 
196         String orientation = node.getStringAttr(ANDROID_URI,
197                 ATTR_ORIENTATION);
198         assertEquals(VALUE_VERTICAL, orientation);
199         callback.action(orientationAction, Collections.singletonList(node), VALUE_HORIZONTAL,
200                 true);
201         orientation = node.getStringAttr(ANDROID_URI, ATTR_ORIENTATION);
202         assertEquals(VALUE_HORIZONTAL, orientation);
203     }
204 
205     // Check that the context menu manipulates the orientation attribute
testProperties()206     public void testProperties() {
207         LinearLayoutRule rule = new LinearLayoutRule();
208         initialize(rule, "android.widget.LinearLayout");
209         TestNode node = TestNode.create("android.widget.LinearLayout").id("@+id/LinearLayout012");
210         node.putAttributeInfo(ANDROID_URI, "orientation",
211                 new TestAttributeInfo(ATTR_ORIENTATION, Format.ENUM_SET,
212                         "android.widget.LinearLayout",
213                         new String[] {"horizontal", "vertical"}, null, null));
214         node.setAttributeSources(Arrays.asList("android.widget.LinearLayout",
215                 "android.view.ViewGroup", "android.view.View"));
216         node.putAttributeInfo(ANDROID_URI, "gravity",
217                 new TestAttributeInfo("gravity", Format.INTEGER_SET,
218                         "android.widget.LinearLayout", null, null, null));
219 
220 
221         assertNull(node.getStringAttr(ANDROID_URI, ATTR_ORIENTATION));
222 
223         List<RuleAction> contextMenu = new ArrayList<RuleAction>();
224         rule.addContextMenuActions(contextMenu, node);
225         assertEquals(8, contextMenu.size());
226 
227         assertEquals("Orientation", contextMenu.get(1).getTitle());
228         assertEquals("Edit Gravity...", contextMenu.get(2).getTitle());
229 
230         assertEquals("Other Properties", contextMenu.get(7).getTitle());
231 
232         RuleAction propertiesMenu = contextMenu.get(7);
233         assertTrue(propertiesMenu.getClass().getName(),
234                 propertiesMenu instanceof NestedAction);
235         NestedAction nested = (NestedAction) propertiesMenu;
236         List<RuleAction> nestedActions = nested.getNestedActions(node);
237         assertEquals(9, nestedActions.size());
238         assertEquals("Recent", nestedActions.get(0).getTitle());
239         assertTrue(nestedActions.get(1) instanceof RuleAction.Separator);
240         assertEquals("Defined by LinearLayout", nestedActions.get(2).getTitle());
241         assertEquals("Inherited from ViewGroup", nestedActions.get(3).getTitle());
242         assertEquals("Inherited from View", nestedActions.get(4).getTitle());
243         assertTrue(nestedActions.get(5) instanceof RuleAction.Separator);
244         assertEquals("Layout Parameters", nestedActions.get(6).getTitle());
245         assertTrue(nestedActions.get(7) instanceof RuleAction.Separator);
246         assertEquals("All By Name", nestedActions.get(8).getTitle());
247 
248         BaseViewRule.editedProperty(ATTR_ORIENTATION);
249 
250         RuleAction recentAction = nestedActions.get(0);
251         assertTrue(recentAction instanceof NestedAction);
252         NestedAction recentChoices = (NestedAction) recentAction;
253         List<RuleAction> recentItems = recentChoices.getNestedActions(node);
254 
255         assertEquals(1, recentItems.size());
256         assertEquals("Orientation", recentItems.get(0).getTitle());
257 
258         BaseViewRule.editedProperty("gravity");
259         recentItems = recentChoices.getNestedActions(node);
260         assertEquals(2, recentItems.size());
261         assertEquals("Gravity...", recentItems.get(0).getTitle());
262         assertEquals("Orientation", recentItems.get(1).getTitle());
263 
264         BaseViewRule.editedProperty(ATTR_ORIENTATION);
265         recentItems = recentChoices.getNestedActions(node);
266         assertEquals(2, recentItems.size());
267         assertEquals("Orientation", recentItems.get(0).getTitle());
268         assertEquals("Gravity...", recentItems.get(1).getTitle());
269 
270         // Lots of other properties -- flushes out properties that apply to this view
271         for (int i = 0; i < 30; i++) {
272             BaseViewRule.editedProperty("dummy_" + i);
273         }
274         recentItems = recentChoices.getNestedActions(node);
275         assertEquals(0, recentItems.size());
276 
277         BaseViewRule.editedProperty("gravity");
278         recentItems = recentChoices.getNestedActions(node);
279         assertEquals(1, recentItems.size());
280         assertEquals("Gravity...", recentItems.get(0).getTitle());
281     }
282 
testDragInEmptyWithBounds()283     public void testDragInEmptyWithBounds() {
284         dragIntoEmpty(new Rect(0, 0, 100, 80));
285     }
286 
testDragInEmptyWithoutBounds()287     public void testDragInEmptyWithoutBounds() {
288         dragIntoEmpty(new Rect(0, 0, 0, 0));
289     }
290 
testDragInVerticalTop()291     public void testDragInVerticalTop() {
292         dragInto(true,
293                 // Bounds of the dragged item
294                 new Rect(0, 0, 105, 80),
295                 // Drag point
296                 new Point(30, -10),
297                 // Expected insert location
298                 0,
299                 // Not dragging one of the existing children
300                 -1,
301                 // Bounds rectangle
302                 "useStyle(DROP_RECIPIENT), drawRect(Rect[0,0,240,480])",
303 
304                 // Drop zones
305                 "useStyle(DROP_ZONE), drawLine(0,0,240,0), drawLine(0,90,240,90), "
306                         + "drawLine(0,190,240,190), drawLine(0,290,240,290), "
307                         + "drawLine(0,381,240,381)",
308 
309                 // Active nearest line
310                 "useStyle(DROP_ZONE_ACTIVE), useStyle(DROP_PREVIEW), drawLine(0,0,240,0)",
311 
312                 // Preview of the dropped rectangle
313                 "useStyle(DROP_PREVIEW), drawRect(0,-40,105,40)");
314 
315         // Without drag bounds it should be identical except no preview
316         // rectangle
317         dragInto(true,
318                 new Rect(0, 0, 0, 0), // Invalid
319                 new Point(30, -10), 0, -1,
320                 "useStyle(DROP_ZONE_ACTIVE), useStyle(DROP_PREVIEW), drawLine(0,0,240,0)");
321     }
322 
testDragInVerticalBottom()323     public void testDragInVerticalBottom() {
324         dragInto(true,
325                 // Bounds of the dragged item
326                 new Rect(0, 0, 105, 80),
327                 // Drag point
328                 new Point(30, 500),
329                 // Expected insert location
330                 4,
331                 // Not dragging one of the existing children
332                 -1,
333                 // Bounds rectangle
334                 "useStyle(DROP_RECIPIENT), drawRect(Rect[0,0,240,480])",
335 
336                 // Drop zones
337                 "useStyle(DROP_ZONE), drawLine(0,0,240,0), drawLine(0,90,240,90), "
338                         + "drawLine(0,190,240,190), drawLine(0,290,240,290), drawLine(0,381,240,381), ",
339 
340                 // Active nearest line
341                 "useStyle(DROP_ZONE_ACTIVE), useStyle(DROP_PREVIEW), drawLine(0,381,240,381)",
342 
343                 // Preview of the dropped rectangle
344                 "useStyle(DROP_PREVIEW), drawRect(0,381,105,461)");
345 
346         // Check without bounds too
347         dragInto(true, new Rect(0, 0, 105, 80), new Point(30, 500), 4, -1,
348                 "useStyle(DROP_PREVIEW), drawRect(0,381,105,461)");
349     }
350 
testDragInVerticalMiddle()351     public void testDragInVerticalMiddle() {
352         dragInto(true,
353                 // Bounds of the dragged item
354                 new Rect(0, 0, 105, 80),
355                 // Drag point
356                 new Point(0, 170),
357                 // Expected insert location
358                 2,
359                 // Not dragging one of the existing children
360                 -1,
361                 // Bounds rectangle
362                 "useStyle(DROP_RECIPIENT), drawRect(Rect[0,0,240,480])",
363 
364                 // Drop zones
365                 "useStyle(DROP_ZONE), drawLine(0,0,240,0), drawLine(0,90,240,90), "
366                         + "drawLine(0,190,240,190), drawLine(0,290,240,290)",
367 
368                 // Active nearest line
369                 "useStyle(DROP_ZONE_ACTIVE), useStyle(DROP_PREVIEW), drawLine(0,190,240,190)",
370 
371                 // Preview of the dropped rectangle
372                 "useStyle(DROP_PREVIEW), drawRect(0,150,105,230)");
373 
374         // Check without bounds too
375         dragInto(true, new Rect(0, 0, 105, 80), new Point(0, 170), 2, -1,
376                 "useStyle(DROP_PREVIEW), drawRect(0,150,105,230)");
377     }
378 
testDragInVerticalMiddleSelfPos()379     public void testDragInVerticalMiddleSelfPos() {
380         // Drag the 2nd button, down to the position between 3rd and 4th
381         dragInto(true,
382                 // Bounds of the dragged item
383                 new Rect(0, 100, 100, 80),
384                 // Drag point
385                 new Point(0, 250),
386                 // Expected insert location
387                 2,
388                 // Dragging 1st item
389                 1,
390                 // Bounds rectangle
391 
392                 "useStyle(DROP_RECIPIENT), drawRect(Rect[0,0,240,480])",
393 
394                 // Drop zones - these are different because we exclude drop
395                 // zones around the
396                 // dragged item itself (it doesn't make sense to insert directly
397                 // before or after
398                 // myself
399                 "useStyle(DROP_ZONE), drawLine(0,0,240,0), drawLine(0,290,240,290), "
400                         + "drawLine(0,381,240,381)",
401 
402                 // Preview line along insert axis
403                 "useStyle(DROP_ZONE_ACTIVE), useStyle(DROP_PREVIEW), drawLine(0,290,240,290)",
404 
405                 // Preview of dropped rectangle
406                 "useStyle(DROP_PREVIEW), drawRect(0,250,100,330)");
407 
408         // Test dropping on self (no position change):
409         dragInto(true,
410                 // Bounds of the dragged item
411                 new Rect(0, 100, 100, 80),
412                 // Drag point
413                 new Point(0, 210),
414                 // Expected insert location
415                 1,
416                 // Dragging from same pos
417                 1,
418                 // Bounds rectangle
419                 "useStyle(DROP_RECIPIENT), drawRect(Rect[0,0,240,480])",
420 
421                 // Drop zones - these are different because we exclude drop
422                 // zones around the
423                 // dragged item itself (it doesn't make sense to insert directly
424                 // before or after
425                 // myself
426                 "useStyle(DROP_ZONE), drawLine(0,0,240,0), drawLine(0,290,240,290), "
427                         + "drawLine(0,381,240,381)",
428 
429                 // No active nearest line when you're over the self pos!
430 
431                 // Preview of the dropped rectangle
432                 "useStyle(DROP_ZONE_ACTIVE), useStyle(DROP_PREVIEW), drawRect(0,100,100,180)");
433     }
434 
testDragToLastPosition()435     public void testDragToLastPosition() {
436         // Drag a button to the last position -- and confirm that the preview rectangle
437         // is now shown midway between the second to last and last positions, but fully
438         // below the drop zone line:
439         dragInto(true,
440                 // Bounds of the dragged item
441                 new Rect(0, 100, 100, 80),
442                 // Drag point
443                 new Point(0, 400),
444                 // Expected insert location
445                 3,
446                 // Dragging 1st item
447                 1,
448 
449                 // Bounds rectangle
450                 "useStyle(DROP_RECIPIENT), drawRect(Rect[0,0,240,480])",
451 
452                 // Drop Zones
453                 "useStyle(DROP_ZONE), drawLine(0,0,240,0), drawLine(0,290,240,290), " +
454                 "drawLine(0,381,240,381), ",
455 
456                 // Active Drop Zone
457                 "useStyle(DROP_ZONE_ACTIVE), useStyle(DROP_PREVIEW), drawLine(0,381,240,381)",
458 
459                 // Drop Preview
460                 "useStyle(DROP_PREVIEW), drawRect(0,381,100,461)");
461     }
462 
463     // Left to test:
464     // Check inserting at last pos with multiple children
465     // Check inserting with no bounds rectangle for dragged element
466 }
467