aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTor Norbye <tnorbye@google.com>2012-10-15 13:03:40 -0700
committerTor Norbye <tnorbye@google.com>2012-10-15 17:46:37 -0700
commita1e7aa7cc973adaba4f31673c897724eb8d9f399 (patch)
treea882deff58ea4fd638421369053660fbc8d21d44
parent1e6c45d826478a9621f433c28cce38786b890f52 (diff)
downloadsdk-a1e7aa7cc973adaba4f31673c897724eb8d9f399.zip
sdk-a1e7aa7cc973adaba4f31673c897724eb8d9f399.tar.gz
sdk-a1e7aa7cc973adaba4f31673c897724eb8d9f399.tar.bz2
Misc improvements to the multi-configuration editing
* Fix bug in switching to preview configurations * Don't draw drop shadows for thumbnails in Dialog themes * Move the preview title labels to sit above each preview thumbnail * Make error thumbnails include rendering error messages (and wrap if necessary), plus tweak appearance * Make switch animation show rectangles animating in both directions Change-Id: I0995617fa277b48419a88c5203abf5b1d49af711
-rw-r--r--common/src/com/android/utils/SdkUtils.java71
-rw-r--r--common/tests/src/com/android/utils/SdkUtilsTest.java49
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ComplementingConfiguration.java24
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/Configuration.java6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationChooser.java7
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/NestedConfiguration.java82
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderPreview.java170
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderPreviewManager.java125
-rw-r--r--lint/cli/src/com/android/tools/lint/Main.java45
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/MainTest.java46
11 files changed, 444 insertions, 185 deletions
diff --git a/common/src/com/android/utils/SdkUtils.java b/common/src/com/android/utils/SdkUtils.java
index 18d3ccd..160f95d 100644
--- a/common/src/com/android/utils/SdkUtils.java
+++ b/common/src/com/android/utils/SdkUtils.java
@@ -17,7 +17,9 @@
package com.android.utils;
import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+/** Miscellaneous utilities used by the Android SDK tools */
public class SdkUtils {
/**
* Returns true if the given string ends with the given suffix, using a
@@ -111,7 +113,12 @@ public class SdkUtils {
return sb.toString();
}
- /** Returns true if the given string has an upper case character. */
+ /**
+ * Returns true if the given string has an upper case character.
+ *
+ * @param s the string to check
+ * @return true if it contains uppercase characters
+ */
public static boolean hasUpperCaseCharacter(String s) {
for (int i = 0; i < s.length(); i++) {
if (Character.isUpperCase(s.charAt(i))) {
@@ -144,4 +151,66 @@ public class SdkUtils {
return sLineSeparator;
}
+
+ /**
+ * Wraps the given text at the given line width, with an optional hanging
+ * indent.
+ *
+ * @param text the text to be wrapped
+ * @param lineWidth the number of characters to wrap the text to
+ * @param hangingIndent the hanging indent (to be used for the second and
+ * subsequent lines in each paragraph, or null if not known
+ * @return the string, wrapped
+ */
+ @NonNull
+ public static String wrap(
+ @NonNull String text,
+ int lineWidth,
+ @Nullable String hangingIndent) {
+ if (hangingIndent == null) {
+ hangingIndent = "";
+ }
+ int explanationLength = text.length();
+ StringBuilder sb = new StringBuilder(explanationLength * 2);
+ int index = 0;
+
+ while (index < explanationLength) {
+ int lineEnd = text.indexOf('\n', index);
+ int next;
+
+ if (lineEnd != -1 && (lineEnd - index) < lineWidth) {
+ next = lineEnd + 1;
+ } else {
+ // Line is longer than available width; grab as much as we can
+ lineEnd = Math.min(index + lineWidth, explanationLength);
+ if (lineEnd - index < lineWidth) {
+ next = explanationLength;
+ } else {
+ // then back up to the last space
+ int lastSpace = text.lastIndexOf(' ', lineEnd);
+ if (lastSpace > index) {
+ lineEnd = lastSpace;
+ next = lastSpace + 1;
+ } else {
+ // No space anywhere on the line: it contains something wider than
+ // can fit (like a long URL) so just hard break it
+ next = lineEnd + 1;
+ }
+ }
+ }
+
+ if (sb.length() > 0) {
+ sb.append(hangingIndent);
+ } else {
+ lineWidth -= hangingIndent.length();
+ }
+
+ sb.append(text.substring(index, lineEnd));
+ sb.append('\n');
+ index = next;
+ }
+
+ return sb.toString();
+ }
+
}
diff --git a/common/tests/src/com/android/utils/SdkUtilsTest.java b/common/tests/src/com/android/utils/SdkUtilsTest.java
index 29a4d51..030e1b7 100644
--- a/common/tests/src/com/android/utils/SdkUtilsTest.java
+++ b/common/tests/src/com/android/utils/SdkUtilsTest.java
@@ -18,6 +18,7 @@ package com.android.utils;
import junit.framework.TestCase;
+@SuppressWarnings("javadoc")
public class SdkUtilsTest extends TestCase {
public void testEndsWithIgnoreCase() {
assertTrue(SdkUtils.endsWithIgnoreCase("foo", "foo"));
@@ -81,4 +82,52 @@ public class SdkUtilsTest extends TestCase {
assertEquals("foobar", SdkUtils.stripWhitespace("foo bar"));
assertEquals("foobar", SdkUtils.stripWhitespace(" foo bar \n\t"));
}
+
+ public void testWrap() {
+ String s =
+ "Hardcoding text attributes directly in layout files is bad for several reasons:\n" +
+ "\n" +
+ "* When creating configuration variations (for example for landscape or portrait)" +
+ "you have to repeat the actual text (and keep it up to date when making changes)\n" +
+ "\n" +
+ "* The application cannot be translated to other languages by just adding new " +
+ "translations for existing string resources.";
+ String wrapped = SdkUtils.wrap(s, 70, "");
+ assertEquals(
+ "Hardcoding text attributes directly in layout files is bad for several\n" +
+ "reasons:\n" +
+ "\n" +
+ "* When creating configuration variations (for example for landscape or\n" +
+ "portrait)you have to repeat the actual text (and keep it up to date\n" +
+ "when making changes)\n" +
+ "\n" +
+ "* The application cannot be translated to other languages by just\n" +
+ "adding new translations for existing string resources.\n",
+ wrapped);
+ }
+
+ public void testWrapPrefix() {
+ String s =
+ "Hardcoding text attributes directly in layout files is bad for several reasons:\n" +
+ "\n" +
+ "* When creating configuration variations (for example for landscape or portrait)" +
+ "you have to repeat the actual text (and keep it up to date when making changes)\n" +
+ "\n" +
+ "* The application cannot be translated to other languages by just adding new " +
+ "translations for existing string resources.";
+ String wrapped = SdkUtils.wrap(s, 70, " ");
+ assertEquals(
+ "Hardcoding text attributes directly in layout files is bad for several\n" +
+ " reasons:\n" +
+ " \n" +
+ " * When creating configuration variations (for example for\n" +
+ " landscape or portrait)you have to repeat the actual text (and keep\n" +
+ " it up to date when making changes)\n" +
+ " \n" +
+ " * The application cannot be translated to other languages by just\n" +
+ " adding new translations for existing string resources.\n",
+ wrapped);
+ }
+
+
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ComplementingConfiguration.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ComplementingConfiguration.java
index cce5bc9..66a29c8 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ComplementingConfiguration.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ComplementingConfiguration.java
@@ -87,6 +87,30 @@ public class ComplementingConfiguration extends NestedConfiguration {
}
/**
+ * Creates a new {@linkplain ComplementingConfiguration} that has the same overriding
+ * attributes as the given other {@linkplain ComplementingConfiguration}.
+ *
+ * @param other the configuration to copy overrides from
+ * @param parent the parent to tie the configuration to for inheriting values
+ * @return a new configuration
+ */
+ @NonNull
+ public static ComplementingConfiguration create(
+ @NonNull ComplementingConfiguration other,
+ @NonNull Configuration parent) {
+ ComplementingConfiguration configuration =
+ new ComplementingConfiguration(other.mConfigChooser, parent);
+ configuration.mOverrideLocale = other.mOverrideLocale;
+ configuration.mOverrideTarget = other.mOverrideTarget;
+ configuration.mOverrideDevice = other.mOverrideDevice;
+ configuration.mOverrideDeviceState = other.mOverrideDeviceState;
+ configuration.mOverrideNightMode = other.mOverrideNightMode;
+ configuration.mOverrideUiMode = other.mOverrideUiMode;
+
+ return configuration;
+ }
+
+ /**
* Sets the variation version for this
* {@linkplain ComplementingConfiguration}. There might be multiple
* {@linkplain ComplementingConfiguration} instances inheriting from a
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/Configuration.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/Configuration.java
index b9c7745..1615722 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/Configuration.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/Configuration.java
@@ -50,6 +50,7 @@ import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.devices.Device;
import com.android.sdklib.devices.State;
import com.android.utils.Pair;
+import com.google.common.base.Objects;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
@@ -1063,6 +1064,9 @@ public class Configuration {
@Override
public String toString() {
- return toPersistentString();
+ return Objects.toStringHelper(this.getClass())
+ .add("display", getDisplayName()) //$NON-NLS-1$
+ .add("persistent", toPersistentString()) //$NON-NLS-1$
+ .toString();
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationChooser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationChooser.java
index ce78800..592fa32 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationChooser.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationChooser.java
@@ -19,7 +19,6 @@ package com.android.ide.eclipse.adt.internal.editors.layout.configuration;
import static com.android.SdkConstants.ANDROID_NS_NAME_PREFIX;
import static com.android.SdkConstants.ANDROID_STYLE_RESOURCE_PREFIX;
import static com.android.SdkConstants.ATTR_CONTEXT;
-import static com.android.SdkConstants.FD_RES_LAYOUT;
import static com.android.SdkConstants.PREFIX_RESOURCE_REF;
import static com.android.SdkConstants.RES_QUALIFIER_SEP;
import static com.android.SdkConstants.STYLE_RESOURCE_PREFIX;
@@ -1091,6 +1090,11 @@ public class ConfigurationChooser extends Composite
}
private void selectConfiguration(FolderConfiguration fileConfig) {
+ /* For now, don't show any text in the configuration combo, use just an
+ icon. This has the advantage that the configuration contents don't
+ shift around, so you can for example click back and forth between
+ portrait and landscape without the icon moving under the mouse.
+ If this works well, remove this whole method post ADT 21.
assert isUiThread();
try {
String current = mEditedFile.getParent().getName();
@@ -1107,6 +1111,7 @@ public class ConfigurationChooser extends Composite
} finally {
mDisableUpdates--;
}
+ */
}
/**
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/NestedConfiguration.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/NestedConfiguration.java
index 73d08f8..432abdb 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/NestedConfiguration.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/NestedConfiguration.java
@@ -23,6 +23,7 @@ import com.android.resources.UiMode;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.devices.Device;
import com.android.sdklib.devices.State;
+import com.google.common.base.Objects;
/**
* An {@linkplain NestedConfiguration} is a {@link Configuration} which inherits
@@ -37,7 +38,7 @@ import com.android.sdklib.devices.State;
* be "en", but otherwise inherit everything else.
*/
public class NestedConfiguration extends Configuration {
- protected final Configuration mParent;
+ protected Configuration mParent;
protected boolean mOverrideLocale;
protected boolean mOverrideTarget;
protected boolean mOverrideDevice;
@@ -66,6 +67,61 @@ public class NestedConfiguration extends Configuration {
}
/**
+ * Creates a new {@linkplain NestedConfiguration} that has the same overriding
+ * attributes as the given other {@linkplain NestedConfiguration}, and gets
+ * its values from the given {@linkplain Configuration}.
+ *
+ * @param other the configuration to copy overrides from
+ * @param values the configuration to copy values from
+ * @param parent the parent to tie the configuration to for inheriting values
+ * @return a new configuration
+ */
+ @NonNull
+ public static NestedConfiguration create(
+ @NonNull NestedConfiguration other,
+ @NonNull Configuration values,
+ @NonNull Configuration parent) {
+ NestedConfiguration configuration =
+ new NestedConfiguration(other.mConfigChooser, parent);
+ configuration.mOverrideLocale = other.mOverrideLocale;
+ if (configuration.mOverrideLocale) {
+ configuration.setLocale(values.getLocale(), true);
+ }
+ configuration.mOverrideTarget = other.mOverrideTarget;
+ if (configuration.mOverrideTarget) {
+ configuration.setTarget(values.getTarget(), true);
+ }
+ configuration.mOverrideDevice = other.mOverrideDevice;
+ configuration.mOverrideDeviceState = other.mOverrideDeviceState;
+ if (configuration.mOverrideDevice) {
+ configuration.setDevice(values.getDevice(), true);
+ }
+ if (configuration.mOverrideDeviceState) {
+ configuration.setDeviceState(values.getDeviceState(), true);
+ }
+
+ configuration.mOverrideNightMode = other.mOverrideNightMode;
+ if (configuration.mOverrideNightMode) {
+ configuration.setNightMode(values.getNightMode(), true);
+ }
+ configuration.mOverrideUiMode = other.mOverrideUiMode;
+ if (configuration.mOverrideUiMode) {
+ configuration.setUiMode(values.getUiMode(), true);
+ }
+
+ return configuration;
+ }
+
+ /**
+ * Sets the parent configuration that this configuration is inheriting from.
+ *
+ * @param parent the parent configuration
+ */
+ public void setParent(@NonNull Configuration parent) {
+ mParent = parent;
+ }
+
+ /**
* Creates a new {@linkplain Configuration} which inherits values from the
* given parent {@linkplain Configuration}, possibly overriding some as
* well.
@@ -302,4 +358,28 @@ public class NestedConfiguration extends Configuration {
public Configuration getParent() {
return mParent;
}
+
+ @Override
+ @Nullable
+ public String getActivity() {
+ return mParent.getActivity();
+ }
+
+ @Override
+ public void setActivity(String activity) {
+ super.setActivity(activity);
+ }
+
+ @Override
+ public String toString() {
+ return Objects.toStringHelper(this.getClass())
+ .add("parent", mParent.getDisplayName()) //$NON-NLS-1$
+ .add("display", getDisplayName()) //$NON-NLS-1$
+ .add("overrideLocale", mOverrideLocale) //$NON-NLS-1$
+ .add("overrideTarget", mOverrideTarget) //$NON-NLS-1$
+ .add("overrideDevice", mOverrideDevice) //$NON-NLS-1$
+ .add("overrideDeviceState", mOverrideDeviceState) //$NON-NLS-1$
+ .add("persistent", toPersistentString()) //$NON-NLS-1$
+ .toString();
+ }
} \ No newline at end of file
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 1813dfb..93fdb5e 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
@@ -657,8 +657,6 @@ public class GraphicalEditorPart extends EditorPart
return true;
}
- getCanvasControl().getPreviewManager().configurationChanged(flags);
-
// Before doing the normal process, test for the following case.
// - the editor is being opened (or reset for a new input)
// - the file being opened is not the best match for any possible configuration
@@ -753,6 +751,8 @@ public class GraphicalEditorPart extends EditorPart
reloadPalette();
+ getCanvasControl().getPreviewManager().configurationChanged(flags);
+
return true;
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderPreview.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderPreview.java
index e3a68b8..4811235 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderPreview.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderPreview.java
@@ -64,6 +64,7 @@ import com.android.resources.ResourceFolderType;
import com.android.resources.ResourceType;
import com.android.resources.ScreenOrientation;
import com.android.sdklib.IAndroidTarget;
+import com.android.utils.SdkUtils;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.IProgressMonitor;
@@ -139,7 +140,7 @@ public class RenderPreview implements IJobChangeListener {
}
/** The configuration being previewed */
- private final @NonNull Configuration mConfiguration;
+ private @NonNull Configuration mConfiguration;
/** The associated manager */
private final @NonNull RenderPreviewManager mManager;
@@ -154,6 +155,7 @@ public class RenderPreview implements IJobChangeListener {
private int mHeight;
private int mX;
private int mY;
+ private int mTitleHeight;
private double mScale = 1.0;
private double mAspectRatio;
@@ -166,9 +168,13 @@ public class RenderPreview implements IJobChangeListener {
/** Whether the mouse is actively hovering over this preview */
private boolean mActive;
- /** Whether this preview cannot be rendered because of a model error - such as
- * an invalid configuration, a missing resource, an error in the XML markup, etc */
- private boolean mError;
+ /**
+ * Whether this preview cannot be rendered because of a model error - such
+ * as an invalid configuration, a missing resource, an error in the XML
+ * markup, etc. If non null, contains the error message (or a blank string
+ * if not known), and null if the render was successful.
+ */
+ private String mError;
/**
* Whether this preview presents a file that has been "forked" (separate,
@@ -211,6 +217,15 @@ public class RenderPreview implements IJobChangeListener {
}
/**
+ * Sets the configuration to use for this preview
+ *
+ * @param configuration the new configuration
+ */
+ public void setConfiguration(@NonNull Configuration configuration) {
+ mConfiguration = configuration;
+ }
+
+ /**
* Gets the scale being applied to the thumbnail
*
* @return the scale being applied to the thumbnail
@@ -545,7 +560,7 @@ public class RenderPreview implements IJobChangeListener {
Document document = DomUtilities.getDocument(mInput);
if (document == null) {
- mError = true;
+ mError = "No document";
createErrorThumbnail();
return;
}
@@ -574,7 +589,14 @@ public class RenderPreview implements IJobChangeListener {
}
}
- mError = !render.isSuccess();
+ if (render.isSuccess()) {
+ mError = null;
+ } else {
+ mError = render.getErrorMessage();
+ if (mError == null) {
+ mError = "";
+ }
+ }
if (render.getStatus() == Status.ERROR_TIMEOUT) {
// TODO: Special handling? schedule update again later
@@ -587,7 +609,7 @@ public class RenderPreview implements IJobChangeListener {
}
}
- if (mError) {
+ if (mError != null) {
createErrorThumbnail();
}
}
@@ -663,18 +685,26 @@ public class RenderPreview implements IJobChangeListener {
double scale = getWidth() / (double) image.getWidth();
if (scale < 1.0) {
+ ImageOverlay imageOverlay = mCanvas.getImageOverlay();
+ boolean drawShadows = imageOverlay == null || imageOverlay.getShowDropShadow();
if (LARGE_SHADOWS) {
+ int shadowSize = drawShadows ? SHADOW_SIZE : 0;
image = ImageUtils.scale(image, scale, scale,
- SHADOW_SIZE, SHADOW_SIZE);
- ImageUtils.drawRectangleShadow(image, 0, 0,
- image.getWidth() - SHADOW_SIZE,
- image.getHeight() - SHADOW_SIZE);
+ shadowSize, shadowSize);
+ if (drawShadows) {
+ ImageUtils.drawRectangleShadow(image, 0, 0,
+ image.getWidth() - shadowSize,
+ image.getHeight() - shadowSize);
+ }
} else {
+ int shadowSize = drawShadows ? SMALL_SHADOW_SIZE : 0;
image = ImageUtils.scale(image, scale, scale,
- SMALL_SHADOW_SIZE, SMALL_SHADOW_SIZE);
- ImageUtils.drawSmallRectangleShadow(image, 0, 0,
- image.getWidth() - SMALL_SHADOW_SIZE,
- image.getHeight() - SMALL_SHADOW_SIZE);
+ shadowSize, shadowSize);
+ if (drawShadows) {
+ ImageUtils.drawSmallRectangleShadow(image, 0, 0,
+ image.getWidth() - shadowSize,
+ image.getHeight() - shadowSize);
+ }
}
}
@@ -700,19 +730,23 @@ public class RenderPreview implements IJobChangeListener {
BufferedImage.TYPE_INT_ARGB);
Graphics2D g = image.createGraphics();
- g.setColor(java.awt.Color.WHITE);
+ g.setColor(new java.awt.Color(0xfffbfcc6));
g.fillRect(0, 0, width, height);
g.dispose();
- if (LARGE_SHADOWS) {
- ImageUtils.drawRectangleShadow(image, 0, 0,
- image.getWidth() - SHADOW_SIZE,
- image.getHeight() - SHADOW_SIZE);
- } else {
- ImageUtils.drawSmallRectangleShadow(image, 0, 0,
- image.getWidth() - SMALL_SHADOW_SIZE,
- image.getHeight() - SMALL_SHADOW_SIZE);
+ ImageOverlay imageOverlay = mCanvas.getImageOverlay();
+ boolean drawShadows = imageOverlay == null || imageOverlay.getShowDropShadow();
+ if (drawShadows) {
+ if (LARGE_SHADOWS) {
+ ImageUtils.drawRectangleShadow(image, 0, 0,
+ image.getWidth() - SHADOW_SIZE,
+ image.getHeight() - SHADOW_SIZE);
+ } else {
+ ImageUtils.drawSmallRectangleShadow(image, 0, 0,
+ image.getWidth() - SMALL_SHADOW_SIZE,
+ image.getHeight() - SMALL_SHADOW_SIZE);
+ }
}
mThumbnail = SwtUtils.convertToSwt(mCanvas.getDisplay(), image,
@@ -761,7 +795,7 @@ public class RenderPreview implements IJobChangeListener {
* @return true if this preview handled (and therefore consumed) the click
*/
public boolean click(int x, int y) {
- if (y < HEADER_HEIGHT) {
+ if (y >= mTitleHeight && y < mTitleHeight + HEADER_HEIGHT) {
int left = 0;
left += CLOSE_ICON_WIDTH;
if (x <= left) {
@@ -829,9 +863,13 @@ public class RenderPreview implements IJobChangeListener {
* @param y the y coordinate to paint the preview at
*/
void paint(GC gc, int x, int y) {
+ mTitleHeight = paintTitle(gc, x, y, true /*showFile*/);
+ y += mTitleHeight;
+ y += 2;
+
int width = getWidth();
int height = getHeight();
- if (mThumbnail != null && !mError) {
+ if (mThumbnail != null && mError == null) {
gc.drawImage(mThumbnail, x, y);
if (mActive) {
@@ -841,7 +879,7 @@ public class RenderPreview implements IJobChangeListener {
gc.drawRectangle(x - 1, y - 1, width + 2, height + 2);
gc.setLineWidth(oldWidth);
}
- } else if (mError) {
+ } else if (mError != null) {
if (mThumbnail != null) {
gc.drawImage(mThumbnail, x, y);
} else {
@@ -849,25 +887,29 @@ public class RenderPreview implements IJobChangeListener {
gc.drawRectangle(x, y, width, height);
}
- gc.setClipping(x, y, width, height + 100);
+ gc.setClipping(x, y, width, height);
Image icon = IconFactory.getInstance().getIcon("renderError"); //$NON-NLS-1$
ImageData data = icon.getImageData();
int prevAlpha = gc.getAlpha();
- int alpha = 128-32;
+ int alpha = 96;
if (mThumbnail != null) {
- alpha -= 64;
+ alpha -= 32;
}
gc.setAlpha(alpha);
gc.drawImage(icon, x + (width - data.width) / 2, y + (height - data.height) / 2);
+ String msg = mError;
Density density = mConfiguration.getDensity();
if (density == Density.TV || density == Density.LOW) {
- gc.setAlpha(255);
- gc.setForeground(gc.getDevice().getSystemColor(SWT.COLOR_BLACK));
- gc.drawText("Broken rendering\nlibrary;\nunsupported DPI\n\nTry updating\nSDK platforms",
- x + 8, y + HEADER_HEIGHT, true);
+ msg = "Broken rendering library; unsupported DPI. Try using the SDK manager " +
+ "to get updated layout libraries.";
}
-
+ int charWidth = gc.getFontMetrics().getAverageCharWidth();
+ int charsPerLine = (width - 10) / charWidth;
+ msg = SdkUtils.wrap(msg, charsPerLine, null);
+ gc.setAlpha(255);
+ gc.setForeground(gc.getDevice().getSystemColor(SWT.COLOR_BLACK));
+ gc.drawText(msg, x + 5, y + HEADER_HEIGHT, true);
gc.setAlpha(prevAlpha);
gc.setClipping((Region) null);
} else {
@@ -877,7 +919,7 @@ public class RenderPreview implements IJobChangeListener {
Image icon = IconFactory.getInstance().getIcon("refreshPreview"); //$NON-NLS-1$
ImageData data = icon.getImageData();
int prevAlpha = gc.getAlpha();
- gc.setAlpha(128-32);
+ gc.setAlpha(96);
gc.drawImage(icon, x + (width - data.width) / 2,
y + (height - data.height) / 2);
gc.setAlpha(prevAlpha);
@@ -886,12 +928,14 @@ public class RenderPreview implements IJobChangeListener {
if (mActive) {
int left = x ;
int prevAlpha = gc.getAlpha();
- gc.setAlpha(128+32);
+ gc.setAlpha(208);
Color bg = mCanvas.getDisplay().getSystemColor(SWT.COLOR_WHITE);
gc.setBackground(bg);
gc.fillRectangle(left, y, x + width - left, HEADER_HEIGHT);
gc.setAlpha(prevAlpha);
+ y += 2;
+
// Paint icons
gc.drawImage(CLOSE_ICON, left, y);
left += CLOSE_ICON_WIDTH;
@@ -905,40 +949,40 @@ public class RenderPreview implements IJobChangeListener {
gc.drawImage(EDIT_ICON, left, y);
left += EDIT_ICON_WIDTH;
}
-
- paintTitle(gc, x, y, true /*showFile*/);
}
/**
- * Paints the preview title at the given position
+ * Paints the preview title at the given position (and returns the required
+ * height)
*
* @param gc the graphics context to paint into
* @param x the left edge of the preview rectangle
* @param y the top edge of the preview rectangle
*/
- void paintTitle(GC gc, int x, int y, boolean showFile) {
+ int paintTitle(GC gc, int x, int y, boolean showFile) {
+ int titleHeight = 0;
+
String displayName = getDisplayName();
if (displayName != null && displayName.length() > 0) {
gc.setForeground(gc.getDevice().getSystemColor(SWT.COLOR_WHITE));
int width = getWidth();
- int height = getHeight();
Point extent = gc.textExtent(displayName);
int labelLeft = Math.max(x, x + (width - extent.x) / 2);
- int labelTop = y + height + 1;
- Image flagImage = null;
+ int labelTop = y + 1;
+ Image icon = null;
Locale locale = mConfiguration.getLocale();
if (locale != null && (locale.hasLanguage() || locale.hasRegion())
&& (!(mConfiguration instanceof NestedConfiguration)
|| ((NestedConfiguration) mConfiguration).isOverridingLocale())) {
- flagImage = locale.getFlagImage();
+ icon = locale.getFlagImage();
}
- gc.setClipping(x, y, width, height + 100);
- if (flagImage != null) {
- int flagWidth = flagImage.getImageData().width;
- int flagHeight = flagImage.getImageData().height;
- gc.drawImage(flagImage, labelLeft - flagWidth / 2 - 1, labelTop);
+ gc.setClipping(x, labelTop, width, 100);
+ if (icon != null) {
+ int flagWidth = icon.getImageData().width;
+ int flagHeight = icon.getImageData().height;
+ gc.drawImage(icon, labelLeft - flagWidth / 2 - 1, labelTop);
labelLeft += flagWidth / 2 + 1;
gc.drawText(displayName, labelLeft,
labelTop - (extent.y - flagHeight) / 2, true);
@@ -946,27 +990,38 @@ public class RenderPreview implements IJobChangeListener {
gc.drawText(displayName, labelLeft, labelTop, true);
}
+ // Use font height rather than extent height since we want two adjacent
+ // previews (which may have different display names and therefore end
+ // up with slightly different extent heights) to have identical title
+ // heights such that they are aligned identically
+ titleHeight = gc.getFontMetrics().getHeight();
+
if (mForked && mInput != null && showFile) {
// Draw file flag, and parent folder name
labelTop += extent.y;
String fileName = mInput.getParent().getName() + File.separator + mInput.getName();
extent = gc.textExtent(fileName);
- flagImage = IconFactory.getInstance().getIcon("android_file"); //$NON-NLS-1$
- int flagWidth = flagImage.getImageData().width;
- int flagHeight = flagImage.getImageData().height;
+ icon = IconFactory.getInstance().getIcon("android_file"); //$NON-NLS-1$
+ int flagWidth = icon.getImageData().width;
+ int flagHeight = icon.getImageData().height;
labelLeft = Math.max(x, x + (width - extent.x - flagWidth - 1) / 2);
- gc.drawImage(flagImage, labelLeft, labelTop);
+ gc.drawImage(icon, labelLeft, labelTop);
gc.setForeground(gc.getDevice().getSystemColor(SWT.COLOR_GRAY));
labelLeft += flagWidth + 1;
labelTop -= (extent.y - flagHeight) / 2;
gc.drawText(fileName, labelLeft, labelTop, true);
+
+ titleHeight += Math.max(titleHeight, icon.getImageData().height);
+
}
gc.setClipping((Region) null);
}
+
+ return titleHeight;
}
/**
@@ -992,7 +1047,7 @@ public class RenderPreview implements IJobChangeListener {
FolderConfiguration folderConfig = mConfiguration.getFullConfig();
ScreenOrientationQualifier qualifier = folderConfig.getScreenOrientationQualifier();
ScreenOrientation orientation = qualifier == null
- ? ScreenOrientation.PORTRAIT : qualifier.getValue();
+ ? ScreenOrientation.PORTRAIT : qualifier.getValue();
if (orientation == ScreenOrientation.LANDSCAPE
|| orientation == ScreenOrientation.SQUARE) {
orientation = ScreenOrientation.PORTRAIT;
@@ -1150,6 +1205,11 @@ public class RenderPreview implements IJobChangeListener {
return mDescription;
}
+ @Override
+ public String toString() {
+ return getDisplayName() + ':' + mConfiguration;
+ }
+
/** Sorts render previews into increasing aspect ratio order */
static Comparator<RenderPreview> INCREASING_ASPECT_RATIO = new Comparator<RenderPreview>() {
@Override
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderPreviewManager.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderPreviewManager.java
index c731266..80391cd 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderPreviewManager.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderPreviewManager.java
@@ -571,7 +571,7 @@ public class RenderPreviewManager {
int destHeight = vi.getScaledImgSize();
int x = destX + destWidth / 2 - preview.getWidth() / 2;
- int y = destY + destHeight - preview.getHeight();
+ int y = destY + destHeight;
preview.paintTitle(gc, x, y, false /*showFile*/);
}
@@ -1145,38 +1145,69 @@ public class RenderPreviewManager {
GraphicalEditorPart editor = mCanvas.getEditorDelegate().getGraphicalEditor();
ConfigurationChooser chooser = editor.getConfigurationChooser();
+ Configuration originalConfiguration = chooser.getConfiguration();
+
+ // The new configuration is the configuration which will become the configuration
+ // in the layout editor's chooser
+ Configuration previewConfiguration = preview.getConfiguration();
+ Configuration newConfiguration = previewConfiguration;
+ if (newConfiguration instanceof NestedConfiguration) {
+ // Should never use a complementing configuration for the main
+ // rendering's configuration; instead, create a new configuration
+ // with a snapshot of the configuration's current values
+ newConfiguration = Configuration.copy(previewConfiguration);
+
+ // Remap all the previews to be parented to this new copy instead
+ // of the old one (which is no longer controlled by the chooser)
+ for (RenderPreview p : mPreviews) {
+ Configuration configuration = p.getConfiguration();
+ if (configuration instanceof NestedConfiguration) {
+ NestedConfiguration nested = (NestedConfiguration) configuration;
+ nested.setParent(newConfiguration);
+ }
+ }
+ }
+
+ // Make a preview for the configuration which *was* showing in the
+ // chooser up until this point:
RenderPreview newPreview = mCanvas.getPreview();
if (newPreview == null) {
- newPreview = RenderPreview.create(this, chooser.getConfiguration());
+ newPreview = RenderPreview.create(this, originalConfiguration);
+ }
+
+ // Update its configuration such that it is complementing or inheriting
+ // from the new chosen configuration
+ if (previewConfiguration instanceof ComplementingConfiguration) {
+ originalConfiguration = ComplementingConfiguration.create(
+ (ComplementingConfiguration) previewConfiguration,
+ newConfiguration);
+ newPreview.setConfiguration(originalConfiguration);
+ } else if (previewConfiguration instanceof NestedConfiguration) {
+ originalConfiguration = NestedConfiguration.create(
+ (NestedConfiguration) previewConfiguration,
+ originalConfiguration,
+ newConfiguration);
+ newPreview.setConfiguration(originalConfiguration);
}
// Replace clicked preview with preview of the formerly edited main configuration
- if (newPreview != null) {
- // This doesn't work yet because the image overlay has had its image
- // replaced by the configuration previews! I should make a list of them
- //newPreview.setFullImage(mImageOverlay.getAwtImage());
-
- for (int i = 0, n = mPreviews.size(); i < n; i++) {
- if (preview == mPreviews.get(i)) {
- mPreviews.set(i, newPreview);
- break;
- }
+ // This doesn't work yet because the image overlay has had its image
+ // replaced by the configuration previews! I should make a list of them
+ //newPreview.setFullImage(mImageOverlay.getAwtImage());
+ for (int i = 0, n = mPreviews.size(); i < n; i++) {
+ if (preview == mPreviews.get(i)) {
+ mPreviews.set(i, newPreview);
+ break;
}
- //newPreview.setPosition(preview.getX(), preview.getY());
}
- // Switch main editor to the clicked configuration preview
+ // Stash the corresponding preview (not active) on the canvas so we can
+ // retrieve it if clicking to some other preview later
mCanvas.setPreview(preview);
- Configuration newConfiguration = preview.getConfiguration();
- if (newConfiguration instanceof NestedConfiguration) {
- // Should never use a complementing configuration for the main
- // rendering's configuration; instead, create a new configuration
- // with a snapshot of the configuration's current values
- newConfiguration = Configuration.copy(preview.getConfiguration());
- }
+ // Switch to the configuration from the clicked preview (though it's
+ // most likely a copy, see above)
chooser.setConfiguration(newConfiguration);
-
editor.recomputeLayout();
// Scroll to the top again, if necessary
@@ -1184,7 +1215,7 @@ public class RenderPreviewManager {
mNeedLayout = mNeedZoom = true;
mCanvas.redraw();
- mAnimation = new SwapAnimation(preview);
+ mAnimation = new SwapAnimation(preview, newPreview);
}
/**
@@ -1430,14 +1461,24 @@ public class RenderPreviewManager {
private long begin;
private long end;
private static final long DURATION = 400; // ms
- private Rect initialPos;
+ private Rect initialRect1;
+ private Rect targetRect1;
+ private Rect initialRect2;
+ private Rect targetRect2;
+ private RenderPreview preview;
- SwapAnimation(RenderPreview preview) {
+ SwapAnimation(RenderPreview preview1, RenderPreview preview2) {
begin = System.currentTimeMillis();
end = begin + DURATION;
- initialPos = new Rect(preview.getX(), preview.getY(),
- preview.getWidth(), preview.getHeight());
+ initialRect1 = new Rect(preview1.getX(), preview1.getY(),
+ preview1.getWidth(), preview1.getHeight());
+
+ CanvasTransform hi = mCanvas.getHorizontalTransform();
+ CanvasTransform vi = mCanvas.getVerticalTransform();
+ initialRect2 = new Rect(hi.translate(0), vi.translate(0),
+ hi.getScaledImgSize(), vi.getScaledImgSize());
+ preview = preview2;
}
void tick(GC gc) {
@@ -1447,21 +1488,33 @@ public class RenderPreviewManager {
return;
}
- // For now, just animation rect1 towards rect2
- // The shape of the canvas might have shifted if zoom or device size
- // or orientation changed, so compute a new target size
CanvasTransform hi = mCanvas.getHorizontalTransform();
CanvasTransform vi = mCanvas.getVerticalTransform();
- Rect rect = new Rect(hi.translate(0), vi.translate(0),
+ if (targetRect1 == null) {
+ targetRect1 = new Rect(hi.translate(0), vi.translate(0),
hi.getScaledImgSize(), vi.getScaledImgSize());
+ }
double portion = (now - begin) / (double) DURATION;
- rect.x = (int) (portion * (rect.x - initialPos.x) + initialPos.x);
- rect.y = (int) (portion * (rect.y - initialPos.y) + initialPos.y);
- rect.w = (int) (portion * (rect.w - initialPos.w) + initialPos.w);
- rect.h = (int) (portion * (rect.h - initialPos.h) + initialPos.h);
+ Rect rect1 = new Rect(
+ (int) (portion * (targetRect1.x - initialRect1.x) + initialRect1.x),
+ (int) (portion * (targetRect1.y - initialRect1.y) + initialRect1.y),
+ (int) (portion * (targetRect1.w - initialRect1.w) + initialRect1.w),
+ (int) (portion * (targetRect1.h - initialRect1.h) + initialRect1.h));
+
+ if (targetRect2 == null) {
+ targetRect2 = new Rect(preview.getX(), preview.getY(),
+ preview.getWidth(), preview.getHeight());
+ }
+ portion = (now - begin) / (double) DURATION;
+ Rect rect2 = new Rect(
+ (int) (portion * (targetRect2.x - initialRect2.x) + initialRect2.x),
+ (int) (portion * (targetRect2.y - initialRect2.y) + initialRect2.y),
+ (int) (portion * (targetRect2.w - initialRect2.w) + initialRect2.w),
+ (int) (portion * (targetRect2.h - initialRect2.h) + initialRect2.h));
gc.setForeground(gc.getDevice().getSystemColor(SWT.COLOR_GRAY));
- gc.drawRectangle(rect.x, rect.y, rect.w, rect.h);
+ gc.drawRectangle(rect1.x, rect1.y, rect1.w, rect1.h);
+ gc.drawRectangle(rect2.x, rect2.y, rect2.w, rect2.h);
mCanvas.getDisplay().timerExec(5, this);
}
diff --git a/lint/cli/src/com/android/tools/lint/Main.java b/lint/cli/src/com/android/tools/lint/Main.java
index a33890d..ad086c7 100644
--- a/lint/cli/src/com/android/tools/lint/Main.java
+++ b/lint/cli/src/com/android/tools/lint/Main.java
@@ -16,9 +16,9 @@
package com.android.tools.lint;
+import static com.android.SdkConstants.DOT_XML;
import static com.android.tools.lint.client.api.IssueRegistry.LINT_ERROR;
import static com.android.tools.lint.client.api.IssueRegistry.PARSER_ERROR;
-import static com.android.SdkConstants.DOT_XML;
import static com.android.tools.lint.detector.api.LintUtils.endsWith;
import com.android.annotations.NonNull;
@@ -40,6 +40,7 @@ import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.Position;
import com.android.tools.lint.detector.api.Project;
import com.android.tools.lint.detector.api.Severity;
+import com.android.utils.SdkUtils;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.io.Closeables;
@@ -873,47 +874,7 @@ public class Main extends LintClient {
}
static String wrap(String explanation, int lineWidth, String hangingIndent) {
- int explanationLength = explanation.length();
- StringBuilder sb = new StringBuilder(explanationLength * 2);
- int index = 0;
-
- while (index < explanationLength) {
- int lineEnd = explanation.indexOf('\n', index);
- int next;
-
- if (lineEnd != -1 && (lineEnd - index) < lineWidth) {
- next = lineEnd + 1;
- } else {
- // Line is longer than available width; grab as much as we can
- lineEnd = Math.min(index + lineWidth, explanationLength);
- if (lineEnd - index < lineWidth) {
- next = explanationLength;
- } else {
- // then back up to the last space
- int lastSpace = explanation.lastIndexOf(' ', lineEnd);
- if (lastSpace > index) {
- lineEnd = lastSpace;
- next = lastSpace + 1;
- } else {
- // No space anywhere on the line: it contains something wider than
- // can fit (like a long URL) so just hard break it
- next = lineEnd + 1;
- }
- }
- }
-
- if (sb.length() > 0) {
- sb.append(hangingIndent);
- } else {
- lineWidth -= hangingIndent.length();
- }
-
- sb.append(explanation.substring(index, lineEnd));
- sb.append('\n');
- index = next;
- }
-
- return sb.toString();
+ return SdkUtils.wrap(explanation, lineWidth, hangingIndent);
}
private static void printUsage(PrintStream out) {
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/MainTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/MainTest.java
index 0ea384e..4e7a029 100644
--- a/lint/libs/lint_checks/tests/src/com/android/tools/lint/MainTest.java
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/MainTest.java
@@ -28,52 +28,6 @@ import java.util.List;
@SuppressWarnings("javadoc")
public class MainTest extends AbstractCheckTest {
- public void testWrap() {
- String s =
- "Hardcoding text attributes directly in layout files is bad for several reasons:\n" +
- "\n" +
- "* When creating configuration variations (for example for landscape or portrait)" +
- "you have to repeat the actual text (and keep it up to date when making changes)\n" +
- "\n" +
- "* The application cannot be translated to other languages by just adding new " +
- "translations for existing string resources.";
- String wrapped = Main.wrap(s, 70, "");
- assertEquals(
- "Hardcoding text attributes directly in layout files is bad for several\n" +
- "reasons:\n" +
- "\n" +
- "* When creating configuration variations (for example for landscape or\n" +
- "portrait)you have to repeat the actual text (and keep it up to date\n" +
- "when making changes)\n" +
- "\n" +
- "* The application cannot be translated to other languages by just\n" +
- "adding new translations for existing string resources.\n",
- wrapped);
- }
-
- public void testWrapPrefix() {
- String s =
- "Hardcoding text attributes directly in layout files is bad for several reasons:\n" +
- "\n" +
- "* When creating configuration variations (for example for landscape or portrait)" +
- "you have to repeat the actual text (and keep it up to date when making changes)\n" +
- "\n" +
- "* The application cannot be translated to other languages by just adding new " +
- "translations for existing string resources.";
- String wrapped = Main.wrap(s, 70, " ");
- assertEquals(
- "Hardcoding text attributes directly in layout files is bad for several\n" +
- " reasons:\n" +
- " \n" +
- " * When creating configuration variations (for example for\n" +
- " landscape or portrait)you have to repeat the actual text (and keep\n" +
- " it up to date when making changes)\n" +
- " \n" +
- " * The application cannot be translated to other languages by just\n" +
- " adding new translations for existing string resources.\n",
- wrapped);
- }
-
protected String checkLint(String[] args, List<File> files) throws Exception {
PrintStream previousOut = System.out;
try {