/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Eclipse Public License, Version 1.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.eclipse.org/org/documents/epl-v10.php
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.ide.common.layout;
import static com.android.ide.common.layout.LayoutConstants.ANDROID_URI;
import static com.android.ide.common.layout.LayoutConstants.ATTR_ID;
import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_HEIGHT;
import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_WIDTH;
import static com.android.ide.common.layout.LayoutConstants.ATTR_ORIENTATION;
import static com.android.ide.common.layout.LayoutConstants.VALUE_HORIZONTAL;
import static com.android.ide.common.layout.LayoutConstants.VALUE_VERTICAL;
import com.android.ide.common.api.DropFeedback;
import com.android.ide.common.api.IDragElement;
import com.android.ide.common.api.IMenuCallback;
import com.android.ide.common.api.INode;
import com.android.ide.common.api.IViewRule;
import com.android.ide.common.api.MenuAction;
import com.android.ide.common.api.Point;
import com.android.ide.common.api.Rect;
import com.android.ide.common.api.MenuAction.Choices;
import java.util.List;
import java.util.Map;
/** Test the {@link LinearLayoutRule} */
public class LinearLayoutRuleTest extends LayoutTestBase {
// Utility for other tests
protected void dragIntoEmpty(Rect dragBounds) {
boolean haveBounds = dragBounds.isValid();
IViewRule rule = new LinearLayoutRule();
INode targetNode = TestNode.create("android.widget.LinearLayout").id(
"@+id/LinearLayout01").bounds(new Rect(0, 0, 240, 480));
Point dropPoint = new Point(10, 5);
IDragElement[] elements = TestDragElement.create(TestDragElement.create(
"android.widget.Button", dragBounds).id("@+id/Button01"));
// Enter target
DropFeedback feedback = rule.onDropEnter(targetNode, elements);
assertNotNull(feedback);
assertFalse(feedback.invalidTarget);
assertNotNull(feedback.painter);
feedback = rule.onDropMove(targetNode, elements, feedback, dropPoint);
assertNotNull(feedback);
assertFalse(feedback.invalidTarget);
// Paint feedback and make sure it's what we expect
TestGraphics graphics = new TestGraphics();
assertNotNull(feedback.painter);
feedback.painter.paint(graphics, targetNode, feedback);
assertEquals(
// Expect to see a recipient rectangle around the bounds of the
// LinearLayout,
// as well as a single vertical line as a drop preview located
// along the left
// edge (for this horizontal linear layout) showing insert
// position at index 0,
// and finally a rectangle for the bounds of the inserted button
// centered over
// the middle
"[useStyle(DROP_RECIPIENT), "
+
// Bounds rectangle
"drawRect(Rect[0,0,240,480]), "
+ "useStyle(DROP_ZONE), drawLine(1,0,1,480), "
+ "useStyle(DROP_ZONE_ACTIVE), " + "useStyle(DROP_PREVIEW), " +
// Insert position line
"drawLine(1,0,1,480)" + (haveBounds ?
// Outline of dragged node centered over position line
", useStyle(DROP_PREVIEW), " + "drawRect(Rect[1,0,100,80])"
// Nothing when we don't have bounds
: "") + "]", graphics.getDrawn().toString());
// Attempt a drop
assertEquals(0, targetNode.getChildren().length);
rule.onDropped(targetNode, elements, feedback, dropPoint);
assertEquals(1, targetNode.getChildren().length);
assertEquals("@+id/Button01", targetNode.getChildren()[0].getStringAttr(
ANDROID_URI, ATTR_ID));
}
// Utility for other tests
protected INode dragInto(boolean vertical, Rect dragBounds, Point dragPoint,
int insertIndex, int currentIndex,
String... graphicsFragments) {
INode linearLayout = TestNode.create("android.widget.LinearLayout").id(
"@+id/LinearLayout01").bounds(new Rect(0, 0, 240, 480)).set(ANDROID_URI,
ATTR_ORIENTATION,
vertical ? VALUE_VERTICAL : VALUE_HORIZONTAL)
.add(
TestNode.create("android.widget.Button").id("@+id/Button01").bounds(
new Rect(0, 0, 100, 80)),
TestNode.create("android.widget.Button").id("@+id/Button02").bounds(
new Rect(0, 100, 100, 80)),
TestNode.create("android.widget.Button").id("@+id/Button03").bounds(
new Rect(0, 200, 100, 80)),
TestNode.create("android.widget.Button").id("@+id/Button04").bounds(
new Rect(0, 300, 100, 80)));
return super.dragInto(new LinearLayoutRule(), linearLayout, dragBounds, dragPoint, null,
insertIndex, currentIndex, graphicsFragments);
}
// Check that the context menu registers the expected menu items
public void testContextMenu() {
LinearLayoutRule rule = new LinearLayoutRule();
initialize(rule, "android.widget.LinearLayout");
INode node = TestNode.create("android.widget.Button").id("@+id/Button012");
List<MenuAction> contextMenu = rule.getContextMenu(node);
assertEquals(6, contextMenu.size());
assertNull(contextMenu.get(0)); // Edit Text... not available
assertEquals("Edit ID...", contextMenu.get(1).getTitle());
assertEquals("Layout Width", contextMenu.get(2).getTitle());
assertEquals("Layout Height", contextMenu.get(3).getTitle());
assertEquals("Properties", contextMenu.get(4).getTitle());
assertEquals("Orientation", contextMenu.get(5).getTitle());
MenuAction propertiesMenu = contextMenu.get(4);
assertTrue(propertiesMenu.getClass().getName(), propertiesMenu instanceof MenuAction.Group);
// TODO: Test Properties-list
}
public void testContextMenuCustom() {
LinearLayoutRule rule = new LinearLayoutRule();
initialize(rule, "android.widget.LinearLayout");
INode node = TestNode.create("android.widget.Button").id("@+id/Button012")
.set(ANDROID_URI, ATTR_LAYOUT_WIDTH, "42dip")
.set(ANDROID_URI, ATTR_LAYOUT_HEIGHT, "50sp");
List<MenuAction> contextMenu = rule.getContextMenu(node);
assertEquals(6, contextMenu.size());
assertEquals("Layout Width", contextMenu.get(2).getTitle());
MenuAction menuAction = contextMenu.get(2);
assertTrue(menuAction instanceof MenuAction.Choices);
MenuAction.Choices choices = (Choices) menuAction;
Map<String, String> items = choices.getChoices();
assertTrue(items.containsKey("42dip"));
assertTrue(items.containsValue("42dip"));
assertEquals("Other...", items.get("zcustom"));
assertEquals("Match Parent", items.get("match_parent"));
assertEquals("42dip", choices.getCurrent());
}
// Check that the context menu manipulates the orientation attribute
public void testOrientation() {
LinearLayoutRule rule = new LinearLayoutRule();
initialize(rule, "android.widget.LinearLayout");
INode node = TestNode.create("android.widget.Button").id("@+id/Button012");
assertNull(node.getStringAttr(ANDROID_URI, ATTR_ORIENTATION));
List<MenuAction> contextMenu = rule.getContextMenu(node);
assertEquals(6, contextMenu.size());
MenuAction orientationAction = contextMenu.get(5);
assertTrue(orientationAction.getClass().getName(),
orientationAction instanceof MenuAction.Choices);
MenuAction.Choices choices = (Choices) orientationAction;
IMenuCallback callback = choices.getCallback();
callback.action(orientationAction, VALUE_VERTICAL, true);
String orientation = node.getStringAttr(ANDROID_URI,
ATTR_ORIENTATION);
assertEquals(VALUE_VERTICAL, orientation);
callback.action(orientationAction, VALUE_HORIZONTAL, true);
orientation = node.getStringAttr(ANDROID_URI, ATTR_ORIENTATION);
assertEquals(VALUE_HORIZONTAL, orientation);
}
public void testDragInEmptyWithBounds() {
dragIntoEmpty(new Rect(0, 0, 100, 80));
}
public void testDragInEmptyWithoutBounds() {
dragIntoEmpty(new Rect(0, 0, 0, 0));
}
public void testDragInVerticalTop() {
dragInto(true,
// Bounds of the dragged item
new Rect(0, 0, 105, 80),
// Drag point
new Point(30, -10),
// Expected insert location
0,
// Not dragging one of the existing children
-1,
// Bounds rectangle
"useStyle(DROP_RECIPIENT), drawRect(Rect[0,0,240,480])",
// Drop zones
"useStyle(DROP_ZONE), drawLine(0,0,240,0), drawLine(0,90,240,90), "
+ "drawLine(0,190,240,190), drawLine(0,290,240,290), "
+ "drawLine(0,381,240,381)",
// Active nearest line
"useStyle(DROP_ZONE_ACTIVE), useStyle(DROP_PREVIEW), drawLine(0,0,240,0)",
// Preview of the dropped rectangle
"useStyle(DROP_PREVIEW), drawRect(Rect[0,-40,105,80])");
// Without drag bounds it should be identical except no preview
// rectangle
dragInto(true,
new Rect(0, 0, 0, 0), // Invalid
new Point(30, -10), 0, -1,
"useStyle(DROP_ZONE_ACTIVE), useStyle(DROP_PREVIEW), drawLine(0,0,240,0)");
}
public void testDragInVerticalBottom() {
dragInto(true,
// Bounds of the dragged item
new Rect(0, 0, 105, 80),
// Drag point
new Point(30, 500),
// Expected insert location
4,
// Not dragging one of the existing children
-1,
// Bounds rectangle
"useStyle(DROP_RECIPIENT), drawRect(Rect[0,0,240,480])",
// Drop zones
"useStyle(DROP_ZONE), drawLine(0,0,240,0), drawLine(0,90,240,90), "
+ "drawLine(0,190,240,190), drawLine(0,290,240,290), drawLine(0,381,240,381), ",
// Active nearest line
"useStyle(DROP_ZONE_ACTIVE), useStyle(DROP_PREVIEW), drawLine(0,381,240,381)",
// Preview of the dropped rectangle
"useStyle(DROP_PREVIEW), drawRect(Rect[0,381,105,80])");
// Check without bounds too
dragInto(true, new Rect(0, 0, 105, 80), new Point(30, 500), 4, -1,
"useStyle(DROP_PREVIEW), drawRect(Rect[0,381,105,80])");
}
public void testDragInVerticalMiddle() {
dragInto(true,
// Bounds of the dragged item
new Rect(0, 0, 105, 80),
// Drag point
new Point(0, 170),
// Expected insert location
2,
// Not dragging one of the existing children
-1,
// Bounds rectangle
"useStyle(DROP_RECIPIENT), drawRect(Rect[0,0,240,480])",
// Drop zones
"useStyle(DROP_ZONE), drawLine(0,0,240,0), drawLine(0,90,240,90), "
+ "drawLine(0,190,240,190), drawLine(0,290,240,290)",
// Active nearest line
"useStyle(DROP_ZONE_ACTIVE), useStyle(DROP_PREVIEW), drawLine(0,190,240,190)",
// Preview of the dropped rectangle
"useStyle(DROP_PREVIEW), drawRect(Rect[0,150,105,80])");
// Check without bounds too
dragInto(true, new Rect(0, 0, 105, 80), new Point(0, 170), 2, -1,
"useStyle(DROP_PREVIEW), drawRect(Rect[0,150,105,80])");
}
public void testDragInVerticalMiddleSelfPos() {
// Drag the 2nd button, down to the position between 3rd and 4th
dragInto(true,
// Bounds of the dragged item
new Rect(0, 100, 100, 80),
// Drag point
new Point(0, 250),
// Expected insert location
2,
// Dragging 1st item
1,
// Bounds rectangle
"useStyle(DROP_RECIPIENT), drawRect(Rect[0,0,240,480])",
// Drop zones - these are different because we exclude drop
// zones around the
// dragged item itself (it doesn't make sense to insert directly
// before or after
// myself
"useStyle(DROP_ZONE), drawLine(0,0,240,0), drawLine(0,290,240,290), "
+ "drawLine(0,381,240,381)",
// Preview line along insert axis
"useStyle(DROP_ZONE_ACTIVE), useStyle(DROP_PREVIEW), drawLine(0,290,240,290)",
// Preview of dropped rectangle
"useStyle(DROP_PREVIEW), drawRect(Rect[0,250,100,80])");
// Test dropping on self (no position change):
dragInto(true,
// Bounds of the dragged item
new Rect(0, 100, 100, 80),
// Drag point
new Point(0, 210),
// Expected insert location
1,
// Dragging from same pos
1,
// Bounds rectangle
"useStyle(DROP_RECIPIENT), drawRect(Rect[0,0,240,480])",
// Drop zones - these are different because we exclude drop
// zones around the
// dragged item itself (it doesn't make sense to insert directly
// before or after
// myself
"useStyle(DROP_ZONE), drawLine(0,0,240,0), drawLine(0,290,240,290), "
+ "drawLine(0,381,240,381)",
// No active nearest line when you're over the self pos!
// Preview of the dropped rectangle
"useStyle(DROP_ZONE_ACTIVE), useStyle(DROP_PREVIEW), drawRect(Rect[0,100,100,80])");
}
public void testDragToLastPosition() {
// Drag a button to the last position -- and confirm that the preview rectangle
// is now shown midway between the second to last and last positions, but fully
// below the drop zone line:
dragInto(true,
// Bounds of the dragged item
new Rect(0, 100, 100, 80),
// Drag point
new Point(0, 400),
// Expected insert location
3,
// Dragging 1st item
1,
// Bounds rectangle
"useStyle(DROP_RECIPIENT), drawRect(Rect[0,0,240,480])",
// Drop Zones
"useStyle(DROP_ZONE), drawLine(0,0,240,0), drawLine(0,290,240,290), " +
"drawLine(0,381,240,381), ",
// Active Drop Zone
"useStyle(DROP_ZONE_ACTIVE), useStyle(DROP_PREVIEW), drawLine(0,381,240,381)",
// Drop Preview
"seStyle(DROP_PREVIEW), drawRect(Rect[0,381,100,80])");
}
// Left to test:
// Check inserting at last pos with multiple children
// Check inserting with no bounds rectangle for dragged element
}