aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml19
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LayoutConstants.java1
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java19
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditor.java2
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfo.java58
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DynamicContextMenu.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java7
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java22
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ViewHierarchy.java10
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleAction.java47
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleContribution.java40
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleRefactoring.java538
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleWizard.java431
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/RefactoringAssistant.java39
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/VisualRefactoring.java49
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/resources/descriptors/ResourcesDescriptors.java1
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileWizard.java8
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/build/AaptQuickFixTest.java3
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/AdtProjectTest.java27
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleRefactoringTest.java229
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/RefactoringAssistantTest.java4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/RefactoringTest.java24
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract1b.diff3
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract1c.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract1d.diff6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract2.diff6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract3.diff15
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract4.diff7
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract5.diff8
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract6.diff6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract8.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1.info3
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1.xml11
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle2-expected-extract7.diff13
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle2.info3
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle2.xml11
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigationstyles-expected-extract2.diff6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles-expected-extract1.diff9
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles2-expected-extract1b.diff9
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles3-expected-extract1c.diff9
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles3-expected-extract8.diff9
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles4-expected-extract1d.diff9
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles4-expected-extract3.diff9
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles5-expected-extract4.diff8
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles6-expected-extract5.diff8
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles7-expected-extract6.diff13
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles8-expected-extract7.diff9
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-assistant1.txt1
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-assistant2.txt1
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-assistant3.txt2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfoTest.java127
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManagerTest.java4
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeFactoryTest.java6
-rw-r--r--ide_common/src/com/android/ide/common/resources/ResourceResolver.java2
-rw-r--r--layoutlib_api/src/com/android/ide/common/rendering/api/Capability.java2
-rw-r--r--monkeyrunner/src/com/android/monkeyrunner/adb/AdbMonkeyDevice.java2
56 files changed, 1853 insertions, 72 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml b/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
index 9c3f208..82f4e05 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
@@ -686,6 +686,15 @@
tooltip="Extracts Views as Included Layout">
</action>
<action
+ class="com.android.ide.eclipse.adt.internal.editors.layout.refactoring.ExtractStyleAction"
+ definitionId="com.android.ide.eclipse.adt.refactoring.extract.style"
+ id="com.android.ide.eclipse.adt.actions.ExtractStyle"
+ label="Extract Style..."
+ menubarPath="org.eclipse.jdt.ui.refactoring.menu/com.android.ide.eclipse.adt.refactoring.menu/android"
+ style="push"
+ tooltip="Extracts Styles">
+ </action>
+ <action
class="com.android.ide.eclipse.adt.internal.editors.layout.refactoring.WrapInAction"
definitionId="com.android.ide.eclipse.adt.refactoring.wrapin"
id="com.android.ide.eclipse.adt.actions.WrapIn"
@@ -829,6 +838,12 @@
</command>
<command
categoryId="com.android.ide.eclipse.adt.refactoring.category"
+ description="Extract Styles"
+ id="com.android.ide.eclipse.adt.refactoring.extract.style"
+ name="Extract Styles">
+ </command>
+ <command
+ categoryId="com.android.ide.eclipse.adt.refactoring.category"
description="Wraps Views in a New Container"
id="com.android.ide.eclipse.adt.refactoring.wrapin"
name="Wrap in Container">
@@ -857,6 +872,10 @@
id="com.android.ide.eclipse.adt.refactoring.extract.include">
</contribution>
<contribution
+ class="com.android.ide.eclipse.adt.internal.editors.layout.refactoring.ExtractStyleContribution"
+ id="com.android.ide.eclipse.adt.refactoring.extract.style">
+ </contribution>
+ <contribution
class="com.android.ide.eclipse.adt.internal.editors.layout.refactoring.WrapInContribution"
id="com.android.ide.eclipse.adt.refactoring.wrapin">
</contribution>
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LayoutConstants.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LayoutConstants.java
index 092b0a5..73cb229 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LayoutConstants.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LayoutConstants.java
@@ -51,6 +51,7 @@ public class LayoutConstants {
public static final String GESTURE_OVERLAY_VIEW = "GestureOverlayView";//$NON-NLS-1$
public static final String ATTR_TEXT = "text"; //$NON-NLS-1$
+ public static final String ATTR_HINT = "hint"; //$NON-NLS-1$
public static final String ATTR_ID = "id"; //$NON-NLS-1$
public static final String ATTR_STYLE = "style"; //$NON-NLS-1$
public static final String ATTR_HANDLE = "handle"; //$NON-NLS-1$
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
index 6018d17..43fd823 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
@@ -1810,7 +1810,8 @@ public class AdtPlugin extends AbstractUIPlugin implements ILogger {
}
/**
- * Opens the given file and shows the given (optional) region
+ * Opens the given file and shows the given (optional) region in the editor (or
+ * if no region is specified, opens the editor tab.)
*
* @param file the file to be opened
* @param region an optional region which if set will be selected and shown to the
@@ -1818,6 +1819,20 @@ public class AdtPlugin extends AbstractUIPlugin implements ILogger {
* @throws PartInitException if something goes wrong
*/
public static void openFile(IFile file, IRegion region) throws PartInitException {
+ openFile(file, region, true);
+ }
+
+ /**
+ * Opens the given file and shows the given (optional) region
+ *
+ * @param file the file to be opened
+ * @param region an optional region which if set will be selected and shown to the
+ * user
+ * @param showEditorTab if true, front the editor tab after opening the file
+ * @throws PartInitException if something goes wrong
+ */
+ public static void openFile(IFile file, IRegion region, boolean showEditorTab)
+ throws PartInitException {
IWorkbench workbench = PlatformUI.getWorkbench();
if (workbench == null) {
return;
@@ -1835,7 +1850,7 @@ public class AdtPlugin extends AbstractUIPlugin implements ILogger {
AndroidXmlEditor editor = (AndroidXmlEditor) targetEditor;
if (region != null) {
editor.show(region.getOffset(), region.getLength());
- } else {
+ } else if (showEditorTab) {
editor.setActivePage(AndroidXmlEditor.TEXT_EDITOR_ID);
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditor.java
index b862ada..bccc7d0 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditor.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditor.java
@@ -16,8 +16,8 @@
package com.android.ide.eclipse.adt.internal.editors.layout;
-import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.AdtConstants;
+import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.DocumentDescriptor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfo.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfo.java
index 2bb8901..2a70dd0 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfo.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfo.java
@@ -526,19 +526,41 @@ public class CanvasViewInfo implements IPropertySource {
* This method will build up a set of {@link CanvasViewInfo} that corresponds to the
* actual <b>selectable</b> views (which are also shown in the Outline).
*
+ * @param layoutlib5 if true, the {@link ViewInfo} hierarchy was created by layoutlib
+ * version 5 or higher, which means this algorithm can make certain assumptions
+ * (for example that {@code <merge>} siblings will provide {@link MergeCookie}
+ * references, so we don't have to search for them.)
* @param root the root {@link ViewInfo} to build from
* @return a {@link CanvasViewInfo} hierarchy
*/
- public static Pair<CanvasViewInfo,List<Rectangle>> create(ViewInfo root) {
- return new Builder().create(root);
+ public static Pair<CanvasViewInfo,List<Rectangle>> create(ViewInfo root, boolean layoutlib5) {
+ return new Builder(layoutlib5).create(root);
}
/** Builder object which walks over a tree of {@link ViewInfo} objects and builds
* up a corresponding {@link CanvasViewInfo} hierarchy. */
private static class Builder {
- private Map<UiViewElementNode,List<CanvasViewInfo>> mMergeNodeMap;
+ public Builder(boolean layoutlib5) {
+ mLayoutLib5 = layoutlib5;
+ }
- public Pair<CanvasViewInfo,List<Rectangle>> create(ViewInfo root) {
+ /**
+ * The mapping from nodes that have a {@code <merge>} as a parent in the node
+ * model to their corresponding views
+ */
+ private Map<UiViewElementNode, List<CanvasViewInfo>> mMergeNodeMap;
+
+ /**
+ * Whether the ViewInfos are provided by a layout library that is version 5 or
+ * later, since that will allow us to take several shortcuts
+ */
+ private boolean mLayoutLib5;
+
+ /**
+ * Creates a hierarchy of {@link CanvasViewInfo} objects and merge bounding
+ * rectangles from the given {@link ViewInfo} hierarchy
+ */
+ private Pair<CanvasViewInfo,List<Rectangle>> create(ViewInfo root) {
Object cookie = root.getCookie();
if (cookie == null) {
// Special case: If the root-most view does not have a view cookie,
@@ -717,10 +739,24 @@ public class CanvasViewInfo implements IPropertySource {
parentX += viewInfo.getLeft();
parentY += viewInfo.getTop();
+ List<ViewInfo> children = viewInfo.getChildren();
+
+ if (mLayoutLib5) {
+ for (ViewInfo child : children) {
+ Object cookie = child.getCookie();
+ if (cookie instanceof UiViewElementNode || cookie instanceof MergeCookie) {
+ CanvasViewInfo childView = createSubtree(view, child,
+ parentX, parentY);
+ view.addChild(childView);
+ } // else: null cookies, adapter item references, etc: No child views.
+ }
+
+ return view;
+ }
+
// See if we have any missing keys at this level
int missingNodes = 0;
int mergeNodes = 0;
- List<ViewInfo> children = viewInfo.getChildren();
for (ViewInfo child : children) {
// Only use children which have a ViewKey of the correct type.
// We can't interact with those when they have a null key or
@@ -751,7 +787,9 @@ public class CanvasViewInfo implements IPropertySource {
// embedded_layout rendering, or we are including a view with a <merge>
// as the root element.
- String containerName = view.getUiViewNode().getDescriptor().getXmlLocalName();
+ UiViewElementNode uiViewNode = view.getUiViewNode();
+ String containerName = uiViewNode != null
+ ? uiViewNode.getDescriptor().getXmlLocalName() : ""; //$NON-NLS-1$
if (containerName.equals(LayoutDescriptors.VIEW_INCLUDE)) {
// This is expected -- we don't WANT to get node keys for the content
// of an include since it's in a different file and should be treated
@@ -764,9 +802,11 @@ public class CanvasViewInfo implements IPropertySource {
// that there are <merge> tags which are doing surprising things
// to the view hierarchy
LinkedList<UiViewElementNode> unused = new LinkedList<UiViewElementNode>();
- for (UiElementNode child : view.getUiViewNode().getUiChildren()) {
- if (child instanceof UiViewElementNode) {
- unused.addLast((UiViewElementNode) child);
+ if (uiViewNode != null) {
+ for (UiElementNode child : uiViewNode.getUiChildren()) {
+ if (child instanceof UiViewElementNode) {
+ unused.addLast((UiViewElementNode) child);
+ }
}
}
for (ViewInfo child : children) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DynamicContextMenu.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DynamicContextMenu.java
index a0db1fb..1857602 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DynamicContextMenu.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DynamicContextMenu.java
@@ -28,6 +28,7 @@ import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeProxy;
import com.android.ide.eclipse.adt.internal.editors.layout.refactoring.ChangeLayoutAction;
import com.android.ide.eclipse.adt.internal.editors.layout.refactoring.ChangeViewAction;
import com.android.ide.eclipse.adt.internal.editors.layout.refactoring.ExtractIncludeAction;
+import com.android.ide.eclipse.adt.internal.editors.layout.refactoring.ExtractStyleAction;
import com.android.ide.eclipse.adt.internal.editors.layout.refactoring.WrapInAction;
import org.eclipse.jface.action.Action;
@@ -219,6 +220,7 @@ import java.util.regex.Pattern;
// or on an included view, or on a non-contiguous selection
mMenuManager.insertBefore(endId, new Separator());
mMenuManager.insertBefore(endId, ExtractIncludeAction.create(mEditor));
+ mMenuManager.insertBefore(endId, ExtractStyleAction.create(mEditor));
mMenuManager.insertBefore(endId, WrapInAction.create(mEditor));
if (selection.size() == 1 && (selection.get(0).isLayout() ||
selection.get(0).getViewInfo().getName().equals(FQCN_GESTURE_OVERLAY_VIEW))) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java
index 47d4f22..0a65865 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java
@@ -1053,7 +1053,7 @@ public class GraphicalEditorPart extends EditorPart
new StaticRenderSession(
Result.Status.SUCCESS.createResult(),
null /*rootViewInfo*/, null /*image*/),
- null /*explodeNodes*/);
+ null /*explodeNodes*/, true /* layoutlib5 */);
return;
}
@@ -1339,7 +1339,8 @@ public class GraphicalEditorPart extends EditorPart
explodeNodes, null /*custom background*/, false /*no decorations*/, logger,
mIncludedWithin, renderingMode);
- canvas.setSession(session, explodeNodes);
+ boolean layoutlib5 = layoutLib.supports(Capability.EMBEDDED_LAYOUT);
+ canvas.setSession(session, explodeNodes, layoutlib5);
// update the UiElementNode with the layout info.
if (session != null && session.getResult().isSuccess() == false) {
@@ -1587,7 +1588,7 @@ public class GraphicalEditorPart extends EditorPart
return null;
}
- ResourceResolver createResolver() {
+ public ResourceResolver createResolver() {
String theme = mConfigComposite.getTheme();
boolean isProjectTheme = mConfigComposite.isProjectTheme();
Map<ResourceType, Map<String, ResourceValue>> configuredProjectRes =
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java
index 277c194..b54e3d1 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java
@@ -214,6 +214,11 @@ public class LayoutCanvas extends Canvas {
private final GestureManager mGestureManager = new GestureManager(this);
/**
+ * When set, performs a zoom-to-fit when the next rendering image arrives.
+ */
+ private boolean mZoomFitNextImage;
+
+ /**
* Native clipboard support.
*/
private ClipboardSupport mClipboardSupport;
@@ -244,6 +249,8 @@ public class LayoutCanvas extends Canvas {
} catch (NumberFormatException nfe) {
// Ignore - use zoom=100%
}
+ } else {
+ mZoomFitNextImage = true;
}
}
@@ -546,11 +553,12 @@ public class LayoutCanvas extends Canvas {
* {@link #showInvisibleViews(boolean)}) where individual invisible nodes
* are padded during certain interactions.
*/
- /* package */ void setSession(RenderSession session, Set<UiElementNode> explodedNodes) {
+ /* package */ void setSession(RenderSession session, Set<UiElementNode> explodedNodes,
+ boolean layoutlib5) {
// disable any hover
clearHover();
- mViewHierarchy.setSession(session, explodedNodes);
+ mViewHierarchy.setSession(session, explodedNodes, layoutlib5);
if (mViewHierarchy.isValid() && session != null) {
Image image = mImageOverlay.setImage(session.getImage(), session.isAlphaChannelImage());
@@ -559,6 +567,16 @@ public class LayoutCanvas extends Canvas {
if (image != null) {
mHScale.setSize(image.getImageData().width, getClientArea().width);
mVScale.setSize(image.getImageData().height, getClientArea().height);
+ if (mZoomFitNextImage) {
+ mZoomFitNextImage = false;
+ // Must be run asynchronously because getClientArea() returns 0 bounds
+ // when the editor is being initialized
+ getDisplay().asyncExec(new Runnable() {
+ public void run() {
+ setFitScale(true);
+ }
+ });
+ }
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ViewHierarchy.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ViewHierarchy.java
index 8624cd3..10625bb 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ViewHierarchy.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ViewHierarchy.java
@@ -136,7 +136,8 @@ public class ViewHierarchy {
* {@link LayoutCanvas#showInvisibleViews}) where individual invisible
* nodes are padded during certain interactions.
*/
- /* package */ void setSession(RenderSession session, Set<UiElementNode> explodedNodes) {
+ /* package */ void setSession(RenderSession session, Set<UiElementNode> explodedNodes,
+ boolean layoutlib5) {
// replace the previous scene, so the previous scene must be disposed.
if (mSession != null) {
mSession.dispose();
@@ -157,7 +158,7 @@ public class ViewHierarchy {
// via drag & drop, etc.
if (hasMergeRoot()) {
ViewInfo mergeRoot = createMergeInfo(session);
- infos = CanvasViewInfo.create(mergeRoot);
+ infos = CanvasViewInfo.create(mergeRoot, layoutlib5);
} else {
infos = null;
}
@@ -165,11 +166,12 @@ public class ViewHierarchy {
if (rootList.size() > 1 && hasMergeRoot()) {
ViewInfo mergeRoot = createMergeInfo(session);
mergeRoot.setChildren(rootList);
- infos = CanvasViewInfo.create(mergeRoot);
+ infos = CanvasViewInfo.create(mergeRoot, layoutlib5);
} else {
ViewInfo root = rootList.get(0);
+
if (root != null) {
- infos = CanvasViewInfo.create(root);
+ infos = CanvasViewInfo.create(root, layoutlib5);
if (DUMP_INFO) {
dump(root, 0);
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleAction.java
new file mode 100644
index 0000000..4eef6b8
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleAction.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2011 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.eclipse.adt.internal.editors.layout.refactoring;
+
+import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.ltk.ui.refactoring.RefactoringWizard;
+import org.eclipse.ltk.ui.refactoring.RefactoringWizardOpenOperation;
+
+/**
+ * Action executed when the "Extract Style" menu item is invoked.
+ */
+public class ExtractStyleAction extends VisualRefactoringAction {
+ @Override
+ public void run(IAction action) {
+ if ((mTextSelection != null || mTreeSelection != null) && mFile != null) {
+ ExtractStyleRefactoring ref = new ExtractStyleRefactoring(mFile, mEditor,
+ mTextSelection, mTreeSelection);
+ RefactoringWizard wizard = new ExtractStyleWizard(ref, mEditor);
+ RefactoringWizardOpenOperation op = new RefactoringWizardOpenOperation(wizard);
+ try {
+ op.run(mWindow.getShell(), wizard.getDefaultPageTitle());
+ } catch (InterruptedException e) {
+ // Interrupted. Pass.
+ }
+ }
+ }
+
+ public static IAction create(LayoutEditor editor) {
+ return create("Extract Style...", editor, ExtractStyleAction.class);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleContribution.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleContribution.java
new file mode 100644
index 0000000..95fbdbc
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleContribution.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2011 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.eclipse.adt.internal.editors.layout.refactoring;
+
+import org.eclipse.ltk.core.refactoring.RefactoringContribution;
+import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
+
+import java.util.Map;
+
+public class ExtractStyleContribution extends RefactoringContribution {
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public RefactoringDescriptor createDescriptor(String id, String project, String description,
+ String comment, Map arguments, int flags) throws IllegalArgumentException {
+ return new ExtractStyleRefactoring.Descriptor(project, description, comment, arguments);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Map retrieveArgumentMap(RefactoringDescriptor descriptor) {
+ if (descriptor instanceof ExtractStyleRefactoring.Descriptor) {
+ return ((ExtractStyleRefactoring.Descriptor) descriptor).getArguments();
+ }
+ return super.retrieveArgumentMap(descriptor);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleRefactoring.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleRefactoring.java
new file mode 100644
index 0000000..3e18fca
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleRefactoring.java
@@ -0,0 +1,538 @@
+/*
+ * Copyright (C) 2011 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.eclipse.adt.internal.editors.layout.refactoring;
+
+import static com.android.AndroidConstants.FD_RES_VALUES;
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_NS_NAME;
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_NS_NAME_PREFIX;
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_URI;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_HINT;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_ID;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_PREFIX;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_SRC;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_STYLE;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_TEXT;
+import static com.android.ide.eclipse.adt.AdtConstants.EXT_XML;
+import static com.android.ide.eclipse.adt.AdtConstants.WS_SEP;
+import static com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor.XMLNS_COLON;
+import static com.android.ide.eclipse.adt.internal.editors.resources.descriptors.ResourcesDescriptors.ITEM_TAG;
+import static com.android.ide.eclipse.adt.internal.editors.resources.descriptors.ResourcesDescriptors.NAME_ATTR;
+import static com.android.ide.eclipse.adt.internal.editors.resources.descriptors.ResourcesDescriptors.PARENT_ATTR;
+import static com.android.ide.eclipse.adt.internal.editors.resources.descriptors.ResourcesDescriptors.ROOT_ELEMENT;
+import static com.android.sdklib.SdkConstants.FD_RESOURCES;
+
+import com.android.annotations.VisibleForTesting;
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.common.resources.ResourceResolver;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils;
+import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
+import com.android.ide.eclipse.adt.internal.wizards.newxmlfile.NewXmlFileWizard;
+import com.android.util.Pair;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jface.text.ITextSelection;
+import org.eclipse.jface.viewers.ITreeSelection;
+import org.eclipse.ltk.core.refactoring.Change;
+import org.eclipse.ltk.core.refactoring.Refactoring;
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+import org.eclipse.ltk.core.refactoring.TextFileChange;
+import org.eclipse.text.edits.InsertEdit;
+import org.eclipse.text.edits.MultiTextEdit;
+import org.eclipse.wst.sse.core.StructuredModelManager;
+import org.eclipse.wst.sse.core.internal.provisional.IModelManager;
+import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
+import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+/**
+ * Extracts the selection and writes it out as a separate layout file, then adds an
+ * include to that new layout file. Interactively asks the user for a new name for the
+ * layout.
+ * <p>
+ * Remaining work to do / Possible enhancements:
+ * <ul>
+ * <li>Optionally look in other files in the project and attempt to set style attributes
+ * in other cases where the style attributes match?
+ * <li>If the elements we are extracting from already contain a style attribute, set that
+ * style as the parent style of the current style?
+ * <li>Add a parent-style picker to the wizard (initialized with the above if applicable)
+ * <li>Pick up indentation settings from the XML module
+ * <li>Integrate with themes somehow -- make an option to have the extracted style go into
+ * the theme instead
+ * </ul>
+ */
+@SuppressWarnings("restriction") // XML model
+public class ExtractStyleRefactoring extends VisualRefactoring {
+ private static final String KEY_NAME = "name"; //$NON-NLS-1$
+ private static final String KEY_REMOVE_EXTRACTED = "removeextracted"; //$NON-NLS-1$
+ private static final String KEY_REMOVE_ALL = "removeall"; //$NON-NLS-1$
+ private static final String KEY_APPLY_STYLE = "applystyle"; //$NON-NLS-1$
+ private static final String KEY_PARENT = "parent"; //$NON-NLS-1$
+ private String mStyleName;
+ /** The name of the file in res/values/ that the style will be added to. Normally
+ * res/values/styles.xml - but unit tests pick other names */
+ private String mStyleFileName = "styles.xml";
+ /** Set a style reference on the extracted elements? */
+ private boolean mApplyStyle;
+ /** Remove the attributes that were extracted? */
+ private boolean mRemoveExtracted;
+ /** List of attributes chosen by the user to be extracted */
+ private List<Attr> mChosenAttributes = new ArrayList<Attr>();
+ /** Remove all attributes that match the extracted attributes names, regardless of value */
+ private boolean mRemoveAll;
+ /** The parent style to extend */
+ private String mParent;
+ /** The full list of available attributes in the refactoring */
+ private Map<String, List<Attr>> mAvailableAttributes;
+
+ /**
+ * This constructor is solely used by {@link Descriptor},
+ * to replay a previous refactoring.
+ * @param arguments argument map created by #createArgumentMap.
+ */
+ ExtractStyleRefactoring(Map<String, String> arguments) {
+ super(arguments);
+ mStyleName = arguments.get(KEY_NAME);
+ mRemoveExtracted = Boolean.parseBoolean(arguments.get(KEY_REMOVE_EXTRACTED));
+ mRemoveAll = Boolean.parseBoolean(arguments.get(KEY_REMOVE_ALL));
+ mApplyStyle = Boolean.parseBoolean(arguments.get(KEY_APPLY_STYLE));
+ mParent = arguments.get(KEY_PARENT);
+ }
+
+ public ExtractStyleRefactoring(IFile file, LayoutEditor editor, ITextSelection selection,
+ ITreeSelection treeSelection) {
+ super(file, editor, selection, treeSelection);
+ }
+
+ @VisibleForTesting
+ ExtractStyleRefactoring(List<Element> selectedElements, LayoutEditor editor) {
+ super(selectedElements, editor);
+ }
+
+ @Override
+ public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException,
+ OperationCanceledException {
+ RefactoringStatus status = new RefactoringStatus();
+
+ try {
+ pm.beginTask("Checking preconditions...", 6);
+
+ if (mSelectionStart == -1 || mSelectionEnd == -1) {
+ status.addFatalError("No selection to extract");
+ return status;
+ }
+
+ // This also ensures that we have a valid DOM model:
+ if (mElements.size() == 0) {
+ status.addFatalError("Nothing to extract");
+ return status;
+ }
+
+ pm.worked(1);
+ return status;
+
+ } finally {
+ pm.done();
+ }
+ }
+
+ @Override
+ protected VisualRefactoringDescriptor createDescriptor() {
+ String comment = getName();
+ return new Descriptor(
+ mProject.getName(), //project
+ comment, //description
+ comment, //comment
+ createArgumentMap());
+ }
+
+ @Override
+ protected Map<String, String> createArgumentMap() {
+ Map<String, String> args = super.createArgumentMap();
+ args.put(KEY_NAME, mStyleName);
+ args.put(KEY_REMOVE_EXTRACTED, Boolean.toString(mRemoveExtracted));
+ args.put(KEY_REMOVE_ALL, Boolean.toString(mRemoveAll));
+ args.put(KEY_APPLY_STYLE, Boolean.toString(mApplyStyle));
+ args.put(KEY_PARENT, mParent);
+
+ return args;
+ }
+
+ @Override
+ public String getName() {
+ return "Extract Style";
+ }
+
+ void setStyleName(String styleName) {
+ mStyleName = styleName;
+ }
+
+ void setStyleFileName(String styleFileName) {
+ mStyleFileName = styleFileName;
+ }
+
+ void setChosenAttributes(List<Attr> attributes) {
+ mChosenAttributes = attributes;
+ }
+
+ void setRemoveExtracted(boolean removeExtracted) {
+ mRemoveExtracted = removeExtracted;
+ }
+
+ void setApplyStyle(boolean applyStyle) {
+ mApplyStyle = applyStyle;
+ }
+
+ void setRemoveAll(boolean removeAll) {
+ mRemoveAll = removeAll;
+ }
+
+ void setParent(String parent) {
+ mParent = parent;
+ }
+
+ // ---- Actual implementation of Extract Style modification computation ----
+
+ /**
+ * Returns two items: a map from attribute name to a list of attribute nodes of that
+ * name, and a subset of these attributes that fall within the text selection
+ * (used to drive initial selection in the wizard)
+ */
+ Pair<Map<String, List<Attr>>, Set<Attr>> getAvailableAttributes() {
+ mAvailableAttributes = new TreeMap<String, List<Attr>>();
+ Set<Attr> withinSelection = new HashSet<Attr>();
+ for (Element element : getElements()) {
+ IndexedRegion elementRegion = getRegion(element);
+ boolean allIncluded =
+ (mOriginalSelectionStart <= elementRegion.getStartOffset() &&
+ mOriginalSelectionEnd >= elementRegion.getEndOffset());
+
+ NamedNodeMap attributeMap = element.getAttributes();
+ for (int i = 0, n = attributeMap.getLength(); i < n; i++) {
+ Attr attribute = (Attr) attributeMap.item(i);
+
+ String name = attribute.getLocalName();
+ if (name == null || name.equals(ATTR_ID) || name.startsWith(ATTR_STYLE)
+ || name.startsWith(ATTR_LAYOUT_PREFIX) || name.equals(ATTR_TEXT)
+ || name.equals(ATTR_HINT) || name.equals(ATTR_SRC)) {
+ // Don't offer to extract attributes that don't make sense in
+ // styles (like "id" or "style"), or attributes that the user
+ // probably does not want to define in styles (like layout
+ // attributes such as layout_width, or the label of a button etc).
+ // This makes the options offered listed in the wizard simpler.
+ // In special cases where the user *does* want to set one of these
+ // attributes, they can always do it manually so optimize for
+ // the common case here.
+ continue;
+ }
+
+ // Skip attributes that are in a namespace other than the Android one
+ String namespace = attribute.getNamespaceURI();
+ if (namespace != null && !ANDROID_URI.equals(namespace)) {
+ continue;
+ }
+
+ if (!allIncluded) {
+ IndexedRegion region = getRegion(attribute);
+ boolean attributeIncluded = mOriginalSelectionStart < region.getEndOffset() &&
+ mOriginalSelectionEnd >= region.getStartOffset();
+ if (attributeIncluded) {
+ withinSelection.add(attribute);
+ }
+ } else {
+ withinSelection.add(attribute);
+ }
+
+ List<Attr> list = mAvailableAttributes.get(name);
+ if (list == null) {
+ list = new ArrayList<Attr>();
+ mAvailableAttributes.put(name, list);
+ }
+ list.add(attribute);
+ }
+ }
+
+ return Pair.of(mAvailableAttributes, withinSelection);
+ }
+
+ IFile getStyleFile(IProject project) {
+ return project.getFile(new Path(FD_RESOURCES + WS_SEP + FD_RES_VALUES + WS_SEP
+ + mStyleFileName));
+ }
+
+ @Override
+ protected List<Change> computeChanges() {
+ List<Change> changes = new ArrayList<Change>();
+ if (mChosenAttributes.size() == 0) {
+ return changes;
+ }
+
+ IFile file = getStyleFile(mEditor.getProject());
+ boolean createFile = !file.exists();
+ int insertAtIndex;
+ String initialIndent = null;
+ if (!createFile) {
+ Pair<Integer, String> context = computeInsertContext(file);
+ insertAtIndex = context.getFirst();
+ initialIndent = context.getSecond();
+ } else {
+ insertAtIndex = 0;
+ }
+
+ TextFileChange addFile = new TextFileChange("Create new separate style declaration", file);
+ addFile.setTextType(EXT_XML);
+ changes.add(addFile);
+ String styleString = computeStyleDeclaration(createFile, initialIndent);
+ addFile.setEdit(new InsertEdit(insertAtIndex, styleString));
+
+ // Remove extracted attributes?
+ MultiTextEdit rootEdit = new MultiTextEdit();
+ if (mRemoveExtracted || mRemoveAll) {
+ for (Attr attribute : mChosenAttributes) {
+ List<Attr> list = mAvailableAttributes.get(attribute.getLocalName());
+ for (Attr attr : list) {
+ if (mRemoveAll || attr.getValue().equals(attribute.getValue())) {
+ removeAttribute(rootEdit, attr);
+ }
+ }
+ }
+ }
+
+ // Set the style attribute?
+ if (mApplyStyle) {
+ for (Element element : getElements()) {
+ String value = ResourceResolver.PREFIX_RESOURCE_REF +
+ ResourceResolver.REFERENCE_STYLE + mStyleName;
+ setAttribute(rootEdit, element, null, null, ATTR_STYLE, value);
+ }
+ }
+
+ if (rootEdit.hasChildren()) {
+ IFile sourceFile = mEditor.getInputFile();
+ TextFileChange change = new TextFileChange(sourceFile.getName(), sourceFile);
+ change.setEdit(rootEdit);
+ change.setTextType(EXT_XML);
+ changes.add(change);
+ }
+
+ return changes;
+ }
+
+ private String computeStyleDeclaration(boolean createFile, String initialIndent) {
+ StringBuilder sb = new StringBuilder();
+ if (createFile) {
+ sb.append(NewXmlFileWizard.XML_HEADER_LINE);
+ sb.append('<').append(ROOT_ELEMENT).append(' ');
+ sb.append(XMLNS_COLON).append(ANDROID_NS_NAME).append('=').append('"');
+ sb.append(ANDROID_URI);
+ sb.append('"').append('>').append('\n');
+ }
+
+ // Indent. Use the existing indent found for previous <style> elements in
+ // the resource file - but if that indent was 0 (e.g. <style> elements are
+ // at the left margin) only use it to indent the style elements and use a real
+ // nonzero indent for its children.
+ String indent = " "; //$NON-NLS-1$
+ if (initialIndent == null) {
+ initialIndent = indent;
+ } else if (initialIndent.length() > 0) {
+ indent = initialIndent;
+ }
+ sb.append(initialIndent);
+ String styleTag = "style"; //$NON-NLS-1$ // TODO - use constant in parallel changeset
+ sb.append('<').append(styleTag).append(' ').append(NAME_ATTR).append('=').append('"');
+ sb.append(mStyleName);
+ sb.append('"');
+ if (mParent != null) {
+ sb.append(' ').append(PARENT_ATTR).append('=').append('"');
+ sb.append(mParent);
+ sb.append('"');
+ }
+ sb.append('>').append('\n');
+
+ for (Attr attribute : mChosenAttributes) {
+ sb.append(initialIndent).append(indent);
+ sb.append('<').append(ITEM_TAG).append(' ').append(NAME_ATTR).append('=').append('"');
+ // We've already enforced that regardless of prefix, only attributes with
+ // an Android namespace can be in the set of chosen attributes. Rewrite the
+ // prefix to android here.
+ if (attribute.getPrefix() != null) {
+ sb.append(ANDROID_NS_NAME_PREFIX);
+ }
+ sb.append(attribute.getLocalName());
+ sb.append('"').append('>');
+ sb.append(attribute.getValue());
+ sb.append('<').append('/').append(ITEM_TAG).append('>').append('\n');
+ }
+ sb.append(initialIndent).append('<').append('/').append(styleTag).append('>').append('\n');
+
+ if (createFile) {
+ sb.append('<').append('/').append(ROOT_ELEMENT).append('>').append('\n');
+ }
+ String styleString = sb.toString();
+ return styleString;
+ }
+
+ /** Computes the location in the file to insert the new style element at, as well as
+ * the exact indent string to use to indent the {@code <style>} element.
+ * @param file the styles.xml file to insert into
+ * @return a pair of an insert offset and an indent string
+ */
+ private Pair<Integer, String> computeInsertContext(final IFile file) {
+ int insertAtIndex = -1;
+ // Find the insert of the final </resources> item where we will insert
+ // the new style elements.
+ String indent = null;
+ IModelManager modelManager = StructuredModelManager.getModelManager();
+ IStructuredModel model = null;
+ try {
+ model = modelManager.getModelForRead(file);
+ if (model instanceof IDOMModel) {
+ IDOMModel domModel = (IDOMModel) model;
+ IDOMDocument otherDocument = domModel.getDocument();
+ Element root = otherDocument.getDocumentElement();
+ Node lastChild = root.getLastChild();
+ if (lastChild != null) {
+ if (lastChild instanceof IndexedRegion) {
+ IndexedRegion region = (IndexedRegion) lastChild;
+ insertAtIndex = region.getStartOffset() + region.getLength();
+ }
+
+ // Compute indent
+ while (lastChild != null) {
+ if (lastChild.getNodeType() == Node.ELEMENT_NODE) {
+ IStructuredDocument document = model.getStructuredDocument();
+ indent = AndroidXmlEditor.getIndent(document, lastChild);
+ break;
+ }
+ lastChild = lastChild.getPreviousSibling();
+ }
+ }
+ }
+ } catch (IOException e) {
+ AdtPlugin.log(e, null);
+ } catch (CoreException e) {
+ AdtPlugin.log(e, null);
+ } finally {
+ if (model != null) {
+ model.releaseFromRead();
+ }
+ }
+
+ if (insertAtIndex == -1) {
+ String contents = AdtPlugin.readFile(file);
+ insertAtIndex = contents.indexOf("</" + ROOT_ELEMENT + ">"); //$NON-NLS-1$
+ if (insertAtIndex == -1) {
+ insertAtIndex = contents.length();
+ }
+ }
+
+ return Pair.of(insertAtIndex, indent);
+ }
+
+ @Override
+ VisualRefactoringWizard createWizard() {
+ return new ExtractStyleWizard(this, mEditor);
+ }
+
+ public static class Descriptor extends VisualRefactoringDescriptor {
+ public Descriptor(String project, String description, String comment,
+ Map<String, String> arguments) {
+ super("com.android.ide.eclipse.adt.refactoring.extract.style", //$NON-NLS-1$
+ project, description, comment, arguments);
+ }
+
+ @Override
+ protected Refactoring createRefactoring(Map<String, String> args) {
+ return new ExtractStyleRefactoring(args);
+ }
+ }
+
+ /**
+ * Determines the parent style to be used for this refactoring
+ *
+ * @return the parent style to be used for this refactoring
+ */
+ public String getParentStyle() {
+ Set<String> styles = new HashSet<String>();
+ for (Element element : getElements()) {
+ // Includes "" for elements not setting the style
+ styles.add(element.getAttribute(ATTR_STYLE));
+ }
+
+ if (styles.size() > 1) {
+ // The elements differ in what style attributes they are set to
+ return null;
+ }
+
+ String style = styles.iterator().next();
+ if (style != null && style.length() > 0) {
+ return style;
+ }
+
+ // None of the elements set the style -- see if they have the same widget types
+ // and if so offer to extend the theme style for that widget type
+
+ Set<String> types = new HashSet<String>();
+ for (Element element : getElements()) {
+ types.add(element.getTagName());
+ }
+
+ if (types.size() == 1) {
+ String view = DescriptorsUtils.getBasename(types.iterator().next());
+
+ ResourceResolver resolver = mEditor.getGraphicalEditor().createResolver();
+ // Look up the theme item name, which for a Button would be "buttonStyle", and so on.
+ String n = Character.toLowerCase(view.charAt(0)) + view.substring(1)
+ + "Style"; //$NON-NLS-1$
+ ResourceValue value = resolver.findItemInTheme(n);
+ if (value != null) {
+ ResourceValue resolvedValue = resolver.resolveResValue(value);
+ String name = resolvedValue.getName();
+ if (name != null) {
+ if (resolvedValue.isFramework()) {
+ return ResourceResolver.PREFIX_ANDROID + name;
+ } else {
+ return name;
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleWizard.java
new file mode 100644
index 0000000..2828288
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleWizard.java
@@ -0,0 +1,431 @@
+/*
+ * Copyright (C) 2011 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.eclipse.adt.internal.editors.layout.refactoring;
+
+import static org.eclipse.jface.viewers.StyledString.DECORATIONS_STYLER;
+import static org.eclipse.jface.viewers.StyledString.QUALIFIER_STYLER;
+
+import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
+import com.android.ide.eclipse.adt.internal.resources.ResourceNameValidator;
+import com.android.resources.ResourceType;
+import com.android.util.Pair;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.jface.viewers.CheckStateChangedEvent;
+import org.eclipse.jface.viewers.CheckboxTableViewer;
+import org.eclipse.jface.viewers.ICheckStateListener;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.StyledCellLabelProvider;
+import org.eclipse.jface.viewers.StyledString;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerCell;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.Text;
+import org.w3c.dom.Attr;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+
+class ExtractStyleWizard extends VisualRefactoringWizard {
+ public ExtractStyleWizard(ExtractStyleRefactoring ref, LayoutEditor editor) {
+ super(ref, editor);
+ setDefaultPageTitle(ref.getName());
+ }
+
+ @Override
+ protected void addUserInputPages() {
+ String initialName = "styleName";
+ addPage(new InputPage(mEditor.getProject(), initialName));
+ }
+
+ /**
+ * Wizard page which inputs parameters for the {@link ExtractStyleRefactoring}
+ * operation
+ */
+ private static class InputPage extends VisualRefactoringInputPage {
+ private final IProject mProject;
+ private final String mSuggestedName;
+ private Text mNameText;
+ private Table mTable;
+ private Button mRemoveExtracted;
+ private Button mSetStyle;
+ private Button mRemoveAll;
+ private Button mExtend;;
+ private CheckboxTableViewer mCheckedView;
+
+ private String mParentStyle;
+ private Set<Attr> mInSelection;
+ private List<Attr> mAllAttributes;
+ private Map<Attr, Integer> mFrequencyCount;
+ private Set<Attr> mShown;
+ private List<Attr> mInitialChecked;
+ private List<Map.Entry<String, List<Attr>>> mRoot;
+ private Map<String, List<Attr>> mAvailableAttributes;
+
+ public InputPage(IProject project, String suggestedName) {
+ super("ExtractStyleInputPage");
+ mProject = project;
+ mSuggestedName = suggestedName;
+ }
+
+ public void createControl(Composite parent) {
+ initialize();
+
+ Composite composite = new Composite(parent, SWT.NONE);
+ composite.setLayout(new GridLayout(2, false));
+
+ Label nameLabel = new Label(composite, SWT.NONE);
+ nameLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
+ nameLabel.setText("Style Name:");
+
+ mNameText = new Text(composite, SWT.BORDER);
+ mNameText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+ mNameText.addModifyListener(mModifyValidateListener);
+
+ mRemoveExtracted = new Button(composite, SWT.CHECK);
+ mRemoveExtracted.setSelection(true);
+ mRemoveExtracted.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false, 2, 1));
+ mRemoveExtracted.setText("Remove extracted attributes");
+ mRemoveExtracted.addSelectionListener(mSelectionValidateListener);
+
+ mRemoveAll = new Button(composite, SWT.CHECK);
+ mRemoveAll.setSelection(false);
+ mRemoveAll.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false, 2, 1));
+ mRemoveAll.setText("Remove all extracted attributes regardless of value");
+ mRemoveAll.addSelectionListener(mSelectionValidateListener);
+
+ boolean defaultSetStyle = false;
+ if (mParentStyle != null) {
+ mExtend = new Button(composite, SWT.CHECK);
+ mExtend.setSelection(true);
+ mExtend.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false, 2, 1));
+ mExtend.setText(String.format("Extend %1$s", mParentStyle));
+ mExtend.addSelectionListener(mSelectionValidateListener);
+ defaultSetStyle = true;
+ }
+
+ mSetStyle = new Button(composite, SWT.CHECK);
+ mSetStyle.setSelection(defaultSetStyle);
+ mSetStyle.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false, 2, 1));
+ mSetStyle.setText("Set style attribute on extracted elements");
+ mSetStyle.addSelectionListener(mSelectionValidateListener);
+
+ new Label(composite, SWT.NONE);
+ new Label(composite, SWT.NONE);
+
+ Label tableLabel = new Label(composite, SWT.NONE);
+ tableLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1));
+ tableLabel.setText("Choose style attributes to extract:");
+
+ mCheckedView = CheckboxTableViewer.newCheckList(composite, SWT.BORDER
+ | SWT.FULL_SELECTION | SWT.HIDE_SELECTION);
+ mTable = mCheckedView.getTable();
+ mTable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 2));
+ ((GridData) mTable.getLayoutData()).heightHint = 200;
+
+ Object[] children = mAllAttributes.toArray();
+
+ mCheckedView.setContentProvider(new ArgumentContentProvider(mRoot, children));
+ mCheckedView.setLabelProvider(new ArgumentLabelProvider(mFrequencyCount));
+ mCheckedView.setInput(mRoot);
+ final Object[] initialSelection = mInitialChecked.toArray();
+ mCheckedView.setCheckedElements(initialSelection);
+
+ mCheckedView.addCheckStateListener(new ICheckStateListener() {
+ public void checkStateChanged(CheckStateChangedEvent event) {
+ // Try to disable other elements that conflict with this
+ boolean isChecked = event.getChecked();
+ if (isChecked) {
+ Attr attribute = (Attr) event.getElement();
+ List<Attr> list = mAvailableAttributes.get(attribute.getLocalName());
+ for (Attr other : list) {
+ if (other != attribute && mShown.contains(other)) {
+ mCheckedView.setChecked(other, false);
+ }
+ }
+ }
+
+ validatePage();
+ }
+ });
+
+ // Select All / Deselect All
+ Composite buttonForm = new Composite(composite, SWT.NONE);
+ buttonForm.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1));
+ RowLayout rowLayout = new RowLayout(SWT.HORIZONTAL);
+ rowLayout.marginTop = 0;
+ rowLayout.marginLeft = 0;
+ buttonForm.setLayout(rowLayout);
+ Button checkAllButton = new Button(buttonForm, SWT.FLAT);
+ checkAllButton.setText("Select All");
+ checkAllButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ // Select "all" (but not conflicting settings)
+ mCheckedView.setCheckedElements(initialSelection);
+ }
+ });
+ Button uncheckAllButton = new Button(buttonForm, SWT.FLAT);
+ uncheckAllButton.setText("Deselect All");
+ uncheckAllButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ mCheckedView.setAllChecked(false);
+ }
+ });
+
+ // Initialize UI:
+ if (mSuggestedName != null) {
+ mNameText.setText(mSuggestedName);
+ }
+
+ setControl(composite);
+ validatePage();
+ }
+
+ private void initialize() {
+ ExtractStyleRefactoring ref = (ExtractStyleRefactoring) getRefactoring();
+
+ mParentStyle = ref.getParentStyle();
+
+ // Set up data structures needed by the wizard -- to compute the actual
+ // attributes to list in the wizard (there could be multiple attributes
+ // of the same name (on different elements) and we only want to show one, etc.)
+
+ Pair<Map<String, List<Attr>>, Set<Attr>> result = ref.getAvailableAttributes();
+ // List of all available attributes on the selected elements
+ mAvailableAttributes = result.getFirst();
+ // Set of attributes that overlap the text selection, or all attributes if
+ // wizard is invoked from GUI context
+ mInSelection = result.getSecond();
+
+ // The root data structure, which we set as the table root. The content provider
+ // will produce children from it. This is the entry set of a map from
+ // attribute name to list of attribute nodes for that attribute name.
+ mRoot = new ArrayList<Map.Entry<String, List<Attr>>>(
+ mAvailableAttributes.entrySet());
+
+ // Sort the items by attribute name -- the attribute name is the key
+ // in the entry set above.
+ Collections.sort(mRoot, new Comparator<Map.Entry<String, List<Attr>>>() {
+ public int compare(Map.Entry<String, List<Attr>> e1,
+ Map.Entry<String, List<Attr>> e2) {
+ return e1.getKey().compareTo(e2.getKey());
+ }
+ });
+
+ // Set of attributes actually included in the list shown to the user.
+ // (There could be many additional "aliasing" nodes on other elements
+ // with the same name.) Note however that we DO show multiple attribute
+ // occurrences of the same attribute name: precisely one for each unique -value-
+ // of that attribute.
+ mShown = new HashSet<Attr>();
+
+ // The list of initially checked attributes.
+ mInitialChecked = new ArrayList<Attr>();
+
+ // All attributes.
+ mAllAttributes = new ArrayList<Attr>();
+
+ // Frequency count, from attribute to integer. Attributes that do not
+ // appear in the list have frequency 1, not 0.
+ mFrequencyCount = new HashMap<Attr, Integer>();
+
+ for (Map.Entry<String, List<Attr>> entry : mRoot) {
+ // Iterate over all attributes of the same name, and sort them
+ // by value. This will make it easy to list each -unique- value in the
+ // wizard.
+ List<Attr> attrList = entry.getValue();
+ Collections.sort(attrList, new Comparator<Attr>() {
+ public int compare(Attr a1, Attr a2) {
+ return a1.getValue().compareTo(a2.getValue());
+ }
+ });
+
+ // We need to compute a couple of things: the frequency for all identical
+ // values (and stash them in the frequency map), and record the first
+ // attribute with a particular value into the list of attributes to
+ // be shown.
+ Attr prevAttr = null;
+ String prev = null;
+ List<Attr> uniqueValueAttrs = new ArrayList<Attr>();
+ for (Attr attr : attrList) {
+ String value = attr.getValue();
+ if (value.equals(prev)) {
+ Integer count = mFrequencyCount.get(prevAttr);
+ if (count == null) {
+ count = Integer.valueOf(2);
+ } else {
+ count = Integer.valueOf(count.intValue() + 1);
+ }
+ mFrequencyCount.put(prevAttr, count);
+ } else {
+ uniqueValueAttrs.add(attr);
+ prev = value;
+ prevAttr = attr;
+ }
+ }
+
+ // Sort the values by frequency (and for equal frequencies, alphabetically
+ // by value)
+ Collections.sort(uniqueValueAttrs, new Comparator<Attr>() {
+ public int compare(Attr a1, Attr a2) {
+ Integer f1 = mFrequencyCount.get(a1);
+ Integer f2 = mFrequencyCount.get(a2);
+ if (f1 == null) {
+ f1 = Integer.valueOf(1);
+ }
+ if (f2 == null) {
+ f2 = Integer.valueOf(1);
+ }
+ int delta = f2.intValue() - f1.intValue();
+ if (delta != 0) {
+ return delta;
+ } else {
+ return a1.getValue().compareTo(a2.getValue());
+ }
+ }
+ });
+
+ // Add the items in order, and select those attributes that overlap
+ // the selection
+ mAllAttributes.addAll(uniqueValueAttrs);
+ mShown.addAll(uniqueValueAttrs);
+ Attr first = uniqueValueAttrs.get(0);
+ if (mInSelection.contains(first)) {
+ mInitialChecked.add(first);
+ }
+ }
+ }
+
+ @Override
+ protected boolean validatePage() {
+ boolean ok = true;
+
+ String text = mNameText.getText().trim();
+
+ if (text.length() == 0) {
+ setErrorMessage("Provide a name for the new style");
+ ok = false;
+ } else {
+ ResourceNameValidator validator = ResourceNameValidator.create(false, mProject,
+ ResourceType.STYLE);
+ String message = validator.isValid(text);
+ if (message != null) {
+ setErrorMessage(message);
+ ok = false;
+ }
+ }
+
+ Object[] checkedElements = mCheckedView.getCheckedElements();
+ if (checkedElements.length == 0) {
+ setErrorMessage("Choose at least one attribute to extract");
+ ok = false;
+ }
+
+ if (ok) {
+ setErrorMessage(null);
+
+ // Record state
+ ExtractStyleRefactoring refactoring = (ExtractStyleRefactoring) getRefactoring();
+ refactoring.setStyleName(text);
+ refactoring.setRemoveExtracted(mRemoveExtracted.getSelection());
+ refactoring.setRemoveAll(mRemoveAll.getSelection());
+ refactoring.setApplyStyle(mSetStyle.getSelection());
+ if (mExtend != null && mExtend.getSelection()) {
+ refactoring.setParent(mParentStyle);
+ }
+ List<Attr> attributes = new ArrayList<Attr>();
+ for (Object o : checkedElements) {
+ attributes.add((Attr) o);
+ }
+ refactoring.setChosenAttributes(attributes);
+ }
+
+ setPageComplete(ok);
+ return ok;
+ }
+ }
+
+ private static class ArgumentLabelProvider extends StyledCellLabelProvider {
+ public ArgumentLabelProvider(Map<Attr, Integer> frequencyCount) {
+ mFrequencyCount = frequencyCount;
+ }
+
+ private Map<Attr, Integer> mFrequencyCount =
+ new HashMap<Attr, Integer>();
+
+ @Override
+ public void update(ViewerCell cell) {
+ Object element = cell.getElement();
+ Attr attribute = (Attr) element;
+
+ StyledString styledString = new StyledString();
+ styledString.append(attribute.getLocalName());
+ styledString.append(" = ", QUALIFIER_STYLER);
+ styledString.append(attribute.getValue());
+
+ Integer f = mFrequencyCount.get(attribute);
+ if (f != null) {
+ styledString.append(String.format(" (%d)", f.intValue()), DECORATIONS_STYLER);
+ }
+ cell.setText(styledString.toString());
+ cell.setStyleRanges(styledString.getStyleRanges());
+ super.update(cell);
+ }
+ }
+
+ private static class ArgumentContentProvider implements IStructuredContentProvider {
+ private Object[] mChildren;
+ private List<Entry<String, List<Attr>>> mRoot;
+
+ public ArgumentContentProvider(List<Entry<String, List<Attr>>> root, Object[] children) {
+ mRoot = root;
+ mChildren = children;
+ }
+
+ public Object[] getElements(Object inputElement) {
+ if (inputElement == mRoot) {
+ return mChildren;
+ }
+
+ return new Object[0];
+ }
+
+ public void dispose() {
+ }
+
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/RefactoringAssistant.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/RefactoringAssistant.java
index 2de8536..4baad1d 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/RefactoringAssistant.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/RefactoringAssistant.java
@@ -81,6 +81,7 @@ public class RefactoringAssistant implements IQuickAssistProcessor {
boolean isValue = false;
boolean isTagName = false;
+ boolean isAttributeName = false;
IStructuredModel model = null;
try {
model = xmlEditor.getModelForRead();
@@ -100,6 +101,9 @@ public class RefactoringAssistant implements IQuickAssistProcessor {
|| type.equals(DOMRegionContext.XML_TAG_OPEN)
|| type.equals(DOMRegionContext.XML_TAG_CLOSE)) {
isTagName = true;
+ } else if (type.equals(DOMRegionContext.XML_TAG_ATTRIBUTE_NAME)
+ || type.equals(DOMRegionContext.XML_TAG_ATTRIBUTE_EQUALS)) {
+ isAttributeName = true;
}
}
} finally {
@@ -108,7 +112,7 @@ public class RefactoringAssistant implements IQuickAssistProcessor {
}
}
- if (isValue || isTagName) {
+ if (isValue || isTagName || isAttributeName) {
StructuredTextEditor structuredEditor = xmlEditor.getStructuredTextEditor();
ISelectionProvider provider = structuredEditor.getSelectionProvider();
ISelection selection = provider.getSelection();
@@ -117,14 +121,41 @@ public class RefactoringAssistant implements IQuickAssistProcessor {
// These operations currently do not work on ranges
if (textSelection.getLength() > 0) {
+ // ...except for Extract Style where the actual attributes overlapping
+ // the selection is going to be the set of eligible attributes
+ if (isAttributeName && xmlEditor instanceof LayoutEditor) {
+ LayoutEditor editor = (LayoutEditor) xmlEditor;
+ return new ICompletionProposal[] {
+ new RefactoringProposal(editor,
+ new ExtractStyleRefactoring(file, editor, textSelection, null))
+ };
+ }
return null;
}
- if (isValue) {
+ if (isAttributeName && xmlEditor instanceof LayoutEditor) {
+ LayoutEditor editor = (LayoutEditor) xmlEditor;
return new ICompletionProposal[] {
+ new RefactoringProposal(editor,
+ new ExtractStyleRefactoring(file, editor, textSelection, null)),
+ };
+ } else if (isValue) {
+ if (xmlEditor instanceof LayoutEditor) {
+ LayoutEditor editor = (LayoutEditor) xmlEditor;
+ return new ICompletionProposal[] {
+ new RefactoringProposal(xmlEditor,
+ new ExtractStringRefactoring(file, xmlEditor,
+ textSelection)),
+ new RefactoringProposal(editor,
+ new ExtractStyleRefactoring(file, editor,
+ textSelection, null)),
+ };
+ } else {
+ return new ICompletionProposal[] {
new RefactoringProposal(xmlEditor,
new ExtractStringRefactoring(file, xmlEditor, textSelection))
- };
+ };
+ }
} else if (xmlEditor instanceof LayoutEditor) {
LayoutEditor editor = (LayoutEditor) xmlEditor;
return new ICompletionProposal[] {
@@ -135,6 +166,8 @@ public class RefactoringAssistant implements IQuickAssistProcessor {
new RefactoringProposal(editor,
new ChangeLayoutRefactoring(file, editor, textSelection, null)),
new RefactoringProposal(editor,
+ new ExtractStyleRefactoring(file, editor, textSelection, null)),
+ new RefactoringProposal(editor,
new ExtractIncludeRefactoring(file, editor, textSelection, null)),
};
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/VisualRefactoring.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/VisualRefactoring.java
index 4b2a0cb..7db0a5c 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/VisualRefactoring.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/VisualRefactoring.java
@@ -113,6 +113,10 @@ public abstract class VisualRefactoring extends Refactoring {
protected final List<Element> mElements;
protected final ITreeSelection mTreeSelection;
protected final ITextSelection mSelection;
+ /** Same as {@link #mSelectionStart} but not adjusted to element edges */
+ protected int mOriginalSelectionStart = -1;
+ /** Same as {@link #mSelectionEnd} but not adjusted to element edges */
+ protected int mOriginalSelectionEnd = -1;
protected final Map<Element, String> mGeneratedIdMap = new HashMap<Element, String>();
protected final Set<String> mGeneratedIds = new HashSet<String>();
@@ -132,6 +136,8 @@ public abstract class VisualRefactoring extends Refactoring {
mFile = (IFile) ResourcesPlugin.getWorkspace().getRoot().findMember(path);
mSelectionStart = Integer.parseInt(arguments.get(KEY_SEL_START));
mSelectionEnd = Integer.parseInt(arguments.get(KEY_SEL_END));
+ mOriginalSelectionStart = mSelectionStart;
+ mOriginalSelectionEnd = mSelectionEnd;
mEditor = null;
mElements = null;
mSelection = null;
@@ -147,6 +153,8 @@ public abstract class VisualRefactoring extends Refactoring {
mProject = editor != null ? editor.getProject() : null;
mSelectionStart = 0;
mSelectionEnd = 0;
+ mOriginalSelectionStart = 0;
+ mOriginalSelectionEnd = 0;
mSelection = null;
mTreeSelection = null;
@@ -162,6 +170,8 @@ public abstract class VisualRefactoring extends Refactoring {
if (start >= 0) {
mSelectionStart = start;
mSelectionEnd = end;
+ mOriginalSelectionStart = start;
+ mOriginalSelectionEnd = end;
}
}
@@ -199,11 +209,19 @@ public abstract class VisualRefactoring extends Refactoring {
if (start >= 0) {
mSelectionStart = start;
mSelectionEnd = end;
+ mOriginalSelectionStart = mSelectionStart;
+ mOriginalSelectionEnd = mSelectionEnd;
+ }
+ if (selection != null) {
+ mOriginalSelectionStart = selection.getOffset();
+ mOriginalSelectionEnd = mOriginalSelectionStart + selection.getLength();
}
} else if (selection != null) {
// TODO: update selection to boundaries!
mSelectionStart = selection.getOffset();
mSelectionEnd = mSelectionStart + selection.getLength();
+ mOriginalSelectionStart = mSelectionStart;
+ mOriginalSelectionEnd = mSelectionEnd;
}
mElements = initElements();
@@ -663,6 +681,8 @@ public abstract class VisualRefactoring extends Refactoring {
} else if (mSelection != null) {
mSelectionStart = mSelection.getOffset();
mSelectionEnd = mSelectionStart + mSelection.getLength();
+ mOriginalSelectionStart = mSelectionStart;
+ mOriginalSelectionEnd = mSelectionEnd;
// Figure out the range of selected nodes from the document offsets
IStructuredDocument doc = mEditor.getStructuredDocument();
@@ -919,7 +939,11 @@ public abstract class VisualRefactoring extends Refactoring {
private void addAttributeDeclaration(MultiTextEdit rootEdit, int offset,
String attributePrefix, String attributeName, String attributeValue) {
StringBuilder sb = new StringBuilder();
- sb.append(' ').append(attributePrefix).append(':');
+ sb.append(' ');
+
+ if (attributePrefix != null) {
+ sb.append(attributePrefix).append(':');
+ }
sb.append(attributeName).append('=').append('"');
sb.append(attributeValue).append('"');
@@ -942,7 +966,8 @@ public abstract class VisualRefactoring extends Refactoring {
int valueStart = -1;
boolean useNextValue = false;
- String targetName = attributePrefix + ':' + attributeName;
+ String targetName = attributePrefix != null
+ ? attributePrefix + ':' + attributeName : attributeName;
// Look at all attribute values and look for an id reference match
for (int j = 0; j < region.getNumberOfRegions(); j++) {
@@ -984,16 +1009,22 @@ public abstract class VisualRefactoring extends Refactoring {
String attributeName) {
if (element.hasAttributeNS(uri, attributeName)) {
Attr attribute = element.getAttributeNodeNS(uri, attributeName);
- IndexedRegion region = getRegion(attribute);
- if (region != null) {
- int startOffset = region.getStartOffset();
- int endOffset = region.getEndOffset();
- DeleteEdit deletion = new DeleteEdit(startOffset, endOffset - startOffset);
- rootEdit.addChild(deletion);
- }
+ removeAttribute(rootEdit, attribute);
+ }
+ }
+
+ /** Strips out the given attribute, if defined */
+ protected void removeAttribute(MultiTextEdit rootEdit, Attr attribute) {
+ IndexedRegion region = getRegion(attribute);
+ if (region != null) {
+ int startOffset = region.getStartOffset();
+ int endOffset = region.getEndOffset();
+ DeleteEdit deletion = new DeleteEdit(startOffset, endOffset - startOffset);
+ rootEdit.addChild(deletion);
}
}
+
/**
* Removes the given element's opening and closing tags (including all of its
* attributes) but leaves any children alone
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/resources/descriptors/ResourcesDescriptors.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/resources/descriptors/ResourcesDescriptors.java
index 8ff6b6e..6a2368a 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/resources/descriptors/ResourcesDescriptors.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/resources/descriptors/ResourcesDescriptors.java
@@ -42,6 +42,7 @@ public final class ResourcesDescriptors implements IDescriptorProvider {
public static final String ITEM_TAG = "item"; //$NON-NLS-1$
public static final String NAME_ATTR = "name"; //$NON-NLS-1$
public static final String TYPE_ATTR = "type"; //$NON-NLS-1$
+ public static final String PARENT_ATTR = "parent"; //$NON-NLS-1$
private static final ResourcesDescriptors sThis = new ResourcesDescriptors();
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileWizard.java
index 9c0d31c..a5a24d7 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileWizard.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileWizard.java
@@ -52,6 +52,7 @@ import java.io.UnsupportedEncodingException;
* the resource folder, resource type and file name. It then creates the XML file.
*/
public class NewXmlFileWizard extends Wizard implements INewWizard {
+ public static final String XML_HEADER_LINE = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"; //$NON-NLS-1$
private static final String PROJECT_LOGO_LARGE = "android_large"; //$NON-NLS-1$
@@ -111,11 +112,10 @@ public class NewXmlFileWizard extends Wizard implements INewWizard {
return false;
} else {
IFile file = created.getFirst();
- IRegion region = created.getSecond();
- // Open the file in an editor
+ // Open the file
try {
- AdtPlugin.openFile(file, region);
+ AdtPlugin.openFile(file, null, false /* showEditorTab */);
} catch (PartInitException e) {
AdtPlugin.log(e, "Failed to create %1$s: missing type", //$NON-NLS-1$
file.getFullPath().toString());
@@ -166,7 +166,7 @@ public class NewXmlFileWizard extends Wizard implements INewWizard {
createWsParentDirectory(file.getParent());
}
- StringBuilder sb = new StringBuilder("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"); //$NON-NLS-1$
+ StringBuilder sb = new StringBuilder(XML_HEADER_LINE);
sb.append('<').append(root);
if (xmlns != null) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/build/AaptQuickFixTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/build/AaptQuickFixTest.java
index 2832c1d..07c0fe1 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/build/AaptQuickFixTest.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/build/AaptQuickFixTest.java
@@ -178,7 +178,8 @@ public class AaptQuickFixTest extends AdtProjectTest {
((FileEditorInput) currentFile.getEditorInput()).getFile().getProjectRelativePath());
// Look up caret offset
- assertTrue(currentFile instanceof AndroidXmlEditor);
+ assertTrue(currentFile != null ? currentFile.getClass().getName() : "null",
+ currentFile instanceof AndroidXmlEditor);
AndroidXmlEditor newEditor = (AndroidXmlEditor) currentFile;
ISourceViewer newViewer = newEditor.getStructuredSourceViewer();
Point selectedRange = newViewer.getSelectedRange();
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/AdtProjectTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/AdtProjectTest.java
index f987729..db74295 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/AdtProjectTest.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/AdtProjectTest.java
@@ -299,7 +299,6 @@ public class AdtProjectTest extends SdkTestCase {
* (such as code completion apply-tests)
*/
protected String getDiff(String before, String after) {
-
// Do line by line analysis
String[] beforeLines = before.split("\n");
String[] afterLines = after.split("\n");
@@ -324,18 +323,44 @@ public class AdtProjectTest extends SdkTestCase {
}
}
+
+ boolean showBeforeWindow = firstDelta >= beforeLines.length - lastDelta;
+ boolean showAfterWindow = firstDelta >= afterLines.length - lastDelta;
+
StringBuilder sb = new StringBuilder();
+ if (showAfterWindow && firstDelta > 0) {
+ sb.append(" ");
+ sb.append(afterLines[firstDelta - 1]);
+ sb.append('\n');
+ }
for (int i = firstDelta; i < beforeLines.length - lastDelta; i++) {
sb.append("< ");
sb.append(beforeLines[i]);
sb.append('\n');
}
+ if (showAfterWindow && lastDelta < afterLines.length - 1) {
+ sb.append(" ");
+ sb.append(afterLines[afterLines.length - (lastDelta -1)]);
+ sb.append('\n');
+ }
+
sb.append("---\n");
+
+ if (showBeforeWindow && firstDelta > 0) {
+ sb.append(" ");
+ sb.append(beforeLines[firstDelta - 1]);
+ sb.append('\n');
+ }
for (int i = firstDelta; i < afterLines.length - lastDelta; i++) {
sb.append("> ");
sb.append(afterLines[i]);
sb.append('\n');
}
+ if (showBeforeWindow && lastDelta < beforeLines.length - 1) {
+ sb.append(" ");
+ sb.append(beforeLines[beforeLines.length - (lastDelta -1)]);
+ sb.append('\n');
+ }
return sb.toString();
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleRefactoringTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleRefactoringTest.java
new file mode 100644
index 0000000..a7bfbad
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleRefactoringTest.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2011 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.eclipse.adt.internal.editors.layout.refactoring;
+
+import com.android.util.Pair;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.jface.text.ITextSelection;
+import org.eclipse.jface.text.TextSelection;
+import org.eclipse.ltk.core.refactoring.Change;
+import org.eclipse.ltk.core.refactoring.TextFileChange;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.ide.IDE;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class ExtractStyleRefactoringTest extends RefactoringTest {
+ @Override
+ protected boolean testCaseNeedsUniqueProject() {
+ return true;
+ }
+
+ public void testExtract1() throws Exception {
+ // Test extracting into a new style file
+ checkRefactoring("extractstyle1.xml", "newstyles.xml", "newstyle",
+ false /* removeExtracted */, false /* applyStyle */, null, 1, "@+id/button2");
+ }
+
+ public void testExtract1b() throws Exception {
+ // Extract and apply new style
+ checkRefactoring("extractstyle1.xml", "newstyles2.xml", "newstyle",
+ false /* removeExtracted */, true /* applyStyle */, null, 2, "@+id/button2");
+ }
+
+ public void testExtract1c() throws Exception {
+ // Extract and remove extracted
+ checkRefactoring("extractstyle1.xml", "newstyles3.xml", "newstyle",
+ true /* removeExtracted */, false /* applyStyle */, null, 2, "@+id/button2");
+ }
+
+ public void testExtract1d() throws Exception {
+ // Extract and apply style and remove extracted
+ checkRefactoring("extractstyle1.xml", "newstyles4.xml", "newstyle",
+ true /* removeExtracted */, true /* applyStyle */, null, 2, "@+id/button2");
+ }
+
+ public void testExtract2() throws Exception {
+ getTestDataFile(getProject(), "navigationstyles.xml", "res/values/navigationstyles.xml");
+
+ // -Modify- the existing styles.xml file
+ checkRefactoring("extractstyle1.xml", "navigationstyles.xml", "newstyle",
+ true /* removeExtracted */, true /* applyStyle */, null, 2, "@+id/button2");
+ }
+
+ public void testExtract3() throws Exception {
+ // Select multiple elements - overlap in values.
+ checkRefactoring("extractstyle1.xml", "newstyles4.xml", "newstyle",
+ true /* removeExtracted */, true /* applyStyle */, null, 2,
+ "@+id/button1", "@+id/button2");
+ }
+
+ // This test fails for some reason - not in the refactoring (checked manually)
+ // but the DOM model returns null when run in a test context.
+ public void testExtract4() throws Exception {
+ // Test extracting on a single caret position over an attribute: Should extract
+ // just that one attribute
+ checkRefactoringByOffset("extractstyle1.xml", "newstyles5.xml", "newstyle",
+ true /* removeExtracted */, true /* applyStyle */, null, 2,
+ "android:text^Color=\"#FF00FF\"", "android:text^Color=\"#FF00FF\"");
+ }
+
+ public void testExtract5() throws Exception {
+ // Test extracting on a range selection inside an element: should extract just
+ // the attributes that overlap the selection
+ checkRefactoringByOffset("extractstyle1.xml", "newstyles6.xml", "newstyle",
+ true /* removeExtracted */, true /* applyStyle */, null, 2,
+ "android:^textSize=\"20pt",
+ "android:id=\"@+id/button1\" android:layout_a^lignParentBottom");
+ }
+
+ public void testExtract6() throws Exception {
+ // Test extracting on a single caret position which is not over any attributes:
+ checkRefactoringByOffset("extractstyle1.xml", "newstyles7.xml", "newstyle",
+ true /* removeExtracted */, true /* applyStyle */, null, 0,
+ "<Bu^tton", "<Bu^tton");
+ }
+
+ public void testExtract7() throws Exception {
+ // Verify that even with a different namespace prefix we end up with android:
+ // in the extracted style
+ checkRefactoring("extractstyle2.xml", "newstyles8.xml", "newstyle",
+ true /* removeExtracted */, true /* applyStyle */, null, 2,
+ "@+id/button1", "@+id/button2");
+ }
+
+ public void testExtract8() throws Exception {
+ // Test setting parent style
+ checkRefactoring("extractstyle1.xml", "newstyles3.xml", "newstyle",
+ true /* removeExtracted */, false /* applyStyle */, "android:Widget.Button",
+ 2, "@+id/button2");
+ }
+
+ // Check extract style on a selection of elements
+ private void checkRefactoring(String basename, String styleFileName, String newStyleName,
+ boolean removeExtracted, boolean applyStyle, String parentStyle,
+ int expectedModifiedFileCount, String... ids) throws Exception {
+ assertTrue(ids.length > 0);
+
+ IFile file = getLayoutFile(getProject(), basename);
+ TestContext info = setupTestContext(file, basename);
+ TestLayoutEditor layoutEditor = info.mLayoutEditor;
+ List<Element> selectedElements = getElements(info.mElement, ids);
+
+ // Open the file such that ModelManager.getExistingModelForRead() in DomUtilities
+ // will succeed
+ IWorkbench workbench = PlatformUI.getWorkbench();
+ IWorkbenchWindow activeWorkbenchWindow = workbench.getActiveWorkbenchWindow();
+ IWorkbenchPage page = activeWorkbenchWindow.getActivePage();
+ IDE.openEditor(page, file);
+
+ ExtractStyleRefactoring refactoring = new ExtractStyleRefactoring(selectedElements,
+ layoutEditor);
+ checkRefactoring(basename, styleFileName, newStyleName, removeExtracted, applyStyle,
+ parentStyle, expectedModifiedFileCount, file, refactoring);
+ }
+
+ // Check extract style against a set of editor text locations
+ private void checkRefactoringByOffset(String basename, String styleFileName,
+ String newStyleName, boolean removeExtracted, boolean applyStyle,
+ String parentStyle,
+ int expectedModifiedFileCount, String beginCaretLocation, String endCaretLocation)
+ throws Exception {
+ IFile file = getLayoutFile(getProject(), basename);
+ int beginOffset = getCaretOffset(file, beginCaretLocation);
+ int endOffset = getCaretOffset(file, endCaretLocation);
+
+ TestContext info = setupTestContext(file, basename);
+ TestLayoutEditor layoutEditor = info.mLayoutEditor;
+
+ // Open the file such that ModelManager.getExistingModelForRead() in DomUtilities
+ // will succeed
+ IWorkbench workbench = PlatformUI.getWorkbench();
+ IWorkbenchWindow activeWorkbenchWindow = workbench.getActiveWorkbenchWindow();
+ IWorkbenchPage page = activeWorkbenchWindow.getActivePage();
+ IDE.openEditor(page, file);
+
+ ITextSelection selection = new TextSelection(beginOffset, endOffset - beginOffset);
+ ExtractStyleRefactoring refactoring = new ExtractStyleRefactoring(file,
+ layoutEditor, selection, null);
+ checkRefactoring(basename, styleFileName, newStyleName, removeExtracted, applyStyle,
+ parentStyle, expectedModifiedFileCount, file, refactoring);
+ }
+
+ // Common test code used by the other two check methods
+ private void checkRefactoring(String basename, String styleFileName, String newStyleName,
+ boolean removeExtracted, boolean applyStyle, String parentStyle,
+ int expectedModifiedFileCount, IFile file,
+ ExtractStyleRefactoring refactoring) throws Exception {
+ refactoring.setStyleName(newStyleName);
+ refactoring.setApplyStyle(applyStyle);
+ refactoring.setRemoveExtracted(removeExtracted);
+ refactoring.setStyleFileName(styleFileName);
+ refactoring.setParent(parentStyle);
+
+ // Pick the attributes to extract -- for now everything (and where there are
+ // conflicting values, pick the first one)
+ Pair<Map<String, List<Attr>>, Set<Attr>> result = refactoring.getAvailableAttributes();
+ Map<String, List<Attr>> availableAttributes = result.getFirst();
+ Set<Attr> selected = result.getSecond();
+ List<Attr> chosenAttributes = new ArrayList<Attr>();
+ for (List<Attr> list : availableAttributes.values()) {
+ Collections.sort(list, new Comparator<Attr>() {
+ public int compare(Attr a1, Attr a2) {
+ return a1.getValue().compareTo(a2.getValue());
+ }
+ });
+ Attr attr = list.get(0);
+ if (selected.contains(attr)) {
+ chosenAttributes.add(attr);
+ }
+ }
+ refactoring.setChosenAttributes(chosenAttributes);
+
+ List<Change> changes = refactoring.computeChanges();
+ assertEquals(expectedModifiedFileCount, changes.size());
+
+ Map<IPath,String> fileToGolden = new HashMap<IPath,String>();
+ IPath sourcePath = file.getProjectRelativePath();
+ fileToGolden.put(sourcePath, basename);
+ IPath newPath = refactoring.getStyleFile(getProject()).getProjectRelativePath();
+ fileToGolden.put(newPath, styleFileName);
+
+ checkEdits(changes, fileToGolden, true);
+
+ int modifiedFileCount = 0;
+ for (Change change : changes) {
+ if (change instanceof TextFileChange) {
+ modifiedFileCount++;
+ }
+ }
+ assertEquals(expectedModifiedFileCount, modifiedFileCount);
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/RefactoringAssistantTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/RefactoringAssistantTest.java
index 773c43c..498f65a 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/RefactoringAssistantTest.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/RefactoringAssistantTest.java
@@ -71,7 +71,7 @@ public class RefactoringAssistantTest extends AdtProjectTest {
final int offset = caretContextIndex + caretDelta;
- RefactoringAssistant aaptQuickFix = new RefactoringAssistant();
+ RefactoringAssistant refactoringAssistant = new RefactoringAssistant();
// Open file
IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
@@ -94,7 +94,7 @@ public class RefactoringAssistantTest extends AdtProjectTest {
return viewer;
}
};
- ICompletionProposal[] proposals = aaptQuickFix
+ ICompletionProposal[] proposals = refactoringAssistant
.computeQuickAssistProposals(invocationContext);
if (proposals != null) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/RefactoringTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/RefactoringTest.java
index 947840c..661b553 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/RefactoringTest.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/RefactoringTest.java
@@ -16,6 +16,7 @@
package com.android.ide.eclipse.adt.internal.editors.layout.refactoring;
import static com.android.ide.common.layout.LayoutConstants.ANDROID_WIDGET_PREFIX;
+import static com.android.ide.eclipse.adt.AdtConstants.DOT_XML;
import com.android.ide.common.rendering.api.ViewInfo;
import com.android.ide.eclipse.adt.internal.editors.layout.gle2.CanvasViewInfo;
@@ -107,8 +108,12 @@ public class RefactoringTest extends AdtProjectTest {
}
protected void checkEdits(List<Change> changes,
- Map<IPath, String> fileToGoldenName) throws BadLocationException,
- IOException {
+ Map<IPath, String> fileToGoldenName) throws BadLocationException {
+ checkEdits(changes, fileToGoldenName, false);
+ }
+
+ protected void checkEdits(List<Change> changes,
+ Map<IPath, String> fileToGoldenName, boolean createDiffs) throws BadLocationException {
for (Change change : changes) {
if (change instanceof TextFileChange) {
TextFileChange tf = (TextFileChange) change;
@@ -125,6 +130,8 @@ public class RefactoringTest extends AdtProjectTest {
IDocument document = new Document();
document.set(xml);
+ String before = document.get();
+
TextEdit edit = tf.getEdit();
if (edit instanceof MultiTextEdit) {
MultiTextEdit edits = (MultiTextEdit) edit;
@@ -134,6 +141,17 @@ public class RefactoringTest extends AdtProjectTest {
}
String actual = document.get();
+
+ if (createDiffs) {
+ // Use a diff as the golden file instead of the after
+ actual = getDiff(before, actual);
+ if (goldenName.endsWith(DOT_XML)) {
+ goldenName = goldenName.substring(0,
+ goldenName.length() - DOT_XML.length())
+ + ".diff";
+ }
+ }
+
assertEqualsGolden(goldenName, actual);
} else {
System.out.println("Ignoring non-textfilechange in refactoring result");
@@ -240,7 +258,7 @@ public class RefactoringTest extends AdtProjectTest {
UiViewElementNode model = createModel(null, element);
ViewInfo info = createInfos(model, relativePath);
- CanvasViewInfo rootView = CanvasViewInfo.create(info).getFirst();
+ CanvasViewInfo rootView = CanvasViewInfo.create(info, true /* layoutlib5 */).getFirst();
TestLayoutEditor layoutEditor = new TestLayoutEditor(file, structuredDocument, null);
TestContext testInfo = createTestContext();
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract1b.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract1b.diff
new file mode 100644
index 0000000..d734ccf
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract1b.diff
@@ -0,0 +1,3 @@
+< <Button android:text="Button"
+---
+> <Button style="@style/newstyle" android:text="Button"
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract1c.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract1c.diff
new file mode 100644
index 0000000..2ebc91d
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract1c.diff
@@ -0,0 +1,4 @@
+ android:layout_width="wrap_content" android:layout_height="fill_parent"
+< android:textColor="#FF00FF" android:textSize="20pt"
+ </FrameLayout>
+---
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract1d.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract1d.diff
new file mode 100644
index 0000000..ec560b3
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract1d.diff
@@ -0,0 +1,6 @@
+< <Button android:text="Button"
+< android:layout_width="wrap_content" android:layout_height="fill_parent"
+< android:textColor="#FF00FF" android:textSize="20pt"
+---
+> <Button style="@style/newstyle" android:text="Button"
+> android:layout_width="wrap_content" android:layout_height="fill_parent"
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract2.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract2.diff
new file mode 100644
index 0000000..ec560b3
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract2.diff
@@ -0,0 +1,6 @@
+< <Button android:text="Button"
+< android:layout_width="wrap_content" android:layout_height="fill_parent"
+< android:textColor="#FF00FF" android:textSize="20pt"
+---
+> <Button style="@style/newstyle" android:text="Button"
+> android:layout_width="wrap_content" android:layout_height="fill_parent"
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract3.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract3.diff
new file mode 100644
index 0000000..f7fd22b
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract3.diff
@@ -0,0 +1,15 @@
+< <Button android:text="Button"
+< android:layout_width="wrap_content" android:layout_height="wrap_content"
+< android:textColor="#FF0000" android:textSize="20pt"
+< android:id="@+id/button1" android:layout_alignParentBottom="true"></Button>
+< <Button android:text="Button"
+< android:layout_width="wrap_content" android:layout_height="fill_parent"
+< android:textColor="#FF00FF" android:textSize="20pt"
+< android:id="@+id/button2" android:layout_alignParentBottom="true"></Button>
+---
+> <Button style="@style/newstyle" android:text="Button"
+> android:layout_width="wrap_content" android:layout_height="wrap_content"
+> android:id="@+id/button1" android:layout_alignParentBottom="true"></Button>
+> <Button style="@style/newstyle" android:text="Button"
+> android:layout_width="wrap_content" android:layout_height="fill_parent"
+> android:textColor="#FF00FF" android:id="@+id/button2" android:layout_alignParentBottom="true"></Button>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract4.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract4.diff
new file mode 100644
index 0000000..a8e2af4
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract4.diff
@@ -0,0 +1,7 @@
+< <Button android:text="Button"
+< android:layout_width="wrap_content" android:layout_height="fill_parent"
+< android:textColor="#FF00FF" android:textSize="20pt"
+---
+> <Button style="@style/newstyle" android:text="Button"
+> android:layout_width="wrap_content" android:layout_height="fill_parent"
+> android:textSize="20pt"
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract5.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract5.diff
new file mode 100644
index 0000000..bcaff2a
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract5.diff
@@ -0,0 +1,8 @@
+< <Button android:text="Button"
+< android:layout_width="wrap_content" android:layout_height="wrap_content"
+< android:textColor="#FF0000" android:textSize="20pt"
+< android:id="@+id/button1" android:layout_alignParentBottom="true"></Button>
+---
+> <Button style="@style/newstyle" android:text="Button"
+> android:layout_width="wrap_content" android:layout_height="wrap_content"
+> android:textColor="#FF0000" android:id="@+id/button1" android:layout_alignParentBottom="true"></Button>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract6.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract6.diff
new file mode 100644
index 0000000..1db5e38
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract6.diff
@@ -0,0 +1,6 @@
+< <Button android:text="Button"
+< android:layout_width="wrap_content" android:layout_height="wrap_content"
+< android:textColor="#FF0000" android:textSize="20pt"
+< android:id="@+id/button1" android:layout_alignParentBottom="true"></Button>
+---
+> <Button style="@style/newstyle" android:id="@+id/button1" ></Button>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract8.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract8.diff
new file mode 100644
index 0000000..2ebc91d
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract8.diff
@@ -0,0 +1,4 @@
+ android:layout_width="wrap_content" android:layout_height="fill_parent"
+< android:textColor="#FF00FF" android:textSize="20pt"
+ </FrameLayout>
+---
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1.info b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1.info
new file mode 100644
index 0000000..69f7739
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1.info
@@ -0,0 +1,3 @@
+android.widget.LinearLayout [0,36,140,320] <LinearLayout>
+ android.widget.Button [0,0,140,62] <Button>
+ android.widget.Button [0,62,140,284] <Button>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1.xml
new file mode 100644
index 0000000..64c49b2
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1.xml
@@ -0,0 +1,11 @@
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content" android:layout_height="match_parent">
+ <Button android:text="Button"
+ android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:textColor="#FF0000" android:textSize="20pt"
+ android:id="@+id/button1" android:layout_alignParentBottom="true"></Button>
+ <Button android:text="Button"
+ android:layout_width="wrap_content" android:layout_height="fill_parent"
+ android:textColor="#FF00FF" android:textSize="20pt"
+ android:id="@+id/button2" android:layout_alignParentBottom="true"></Button>
+</FrameLayout>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle2-expected-extract7.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle2-expected-extract7.diff
new file mode 100644
index 0000000..84c2ad7
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle2-expected-extract7.diff
@@ -0,0 +1,13 @@
+< <Button foo:text="Button"
+< foo:layout_width="wrap_content" foo:layout_height="wrap_content"
+< foo:textColor="#FF0000" foo:textSize="20pt"
+< foo:id="@+id/button1" foo:layout_alignParentBottom="true"></Button>
+< <Button foo:text="Button"
+< foo:layout_width="wrap_content" foo:layout_height="fill_parent"
+< foo:textColor="#00FF00" foo:textSize="20pt"
+---
+> <Button style="@style/newstyle" foo:text="Button"
+> foo:layout_width="wrap_content" foo:layout_height="wrap_content"
+> foo:textColor="#FF0000" foo:id="@+id/button1" foo:layout_alignParentBottom="true"></Button>
+> <Button style="@style/newstyle" foo:text="Button"
+> foo:layout_width="wrap_content" foo:layout_height="fill_parent"
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle2.info b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle2.info
new file mode 100644
index 0000000..69f7739
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle2.info
@@ -0,0 +1,3 @@
+android.widget.LinearLayout [0,36,140,320] <LinearLayout>
+ android.widget.Button [0,0,140,62] <Button>
+ android.widget.Button [0,62,140,284] <Button>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle2.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle2.xml
new file mode 100644
index 0000000..3cb966f
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle2.xml
@@ -0,0 +1,11 @@
+<LinearLayout xmlns:foo="http://schemas.android.com/apk/res/android"
+ foo:layout_width="wrap_content" foo:layout_height="match_parent" foo:orientation="vertical">
+ <Button foo:text="Button"
+ foo:layout_width="wrap_content" foo:layout_height="wrap_content"
+ foo:textColor="#FF0000" foo:textSize="20pt"
+ foo:id="@+id/button1" foo:layout_alignParentBottom="true"></Button>
+ <Button foo:text="Button"
+ foo:layout_width="wrap_content" foo:layout_height="fill_parent"
+ foo:textColor="#00FF00" foo:textSize="20pt"
+ foo:id="@+id/button2" foo:layout_alignParentBottom="true"></Button>
+</LinearLayout>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigationstyles-expected-extract2.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigationstyles-expected-extract2.diff
new file mode 100644
index 0000000..141180b
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigationstyles-expected-extract2.diff
@@ -0,0 +1,6 @@
+---
+ </style>
+> <style name="newstyle">
+> <item name="android:textColor">#FF00FF</item>
+> <item name="android:textSize">20pt</item>
+ </resources>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles-expected-extract1.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles-expected-extract1.diff
new file mode 100644
index 0000000..d83eb49
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles-expected-extract1.diff
@@ -0,0 +1,9 @@
+<
+---
+> <?xml version="1.0" encoding="utf-8"?>
+> <resources xmlns:android="http://schemas.android.com/apk/res/android">
+> <style name="newstyle">
+> <item name="android:textColor">#FF00FF</item>
+> <item name="android:textSize">20pt</item>
+> </style>
+> </resources>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles2-expected-extract1b.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles2-expected-extract1b.diff
new file mode 100644
index 0000000..d83eb49
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles2-expected-extract1b.diff
@@ -0,0 +1,9 @@
+<
+---
+> <?xml version="1.0" encoding="utf-8"?>
+> <resources xmlns:android="http://schemas.android.com/apk/res/android">
+> <style name="newstyle">
+> <item name="android:textColor">#FF00FF</item>
+> <item name="android:textSize">20pt</item>
+> </style>
+> </resources>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles3-expected-extract1c.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles3-expected-extract1c.diff
new file mode 100644
index 0000000..d83eb49
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles3-expected-extract1c.diff
@@ -0,0 +1,9 @@
+<
+---
+> <?xml version="1.0" encoding="utf-8"?>
+> <resources xmlns:android="http://schemas.android.com/apk/res/android">
+> <style name="newstyle">
+> <item name="android:textColor">#FF00FF</item>
+> <item name="android:textSize">20pt</item>
+> </style>
+> </resources>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles3-expected-extract8.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles3-expected-extract8.diff
new file mode 100644
index 0000000..3b4d930
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles3-expected-extract8.diff
@@ -0,0 +1,9 @@
+<
+---
+> <?xml version="1.0" encoding="utf-8"?>
+> <resources xmlns:android="http://schemas.android.com/apk/res/android">
+> <style name="newstyle" parent="android:Widget.Button">
+> <item name="android:textColor">#FF00FF</item>
+> <item name="android:textSize">20pt</item>
+> </style>
+> </resources>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles4-expected-extract1d.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles4-expected-extract1d.diff
new file mode 100644
index 0000000..d83eb49
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles4-expected-extract1d.diff
@@ -0,0 +1,9 @@
+<
+---
+> <?xml version="1.0" encoding="utf-8"?>
+> <resources xmlns:android="http://schemas.android.com/apk/res/android">
+> <style name="newstyle">
+> <item name="android:textColor">#FF00FF</item>
+> <item name="android:textSize">20pt</item>
+> </style>
+> </resources>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles4-expected-extract3.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles4-expected-extract3.diff
new file mode 100644
index 0000000..0685d94
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles4-expected-extract3.diff
@@ -0,0 +1,9 @@
+<
+---
+> <?xml version="1.0" encoding="utf-8"?>
+> <resources xmlns:android="http://schemas.android.com/apk/res/android">
+> <style name="newstyle">
+> <item name="android:textColor">#FF0000</item>
+> <item name="android:textSize">20pt</item>
+> </style>
+> </resources>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles5-expected-extract4.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles5-expected-extract4.diff
new file mode 100644
index 0000000..f052485
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles5-expected-extract4.diff
@@ -0,0 +1,8 @@
+<
+---
+> <?xml version="1.0" encoding="utf-8"?>
+> <resources xmlns:android="http://schemas.android.com/apk/res/android">
+> <style name="newstyle">
+> <item name="android:textColor">#FF00FF</item>
+> </style>
+> </resources>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles6-expected-extract5.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles6-expected-extract5.diff
new file mode 100644
index 0000000..ce1d4aa
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles6-expected-extract5.diff
@@ -0,0 +1,8 @@
+<
+---
+> <?xml version="1.0" encoding="utf-8"?>
+> <resources xmlns:android="http://schemas.android.com/apk/res/android">
+> <style name="newstyle">
+> <item name="android:textSize">20pt</item>
+> </style>
+> </resources>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles7-expected-extract6.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles7-expected-extract6.diff
new file mode 100644
index 0000000..51f0812
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles7-expected-extract6.diff
@@ -0,0 +1,13 @@
+<
+---
+> <?xml version="1.0" encoding="utf-8"?>
+> <resources xmlns:android="http://schemas.android.com/apk/res/android">
+> <style name="newstyle">
+> <item name="android:layout_alignParentBottom">true</item>
+> <item name="android:layout_height">wrap_content</item>
+> <item name="android:layout_width">wrap_content</item>
+> <item name="android:text">Button</item>
+> <item name="android:textColor">#FF0000</item>
+> <item name="android:textSize">20pt</item>
+> </style>
+> </resources>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles8-expected-extract7.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles8-expected-extract7.diff
new file mode 100644
index 0000000..8f7ad98
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles8-expected-extract7.diff
@@ -0,0 +1,9 @@
+<
+---
+> <?xml version="1.0" encoding="utf-8"?>
+> <resources xmlns:android="http://schemas.android.com/apk/res/android">
+> <style name="newstyle">
+> <item name="android:textColor">#00FF00</item>
+> <item name="android:textSize">20pt</item>
+> </style>
+> </resources>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-assistant1.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-assistant1.txt
index 853dbaa..457239f 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-assistant1.txt
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-assistant1.txt
@@ -1,2 +1,3 @@
Quick assistant in sample1a.xml for <Button android:text="Fir^stButton":
Extract Android String : Initiates the given refactoring operation
+Extract Style : Initiates the given refactoring operation
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-assistant2.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-assistant2.txt
index dfa9d15..95187d3 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-assistant2.txt
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-assistant2.txt
@@ -2,4 +2,5 @@ Quick assistant in sample1a.xml for <Bu^tton android:text:
Wrap in Container : Initiates the given refactoring operation
Change Widget Type : Initiates the given refactoring operation
Change Layout : Initiates the given refactoring operation
+Extract Style : Initiates the given refactoring operation
Extract as Include : Initiates the given refactoring operation
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-assistant3.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-assistant3.txt
index 55bbbad..8123be4 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-assistant3.txt
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-assistant3.txt
@@ -1,2 +1,2 @@
Quick assistant in sample1a.xml for <Button andr^oid:text="FirstButton":
-None found.
+Extract Style : Initiates the given refactoring operation
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfoTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfoTest.java
index 494346a..d336b35 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfoTest.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfoTest.java
@@ -18,7 +18,9 @@ package com.android.ide.eclipse.adt.internal.editors.layout.gle2;
import com.android.ide.common.rendering.api.Capability;
import com.android.ide.common.rendering.api.MergeCookie;
+import com.android.ide.common.rendering.api.SessionParams;
import com.android.ide.common.rendering.api.ViewInfo;
+import com.android.ide.common.rendering.api.SessionParams.AdapterItemReference;
import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
@@ -65,6 +67,15 @@ public class CanvasViewInfoTest extends TestCase {
}
public void testNormalCreate() throws Exception {
+ normal(true);
+ }
+
+ public void testNormalCreateLayoutLib5() throws Exception {
+ normal(false);
+ }
+
+ private void normal(boolean layoutlib5) {
+
// Normal view hierarchy, no null keys anywhere
UiViewElementNode rootNode = createNode("android.widget.LinearLayout", true);
@@ -75,7 +86,7 @@ public class CanvasViewInfoTest extends TestCase {
ViewInfo child2 = new ViewInfo("Button", child2Node, 0, 20, 70, 25);
root.setChildren(Arrays.asList(child1, child2));
- CanvasViewInfo rootView = CanvasViewInfo.create(root).getFirst();
+ CanvasViewInfo rootView = CanvasViewInfo.create(root, layoutlib5).getFirst();
assertNotNull(rootView);
assertEquals("LinearLayout", rootView.getName());
assertEquals(new Rectangle(10, 10, 89, 89), rootView.getAbsRect());
@@ -100,6 +111,15 @@ public class CanvasViewInfoTest extends TestCase {
}
public void testShowIn() throws Exception {
+ showIn(false);
+ }
+
+ public void testShowInLayoutLib5() throws Exception {
+ showIn(true);
+ }
+
+ public void showIn(boolean layoutlib5) throws Exception {
+
// Test rendering of "Show Included In" (included content rendered
// within an outer content that has null keys)
@@ -112,7 +132,7 @@ public class CanvasViewInfoTest extends TestCase {
ViewInfo child21 = new ViewInfo("RadioButton", child21Node, 0, 20, 70, 25);
child2.setChildren(Arrays.asList(child21));
- CanvasViewInfo rootView = CanvasViewInfo.create(root).getFirst();
+ CanvasViewInfo rootView = CanvasViewInfo.create(root, layoutlib5).getFirst();
assertNotNull(rootView);
assertEquals("LinearLayout", rootView.getName());
assertEquals(new Rectangle(10, 10, 89, 89), rootView.getAbsRect());
@@ -137,6 +157,8 @@ public class CanvasViewInfoTest extends TestCase {
}
public void testIncludeTag() throws Exception {
+ boolean layoutlib5 = true;
+
// Test rendering of included views on layoutlib 5+ (e.g. has <include> tag)
UiViewElementNode rootNode = createNode("android.widget.LinearLayout", true);
@@ -149,7 +171,7 @@ public class CanvasViewInfoTest extends TestCase {
ViewInfo child21 = new ViewInfo("RadioButton", null, 0, 20, 70, 25);
child2.setChildren(Arrays.asList(child21));
- CanvasViewInfo rootView = CanvasViewInfo.create(root).getFirst();
+ CanvasViewInfo rootView = CanvasViewInfo.create(root, layoutlib5).getFirst();
assertNotNull(rootView);
assertEquals("LinearLayout", rootView.getName());
assertEquals(new Rectangle(10, 10, 89, 89), rootView.getAbsRect());
@@ -176,9 +198,10 @@ public class CanvasViewInfoTest extends TestCase {
}
public void testNoIncludeTag() throws Exception {
+ boolean layoutlib5 = false;
+
// Test rendering of included views on layoutlib 4- (e.g. no <include> tag cookie
- // in
- // view info)
+ // in view info)
UiViewElementNode rootNode = createNode("android.widget.LinearLayout", true);
ViewInfo root = new ViewInfo("LinearLayout", rootNode, 10, 10, 100, 100);
@@ -190,7 +213,7 @@ public class CanvasViewInfoTest extends TestCase {
ViewInfo child21 = new ViewInfo("RadioButton", null, 0, 20, 70, 25);
child2.setChildren(Arrays.asList(child21));
- CanvasViewInfo rootView = CanvasViewInfo.create(root).getFirst();
+ CanvasViewInfo rootView = CanvasViewInfo.create(root, layoutlib5).getFirst();
assertNotNull(rootView);
assertEquals("LinearLayout", rootView.getName());
assertEquals(new Rectangle(10, 10, 89, 89), rootView.getAbsRect());
@@ -217,6 +240,8 @@ public class CanvasViewInfoTest extends TestCase {
}
public void testMergeMatching() throws Exception {
+ boolean layoutlib5 = false;
+
// Test rendering of MULTIPLE included views or when there is no simple match
// between view info and ui element node children
@@ -232,7 +257,7 @@ public class CanvasViewInfoTest extends TestCase {
ViewInfo child21 = new ViewInfo("RadioButton", null, 0, 20, 70, 25);
child2.setChildren(Arrays.asList(child21));
- CanvasViewInfo rootView = CanvasViewInfo.create(root).getFirst();
+ CanvasViewInfo rootView = CanvasViewInfo.create(root, layoutlib5).getFirst();
assertNotNull(rootView);
assertEquals("LinearLayout", rootView.getName());
assertEquals(new Rectangle(10, 10, 89, 89), rootView.getAbsRect());
@@ -272,6 +297,8 @@ public class CanvasViewInfoTest extends TestCase {
}
public void testMerge() throws Exception {
+ boolean layoutlib5 = false;
+
// Test rendering of MULTIPLE included views or when there is no simple match
// between view info and ui element node children
@@ -286,7 +313,7 @@ public class CanvasViewInfoTest extends TestCase {
ViewInfo child21 = new ViewInfo("RadioButton", null, 0, 20, 70, 25);
child2.setChildren(Arrays.asList(child21));
- CanvasViewInfo rootView = CanvasViewInfo.create(root).getFirst();
+ CanvasViewInfo rootView = CanvasViewInfo.create(root, layoutlib5).getFirst();
assertNotNull(rootView);
assertEquals("LinearLayout", rootView.getName());
assertEquals(new Rectangle(10, 10, 89, 89), rootView.getAbsRect());
@@ -313,6 +340,8 @@ public class CanvasViewInfoTest extends TestCase {
}
public void testInsertMerge() throws Exception {
+ boolean layoutlib5 = false;
+
// Test rendering of MULTIPLE included views or when there is no simple match
// between view info and ui element node children
@@ -320,7 +349,7 @@ public class CanvasViewInfoTest extends TestCase {
UiViewElementNode rootNode = createNode(mergeNode, "android.widget.Button", false);
ViewInfo root = new ViewInfo("Button", rootNode, 10, 10, 100, 100);
- CanvasViewInfo rootView = CanvasViewInfo.create(root).getFirst();
+ CanvasViewInfo rootView = CanvasViewInfo.create(root, layoutlib5).getFirst();
assertNotNull(rootView);
assertEquals("merge", rootView.getName());
assertSame(rootView.getUiViewNode(), mergeNode);
@@ -340,6 +369,8 @@ public class CanvasViewInfoTest extends TestCase {
}
public void testUnmatchedMissing() throws Exception {
+ boolean layoutlib5 = false;
+
UiViewElementNode rootNode = createNode("android.widget.LinearLayout", true);
ViewInfo root = new ViewInfo("LinearLayout", rootNode, 0, 0, 100, 100);
List<ViewInfo> children = new ArrayList<ViewInfo>();
@@ -387,7 +418,7 @@ public class CanvasViewInfoTest extends TestCase {
}
root.setChildren(children);
- CanvasViewInfo rootView = CanvasViewInfo.create(root).getFirst();
+ CanvasViewInfo rootView = CanvasViewInfo.create(root, layoutlib5).getFirst();
assertNotNull(rootView);
// dump(root, 0);
@@ -412,6 +443,8 @@ public class CanvasViewInfoTest extends TestCase {
}
public void testMergeCookies() throws Exception {
+ boolean layoutlib5 = true;
+
UiViewElementNode rootNode = createNode("android.widget.LinearLayout", true);
ViewInfo root = new ViewInfo("LinearLayout", rootNode, 0, 0, 100, 100);
@@ -431,7 +464,7 @@ public class CanvasViewInfoTest extends TestCase {
}
root.setChildren(children);
- CanvasViewInfo rootView = CanvasViewInfo.create(root).getFirst();
+ CanvasViewInfo rootView = CanvasViewInfo.create(root, layoutlib5).getFirst();
assertNotNull(rootView);
assertEquals("LinearLayout", rootView.getName());
@@ -446,6 +479,8 @@ public class CanvasViewInfoTest extends TestCase {
}
public void testMergeCookies2() throws Exception {
+ boolean layoutlib5 = true;
+
UiViewElementNode rootNode = createNode("android.widget.LinearLayout", true);
ViewInfo root = new ViewInfo("LinearLayout", rootNode, 0, 0, 100, 100);
@@ -460,12 +495,13 @@ public class CanvasViewInfoTest extends TestCase {
ArrayList<ViewInfo> children = new ArrayList<ViewInfo>();
for (int i = 0; i < 10; i++) {
Object cookie = (i % 2) == 0 ? cookie1 : cookie2;
- ViewInfo childView = new ViewInfo("childView" + i, cookie, 0, i * 20, 50, (i + 1) * 20);
+ ViewInfo childView = new ViewInfo("childView" + i, cookie, 0, i * 20, 50,
+ (i + 1) * 20);
children.add(childView);
}
root.setChildren(children);
- Pair<CanvasViewInfo, List<Rectangle>> result = CanvasViewInfo.create(root);
+ Pair<CanvasViewInfo, List<Rectangle>> result = CanvasViewInfo.create(root, layoutlib5);
CanvasViewInfo rootView = result.getFirst();
List<Rectangle> bounds = result.getSecond();
assertNull(bounds);
@@ -495,6 +531,8 @@ public class CanvasViewInfoTest extends TestCase {
}
public void testIncludeBounds() throws Exception {
+ boolean layoutlib5 = true;
+
UiViewElementNode rootNode = createNode("android.widget.LinearLayout", true);
ViewInfo root = new ViewInfo("included", null, 0, 0, 100, 100);
@@ -509,12 +547,13 @@ public class CanvasViewInfoTest extends TestCase {
ArrayList<ViewInfo> children = new ArrayList<ViewInfo>();
for (int i = 0; i < 10; i++) {
Object cookie = (i % 2) == 0 ? cookie1 : cookie2;
- ViewInfo childView = new ViewInfo("childView" + i, cookie, 0, i * 20, 50, (i + 1) * 20);
+ ViewInfo childView = new ViewInfo("childView" + i, cookie, 0, i * 20, 50,
+ (i + 1) * 20);
children.add(childView);
}
root.setChildren(children);
- Pair<CanvasViewInfo, List<Rectangle>> result = CanvasViewInfo.create(root);
+ Pair<CanvasViewInfo, List<Rectangle>> result = CanvasViewInfo.create(root, layoutlib5);
CanvasViewInfo rootView = result.getFirst();
List<Rectangle> bounds = result.getSecond();
assertNotNull(rootView);
@@ -548,21 +587,27 @@ public class CanvasViewInfoTest extends TestCase {
}
public void testIncludeBounds2() throws Exception {
+ includeBounds2(false);
+ }
+
+ public void testIncludeBounds2LayoutLib5() throws Exception {
+ includeBounds2(true);
+ }
+
+ public void includeBounds2(boolean layoutlib5) throws Exception {
+
UiViewElementNode rootNode = createNode("android.widget.LinearLayout", true);
ViewInfo root = new ViewInfo("included", null, 0, 0, 100, 100);
UiViewElementNode node1 = createNode(rootNode, "childNode1", false);
UiViewElementNode node2 = createNode(rootNode, "childNode2", false);
- // Sets alternating merge cookies and checks whether the node sibling lists are
- // okay and merged correctly
-
ViewInfo childView1 = new ViewInfo("childView1", node1, 0, 20, 50, 40);
ViewInfo childView2 = new ViewInfo("childView2", node2, 0, 40, 50, 60);
root.setChildren(Arrays.asList(childView1, childView2));
- Pair<CanvasViewInfo, List<Rectangle>> result = CanvasViewInfo.create(root);
+ Pair<CanvasViewInfo, List<Rectangle>> result = CanvasViewInfo.create(root, layoutlib5);
CanvasViewInfo rootView = result.getFirst();
List<Rectangle> bounds = result.getSecond();
assertNotNull(rootView);
@@ -580,6 +625,8 @@ public class CanvasViewInfoTest extends TestCase {
}
public void testGestureOverlayView() throws Exception {
+ boolean layoutlib5 = true;
+
// Test rendering of included views on layoutlib 5+ (e.g. has <include> tag)
UiViewElementNode rootNode = createNode("android.gesture.GestureOverlayView", true);
@@ -590,7 +637,7 @@ public class CanvasViewInfoTest extends TestCase {
root.setChildren(Collections.singletonList(child));
ViewInfo grandChild = new ViewInfo("Button", grandChildNode, 0, 20, 70, 25);
child.setChildren(Collections.singletonList(grandChild));
- CanvasViewInfo rootView = CanvasViewInfo.create(root).getFirst();
+ CanvasViewInfo rootView = CanvasViewInfo.create(root, layoutlib5).getFirst();
assertNotNull(rootView);
assertEquals("GestureOverlayView", rootView.getName());
@@ -611,6 +658,46 @@ public class CanvasViewInfoTest extends TestCase {
assertFalse(grandChildView.isRoot());
}
+ public void testListView() throws Exception {
+ // For ListViews we get AdapterItemReferences as cookies. Ensure that this
+ // works properly.
+ //
+ // android.widget.FrameLayout [0,50,320,480] <FrameLayout>
+ // android.widget.ListView [0,0,320,430] <ListView>
+ // android.widget.LinearLayout [0,0,320,17] SessionParams$AdapterItemReference
+ // android.widget.TextView [0,0,73,17]
+ // android.widget.LinearLayout [0,18,320,35] SessionParams$AdapterItemReference
+ // android.widget.TextView [0,0,73,17]
+ // android.widget.LinearLayout [0,36,320,53] SessionParams$AdapterItemReference
+ // android.widget.TextView [0,0,73,17]
+ // ...
+
+ UiViewElementNode rootNode = createNode("FrameLayout", true);
+ UiViewElementNode childNode = createNode(rootNode, "ListView", false);
+ /*UiViewElementNode grandChildNode =*/ createNode(childNode, "LinearLayout", false);
+ /*UiViewElementNode greatGrandChildNode =*/ createNode(childNode, "TextView", false);
+ AdapterItemReference adapterItem = new SessionParams.AdapterItemReference("foo");
+
+ ViewInfo root = new ViewInfo("FrameLayout", rootNode, 0, 50, 320, 480);
+ ViewInfo child = new ViewInfo("ListView", childNode, 0, 0, 320, 430);
+ root.setChildren(Collections.singletonList(child));
+ ViewInfo grandChild = new ViewInfo("LinearLayout", adapterItem, 0, 0, 320, 17);
+ child.setChildren(Collections.singletonList(grandChild));
+ ViewInfo greatGrandChild = new ViewInfo("Button", null, 0, 0, 73, 17);
+ grandChild.setChildren(Collections.singletonList(greatGrandChild));
+ CanvasViewInfo rootView = CanvasViewInfo.create(root, true /*layoutlib5*/).getFirst();
+ assertNotNull(rootView);
+
+ assertEquals("FrameLayout", rootView.getName());
+ assertEquals(1, rootView.getChildren().size());
+ assertSame(rootNode, rootView.getUiViewNode());
+
+ CanvasViewInfo childView = rootView.getChildren().get(0);
+ assertEquals("ListView", childView.getName());
+ assertEquals(0, childView.getChildren().size());
+ assertSame(childNode, childView.getUiViewNode());
+ }
+
/**
* Dumps out the given {@link ViewInfo} hierarchy to standard out.
* Useful during development.
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManagerTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManagerTest.java
index db18762..b29f9f3 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManagerTest.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManagerTest.java
@@ -53,7 +53,7 @@ public class SelectionManagerTest extends TestCase {
"android.widget.Button", false);
ViewInfo child2 = new ViewInfo("Button2", child2Node, 0, 20, 70, 25);
root.setChildren(Arrays.asList(child1, child2));
- CanvasViewInfo rootView = CanvasViewInfo.create(root).getFirst();
+ CanvasViewInfo rootView = CanvasViewInfo.create(root, true /* layoutlib5 */).getFirst();
assertNotNull(rootView);
manager.selectMultiple(Arrays.asList(rootView, rootView.getChildren().get(0), rootView
@@ -104,7 +104,7 @@ public class SelectionManagerTest extends TestCase {
"android.widget.Button", false);
ViewInfo child2 = new ViewInfo("Button2", child2Node, 0, 20, 70, 25);
root.setChildren(Arrays.asList(child1, child2));
- CanvasViewInfo rootView = CanvasViewInfo.create(root).getFirst();
+ CanvasViewInfo rootView = CanvasViewInfo.create(root, true /* layoutlib5 */).getFirst();
assertNotNull(rootView);
manager.selectMultiple(Arrays.asList(rootView.getChildren().get(0)));
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeFactoryTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeFactoryTest.java
index 277089f..9f670cc 100755
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeFactoryTest.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeFactoryTest.java
@@ -48,7 +48,7 @@ public class NodeFactoryTest extends TestCase {
ViewElementDescriptor ved = new ViewElementDescriptor("xml", "com.example.MyJavaClass");
UiViewElementNode uiv = new UiViewElementNode(ved);
ViewInfo lvi = new ViewInfo("name", uiv, 10, 12, 110, 120);
- CanvasViewInfo cvi = CanvasViewInfo.create(lvi).getFirst();
+ CanvasViewInfo cvi = CanvasViewInfo.create(lvi, true /* layoutlib5 */).getFirst();
// Create a NodeProxy.
NodeProxy proxy = m.create(cvi);
@@ -95,7 +95,7 @@ public class NodeFactoryTest extends TestCase {
ViewElementDescriptor ved = new ViewElementDescriptor("xml", "com.example.MyJavaClass");
UiViewElementNode uiv = new UiViewElementNode(ved);
ViewInfo lvi = new ViewInfo("name", uiv, 10, 12, 110, 120);
- CanvasViewInfo cvi = CanvasViewInfo.create(lvi).getFirst();
+ CanvasViewInfo cvi = CanvasViewInfo.create(lvi, true /* layoutlib5 */).getFirst();
// NodeProxies are cached. Creating the same one twice returns the same proxy.
NodeProxy proxy1 = m.create(cvi);
@@ -107,7 +107,7 @@ public class NodeFactoryTest extends TestCase {
ViewElementDescriptor ved = new ViewElementDescriptor("xml", "com.example.MyJavaClass");
UiViewElementNode uiv = new UiViewElementNode(ved);
ViewInfo lvi = new ViewInfo("name", uiv, 10, 12, 110, 120);
- CanvasViewInfo cvi = CanvasViewInfo.create(lvi).getFirst();
+ CanvasViewInfo cvi = CanvasViewInfo.create(lvi, true /* layoutlib5 */).getFirst();
// NodeProxies are cached. Creating the same one twice returns the same proxy.
NodeProxy proxy1 = m.create(cvi);
diff --git a/ide_common/src/com/android/ide/common/resources/ResourceResolver.java b/ide_common/src/com/android/ide/common/resources/ResourceResolver.java
index e3c93b7..216d694 100644
--- a/ide_common/src/com/android/ide/common/resources/ResourceResolver.java
+++ b/ide_common/src/com/android/ide/common/resources/ResourceResolver.java
@@ -28,7 +28,7 @@ import java.util.Map;
public class ResourceResolver extends RenderResources {
- private final static String REFERENCE_STYLE = ResourceType.STYLE.getName() + "/";
+ public final static String REFERENCE_STYLE = ResourceType.STYLE.getName() + "/";
public final static String PREFIX_ANDROID_RESOURCE_REF = "@android:";
public final static String PREFIX_RESOURCE_REF = "@";
public final static String PREFIX_ANDROID_THEME_REF = "?android:";
diff --git a/layoutlib_api/src/com/android/ide/common/rendering/api/Capability.java b/layoutlib_api/src/com/android/ide/common/rendering/api/Capability.java
index 2f79038..6620571 100644
--- a/layoutlib_api/src/com/android/ide/common/rendering/api/Capability.java
+++ b/layoutlib_api/src/com/android/ide/common/rendering/api/Capability.java
@@ -24,7 +24,7 @@ public enum Capability {
/** Ability to render at full size, as required by the layout, and unbound by the screen */
UNBOUND_RENDERING,
/** Ability to override the background of the rendering with transparency using
- * {@link SessionParams#setCustomBackgroundColor(int)} */
+ * {@link SessionParams#setOverrideBgColor(int)} */
CUSTOM_BACKGROUND_COLOR,
/** Ability to call {@link RenderSession#render()} and {@link RenderSession#render(long)}. */
RENDER,
diff --git a/monkeyrunner/src/com/android/monkeyrunner/adb/AdbMonkeyDevice.java b/monkeyrunner/src/com/android/monkeyrunner/adb/AdbMonkeyDevice.java
index befec28..41c4d02 100644
--- a/monkeyrunner/src/com/android/monkeyrunner/adb/AdbMonkeyDevice.java
+++ b/monkeyrunner/src/com/android/monkeyrunner/adb/AdbMonkeyDevice.java
@@ -516,6 +516,7 @@ public class AdbMonkeyDevice extends MonkeyDevice {
public void start(Point point) {
try {
manager.touchDown(point.getX(), point.getY());
+ manager.touchMove(point.getX(), point.getY());
} catch (IOException e) {
LOG.log(Level.SEVERE, "Error sending drag start event", e);
}
@@ -529,6 +530,7 @@ public class AdbMonkeyDevice extends MonkeyDevice {
public void end(Point point) {
try {
+ manager.touchMove(point.getX(), point.getY());
manager.touchUp(point.getX(), point.getY());
} catch (IOException e) {
LOG.log(Level.SEVERE, "Error sending drag end event", e);