aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--anttasks/src/com/android/ant/AaptExecLoopTask.java7
-rw-r--r--anttasks/src/com/android/ant/BaseTask.java20
-rw-r--r--anttasks/src/com/android/ant/DependencyGraph.java34
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/build_messages.properties2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerBuilder.java28
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerDeltaVisitor.java45
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlAutoEditStrategy.java88
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlCharacterMatcher.java12
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GestureManager.java6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiElementNode.java13
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/CompiledResourcesMonitor.java5
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectResources.java5
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceManager.java50
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlAutoEditStrategyTest.java141
-rw-r--r--files/ant/build.xml3
-rw-r--r--hierarchyviewer/src/com/android/hierarchyviewer/HierarchyViewer.java10
-rw-r--r--hierarchyviewer/src/com/android/hierarchyviewer/scene/ProfilesLoader.java5
-rw-r--r--ide_common/src/com/android/ide/common/resources/IdGeneratingResourceFile.java24
-rw-r--r--ide_common/src/com/android/ide/common/resources/MultiResourceFile.java46
-rw-r--r--ide_common/src/com/android/ide/common/resources/ResourceRepository.java24
-rw-r--r--ide_common/src/com/android/ide/common/resources/SingleResourceFile.java10
-rw-r--r--sdkmanager/app/tests/com/android/sdkmanager/AvdManagerTest.java1
-rw-r--r--sdkmanager/app/tests/com/android/sdkmanager/MainTest.java1
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/PlatformTarget.java29
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java93
-rwxr-xr-xsdkmanager/libs/sdklib/tests/src/com/android/sdklib/LayoutlibVersionTest.java51
-rwxr-xr-xsdkmanager/libs/sdklib/tests/src/com/android/sdklib/SdkManagerTest.java47
-rwxr-xr-xsdkmanager/libs/sdklib/tests/src/com/android/sdklib/SdkManagerTestCase.java (renamed from sdkmanager/app/tests/com/android/sdkmanager/SdkManagerTestCase.java)17
-rw-r--r--traceview/src/com/android/traceview/DmTraceReader.java5
29 files changed, 726 insertions, 96 deletions
diff --git a/anttasks/src/com/android/ant/AaptExecLoopTask.java b/anttasks/src/com/android/ant/AaptExecLoopTask.java
index 6b438bb..7b7d82d 100644
--- a/anttasks/src/com/android/ant/AaptExecLoopTask.java
+++ b/anttasks/src/com/android/ant/AaptExecLoopTask.java
@@ -348,7 +348,7 @@ public final class AaptExecLoopTask extends BaseTask {
// Now we figure out what we need to do
if (generateRClass) {
// Check to see if our dependencies have changed. If not, then skip
- if (initDependencies(mRFolder + File.separator + "R.d", watchPaths)
+ if (initDependencies(mRFolder + File.separator + "R.java.d", watchPaths)
&& dependenciesHaveChanged() == false) {
System.out.println("No changed resources. R.java and Manifest.java untouched.");
return;
@@ -357,13 +357,12 @@ public final class AaptExecLoopTask extends BaseTask {
// Find our dependency file. It should have the same name as our target .ap_ but
// with a .d extension
String dependencyFilePath = mApkFolder + File.separator + mApkName;
- dependencyFilePath =
- dependencyFilePath.substring(0, dependencyFilePath.lastIndexOf(".")) + ".d";
+ dependencyFilePath += ".d";
// Check to see if our dependencies have changed
if (initDependencies(dependencyFilePath , watchPaths)
&& dependenciesHaveChanged() == false) {
- System.out.println("No changed resources or assets. " + dependencyFilePath
+ System.out.println("No changed resources or assets. " + mApkName
+ " remains untouched");
return;
}
diff --git a/anttasks/src/com/android/ant/BaseTask.java b/anttasks/src/com/android/ant/BaseTask.java
index 00b7fcb..0ff7bf1 100644
--- a/anttasks/src/com/android/ant/BaseTask.java
+++ b/anttasks/src/com/android/ant/BaseTask.java
@@ -21,6 +21,8 @@ import org.apache.tools.ant.Task;
import java.io.File;
import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
/**
* A base class for the ant task that contains logic for handling dependency files
@@ -41,6 +43,22 @@ public abstract class BaseTask extends Task {
protected abstract String getExecTaskName();
+ private Set<String> mRestrictTouchedExtensionsTo;
+
+ /**
+ * Sets the value of the "restricttouchedextensionsto" attribute.
+ * @param touchedExtensions the extensions to check to see if they have been modified.
+ * values should be separated by a colon (:). If left blank or not set, all extensions
+ * will be checked.
+ */
+ public void setRestrictTouchedExtensionsTo(String restrictTouchedExtensionsTo) {
+ mRestrictTouchedExtensionsTo = new HashSet<String>();
+ String[] extensions = restrictTouchedExtensionsTo.split(":");
+ for (String s : extensions) {
+ mRestrictTouchedExtensionsTo.add(s);
+ }
+ }
+
@Override
public void execute() throws BuildException {
@@ -84,6 +102,6 @@ public abstract class BaseTask extends Task {
}
assert mDependencies != null : "Dependencies have not been initialized";
- return mDependencies.dependenciesHaveChanged();
+ return mDependencies.dependenciesHaveChanged(mRestrictTouchedExtensionsTo);
}
}
diff --git a/anttasks/src/com/android/ant/DependencyGraph.java b/anttasks/src/com/android/ant/DependencyGraph.java
index 5939a92..4c85860 100644
--- a/anttasks/src/com/android/ant/DependencyGraph.java
+++ b/anttasks/src/com/android/ant/DependencyGraph.java
@@ -36,7 +36,7 @@ public class DependencyGraph {
// Files that we know about from the dependency file
private Set<File> mTargets = Collections.emptySet();
private Set<File> mPrereqs = mTargets;
- private ArrayList<File> mWatchPaths;
+ private final ArrayList<File> mWatchPaths;
public DependencyGraph(String dependencyFilePath, ArrayList<File> watchPaths) {
mWatchPaths = watchPaths;
@@ -45,15 +45,18 @@ public class DependencyGraph {
/**
* Check all the dependencies to see if anything has changed.
+ * @param extensionsToCheck a set of extensions. Only files with an extension in this set will
+ * be considered for a modification check. All deleted/created files will still be
+ * checked. If this is null, all files will be checked for modification date
* @return true if new prerequisites have appeared, target files are missing or if
* prerequisite files have been modified since the last target generation.
*/
- public boolean dependenciesHaveChanged() {
+ public boolean dependenciesHaveChanged(Set<String> extensionsToCheck) {
boolean noFile = (mTargets.size() == 0);
boolean missingPrereq = missingPrereqFile();
boolean newPrereq = newPrereqFile();
boolean missingTarget = missingTargetFile();
- boolean modPrereq = modifiedPrereq();
+ boolean modPrereq = modifiedPrereq(extensionsToCheck);
if (noFile) {
System.out.println("No Dependency File Found");
@@ -206,7 +209,7 @@ public class DependencyGraph {
* @return true if the latest prerequisite modification is after the oldest
* target modification.
*/
- private boolean modifiedPrereq() {
+ private boolean modifiedPrereq(Set<String> extensionsToCheck) {
// Find the oldest target
long oldestTarget = Long.MAX_VALUE;
for (File target : mTargets) {
@@ -218,8 +221,12 @@ public class DependencyGraph {
// Find the newest prerequisite
long newestPrereq = 0;
for (File prereq : mPrereqs) {
- if (prereq.lastModified() > newestPrereq) {
- newestPrereq = prereq.lastModified();
+ // If we have a list of extensions that we need to restrict ourselves to, only
+ // consider this file if it has that extension.
+ if (extensionsToCheck == null || extensionsToCheck.contains(getExtension(prereq))) {
+ if (prereq.lastModified() > newestPrereq) {
+ newestPrereq = prereq.lastModified();
+ }
}
}
@@ -251,4 +258,19 @@ public class DependencyGraph {
}
return null;
}
+
+ /**
+ * Gets the extension (if present) on a file by looking at the filename
+ * @param file the file to get the extension of
+ * @return the extension if present, or the empty string if the filename doesn't have
+ * and extension.
+ */
+ private static String getExtension(File file) {
+ String filename = file.getName();
+ if (filename.lastIndexOf('.') == -1) {
+ return "";
+ }
+ // Don't include the leading '.' in the extension
+ return filename.substring(filename.lastIndexOf('.') + 1);
+ }
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/build_messages.properties b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/build_messages.properties
index aabe2a4..28ceebd 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/build_messages.properties
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/build_messages.properties
@@ -37,7 +37,7 @@ AIDL_Java_Conflict=%1$s is in the way of %2$s, remove it or rename of one the fi
AIDL_Exec_Error=Error executing aidl. Please check aidl is present at %1$s
s_Removed_Recreating_s=%1$s was removed\! Recreating %1$s\!
s_Modified_Manually_Recreating_s=%1$s was modified manually\! Reverting to generated version\!
-s_Modified_Recreating_s='%1$s' was modified, %2$s needs to be updated.
+s_Modified_Recreating_s='%1$s' was modified.
Added_s_s_Needs_Updating=New resource file: '%1$s', %2$s needs to be updated.
s_Removed_s_Needs_Updating='%1$s' was removed, %2$s needs to be updated.
Requires_Compiler_Compliance_s=Android requires compiler compliance level 5.0 or 6.0. Found '%1$s' instead. Please use Android Tools > Fix Project Properties.
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerBuilder.java
index 2a988d1..3ece0e5 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerBuilder.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerBuilder.java
@@ -255,10 +255,15 @@ public class PreCompilerBuilder extends BaseBuilder {
dv = new PreCompilerDeltaVisitor(this, sourceFolderPathList, mProcessors);
delta.accept(dv);
// Notify the ResourceManager:
- ResourceManager.getInstance().processDelta(delta);
+ ResourceManager resManager = ResourceManager.getInstance();
+ resManager.processDelta(delta);
- // record the state
+ // Check whether this project or its dependencies (libraries) have
+ // resources that need compilation
+ mMustCompileResources |= resManager.projectNeedsIdGeneration(project);
+ // Check to see if Manifest.xml, Manifest.java, or R.java have changed:
mMustCompileResources |= dv.getCompileResources();
+
for (SourceProcessor processor : mProcessors) {
processor.doneVisiting(project);
}
@@ -266,24 +271,6 @@ public class PreCompilerBuilder extends BaseBuilder {
// get the java package from the visitor
javaPackage = dv.getManifestPackage();
minSdkVersion = dv.getMinSdkVersion();
-
- // if the main resources didn't change, then we check for the library
- // ones (will trigger resource recompilation too)
- if (mMustCompileResources == false && libProjects.size() > 0) {
- for (IProject libProject : libProjects) {
- delta = getDelta(libProject);
- if (delta != null) {
- LibraryDeltaVisitor visitor = new LibraryDeltaVisitor();
- delta.accept(visitor);
-
- mMustCompileResources = visitor.getResChange();
-
- if (mMustCompileResources) {
- break;
- }
- }
- }
- }
}
}
@@ -496,6 +483,7 @@ public class PreCompilerBuilder extends BaseBuilder {
handleResources(project, javaPackage, projectTarget, manifestFile, libProjects,
projectState.isLibrary());
saveProjectBooleanProperty(PROPERTY_COMPILE_RESOURCES , false);
+ // The project resources will find out that they're in sync when their IDs are set
}
if (processorStatus == SourceProcessor.COMPILE_STATUS_NONE &&
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerDeltaVisitor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerDeltaVisitor.java
index cd99fbe..5f08856 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerDeltaVisitor.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerDeltaVisitor.java
@@ -16,11 +16,11 @@
package com.android.ide.eclipse.adt.internal.build.builders;
-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.build.Messages;
import com.android.ide.eclipse.adt.internal.build.SourceChangeHandler;
import com.android.ide.eclipse.adt.internal.build.SourceProcessor;
-import com.android.ide.eclipse.adt.internal.build.Messages;
import com.android.ide.eclipse.adt.internal.build.builders.BaseBuilder.BaseDeltaVisitor;
import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs.BuildVerbosity;
import com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper;
@@ -60,9 +60,8 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements IResourceDelta
// Result fields.
/**
* Compile flag. This is set to true if one of the changed/added/removed
- * file is a resource file. Upon visiting all the delta resources, if
- * this flag is true, then we know we'll have to compile the resources
- * into R.java
+ * files is Manifest.xml, Manifest.java, or R.java. All other file changes
+ * will be taken care of by ResourceManager.
*/
private boolean mCompileResources = false;
@@ -88,12 +87,12 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements IResourceDelta
private IFolder mSourceFolder = null;
/** List of source folders. */
- private List<IPath> mSourceFolders;
+ private final List<IPath> mSourceFolders;
private boolean mIsGenSourceFolder = false;
private final List<SourceChangeHandler> mSourceChangeHandlers =
new ArrayList<SourceChangeHandler>();
- private IWorkspaceRoot mRoot;
+ private final IWorkspaceRoot mRoot;
@@ -109,6 +108,10 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements IResourceDelta
}
}
+ /**
+ * Get whether Manifest.java, Manifest.xml, or R.java have changed
+ * @return true if any of Manifest.xml, Manifest.java, or R.java have been modified
+ */
public boolean getCompileResources() {
return mCompileResources;
}
@@ -323,8 +326,7 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements IResourceDelta
switch (kind) {
case IResourceDelta.CHANGED:
// display verbose message
- message = String.format(Messages.s_Modified_Recreating_s, p,
- AdtConstants.FN_RESOURCE_CLASS);
+ message = String.format(Messages.s_Modified_Recreating_s, p);
break;
case IResourceDelta.ADDED:
// display verbose message
@@ -345,26 +347,13 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements IResourceDelta
for (SourceChangeHandler handler : mSourceChangeHandlers) {
handler.handleResourceFile((IFile)resource, kind);
}
-
- if (AdtConstants.EXT_XML.equalsIgnoreCase(ext)) {
- if (kind != IResourceDelta.REMOVED) {
- // check xml Validity
- mBuilder.checkXML(resource, this);
- }
-
- // if we are going through this resource, it was modified
- // somehow.
- // we don't care if it was an added/removed/changed event
- mCompileResources = true;
- return false;
- } else {
- // this is a non xml resource.
- if (kind == IResourceDelta.ADDED
- || kind == IResourceDelta.REMOVED) {
- mCompileResources = true;
- return false;
- }
+ // If it's an XML resource, check the syntax
+ if (AdtConstants.EXT_XML.equalsIgnoreCase(ext) && kind != IResourceDelta.REMOVED) {
+ // check xml Validity
+ mBuilder.checkXML(resource, this);
}
+ // Whether or not to generate R.java for a changed resource is taken care of by the
+ // Resource Manager.
} else if (resource instanceof IFolder) {
// in this case we may be inside a folder that contains a source
// folder, go through the list of known source folders
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlAutoEditStrategy.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlAutoEditStrategy.java
index 2ec328d..e8d0aee 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlAutoEditStrategy.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlAutoEditStrategy.java
@@ -15,6 +15,7 @@
*/
package com.android.ide.eclipse.adt.internal.editors;
+import static org.eclipse.wst.xml.core.internal.regions.DOMRegionContext.XML_CONTENT;
import static org.eclipse.wst.xml.core.internal.regions.DOMRegionContext.XML_EMPTY_TAG_CLOSE;
import static org.eclipse.wst.xml.core.internal.regions.DOMRegionContext.XML_END_TAG_OPEN;
import static org.eclipse.wst.xml.core.internal.regions.DOMRegionContext.XML_TAG_CLOSE;
@@ -172,6 +173,93 @@ public class AndroidXmlAutoEditStrategy implements IAutoEditStrategy {
sb.append(lineIndent);
}
c.text = sb.toString();
+ } else if (region != null && region.getType().equals(XML_CONTENT)) {
+ // Indenting in text content. If you're in the middle of editing
+ // text, just copy the current line indentation.
+ // However, if you're editing in leading whitespace (e.g. you press
+ // newline on a blank line following say an element) then figure
+ // out the indentation as if the newline had been pressed at the
+ // end of the element, and insert that amount of indentation.
+ // In this case we need to also make sure to subtract any existing
+ // whitespace on the current line such that if we have
+ //
+ // <foo>
+ // ^ <bar/>
+ // </foo>
+ //
+ // you end up with
+ //
+ // <foo>
+ //
+ // ^<bar/>
+ // </foo>
+ //
+ String text = region.getText();
+ int regionStart = region.getStartOffset();
+ int delta = offset - regionStart;
+ boolean inWhitespacePrefix = true;
+ for (int i = 0, n = Math.min(delta, text.length()); i < n; i++) {
+ char ch = text.charAt(i);
+ if (!Character.isWhitespace(ch)) {
+ inWhitespacePrefix = false;
+ break;
+ }
+ }
+ if (inWhitespacePrefix) {
+ IStructuredDocumentRegion previous = region.getPrevious();
+ if (previous != null && previous.getType() == XML_TAG_NAME) {
+ ITextRegionList subRegions = previous.getRegions();
+ ITextRegion last = subRegions.get(subRegions.size() - 1);
+ if (last.getType() == XML_TAG_CLOSE ||
+ last.getType() == XML_EMPTY_TAG_CLOSE) {
+ int begin = AndroidXmlCharacterMatcher.findTagBackwards(doc,
+ previous.getStartOffset() + last.getStart(), 0);
+ int prevLineStart = findLineStart(doc, begin);
+ int prevTextStart = findTextStart(doc, prevLineStart, begin);
+
+ String lineIndent = ""; //$NON-NLS-1$
+ if (prevTextStart > prevLineStart) {
+ lineIndent = doc.get(prevLineStart,
+ prevTextStart - prevLineStart);
+ }
+ StringBuilder sb = new StringBuilder(c.text);
+ sb.append(lineIndent);
+ String oneIndentUnit =
+ XmlFormatPreferences.create().getOneIndentUnit();
+
+ // See if there is whitespace on the insert line that
+ // we should also remove
+ for (int i = delta, n = text.length(); i < n; i++) {
+ char ch = text.charAt(i);
+ if (ch == ' ') {
+ c.length++;
+ } else {
+ break;
+ }
+ }
+ boolean onClosingTagLine = false;
+ if (text.indexOf('\n', delta) == -1) {
+ IStructuredDocumentRegion next = region.getNext();
+ if (next != null && next.getType() == XML_TAG_NAME) {
+ String nextType = next.getRegions().get(0).getType();
+ if (nextType == XML_END_TAG_OPEN) {
+ onClosingTagLine = true;;
+ }
+ }
+ }
+
+ boolean addIndent = (last.getType() == XML_TAG_CLOSE)
+ && !onClosingTagLine;
+ if (addIndent) {
+ sb.append(oneIndentUnit);
+ }
+ c.text = sb.toString();
+
+ return;
+ }
+ }
+ }
+ copyPreviousLineIndentation(doc, c);
} else {
copyPreviousLineIndentation(doc, c);
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlCharacterMatcher.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlCharacterMatcher.java
index abf446e..8a12fe0 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlCharacterMatcher.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlCharacterMatcher.java
@@ -36,6 +36,9 @@ import org.eclipse.wst.xml.ui.internal.text.XMLDocumentRegionEdgeMatcher;
*/
@SuppressWarnings("restriction")
public class AndroidXmlCharacterMatcher extends XMLDocumentRegionEdgeMatcher {
+ /**
+ * Constructs a new character matcher for Android XML files
+ */
public AndroidXmlCharacterMatcher() {
}
@@ -184,6 +187,15 @@ public class AndroidXmlCharacterMatcher extends XMLDocumentRegionEdgeMatcher {
return -1;
}
+ /**
+ * Finds the corresponding closing tag by searching forwards until the tag balance
+ * reaches a given target.
+ *
+ * @param doc the document
+ * @param start the starting offset (where the search begins searching forwards from)
+ * @param targetTagBalance the balance to end the search at
+ * @return the offset of the beginning of the closing tag
+ */
public static int findTagForwards(IStructuredDocument doc, int start, int targetTagBalance) {
int tagBalance = 0;
IStructuredDocumentRegion region = doc.getRegionAtCharacterOffset(start);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GestureManager.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GestureManager.java
index 74960cf..6d878a7 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GestureManager.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GestureManager.java
@@ -468,6 +468,12 @@ public class GestureManager {
}
if (mCurrentGesture == null) {
updateCursor(mousePos);
+ } else if (mCurrentGesture instanceof DropGesture) {
+ // Mouse Up shouldn't be delivered in the middle of a drag & drop -
+ // but this can happen on some versions of Linux
+ // (see http://code.google.com/p/android/issues/detail?id=19057 )
+ // and if we process the mouseUp it will abort the remainder of
+ // the drag & drop operation, so ignore this event!
} else {
finishGesture(mousePos, false);
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiElementNode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiElementNode.java
index ef7deaa..eb24870 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiElementNode.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiElementNode.java
@@ -1371,7 +1371,8 @@ public class UiElementNode implements IPropertySource {
}
/**
- * Updates the {@link UiAttributeNode} list for this {@link UiElementNode}.
+ * Updates the {@link UiAttributeNode} list for this {@link UiElementNode}
+ * using the values from the XML element.
* <p/>
* For a given {@link UiElementNode}, the attribute list always exists in
* full and is totally independent of whether the XML model actually
@@ -1456,6 +1457,12 @@ public class UiElementNode implements IPropertySource {
}
}
+ /**
+ * Create a new temporary text attribute descriptor for the unknown attribute
+ * and returns a new {@link UiAttributeNode} associated to this descriptor.
+ * <p/>
+ * The attribute is not marked as dirty, doing so is up to the caller.
+ */
private UiAttributeNode addUnknownAttribute(String xmlFullName,
String xmlAttrLocalName, String xmlNsUri) {
// Create a new unknown attribute of format string
@@ -1467,7 +1474,6 @@ public class UiElementNode implements IPropertySource {
new AttributeInfo(xmlAttrLocalName, new Format[] { Format.STRING } )
);
UiAttributeNode uiAttr = desc.createUiNode(this);
- uiAttr.setDirty(true);
mUnknownUiAttributes.add(uiAttr);
mCachedAllUiAttributes = null;
return uiAttr;
@@ -1815,6 +1821,9 @@ public class UiElementNode implements IPropertySource {
// We've created the attribute, but not actually set the value on it, so let's do it.
// Try with the updated internal attributes.
+ // Implementation detail: we could just do a setCurrentValue + setDirty on the
+ // uiAttr returned by addUnknownAttribute(); however going through setInternalAttrValue
+ // means we won't duplicate the logic, at the expense of doing one more lookup.
uiAttr = setInternalAttrValue(
getAllUiAttributes(), attrXmlName, attrNsUri, value, override);
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/CompiledResourcesMonitor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/CompiledResourcesMonitor.java
index c917d1c..6ef1710 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/CompiledResourcesMonitor.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/CompiledResourcesMonitor.java
@@ -32,7 +32,6 @@ import org.eclipse.core.resources.IMarkerDelta;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceDelta;
-import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
@@ -79,9 +78,7 @@ public final class CompiledResourcesMonitor implements IFileListener, IProjectLi
* @see IFileListener#fileChanged
*/
public void fileChanged(IFile file, IMarkerDelta[] markerDeltas, int kind) {
- // Don't execute if we're autobuilding, let the precompiler take care of the delta
- if (file.getName().equals(AdtConstants.FN_COMPILED_RESOURCE_CLASS)
- && ResourcesPlugin.getWorkspace().getDescription().isAutoBuilding() == false) {
+ if (file.getName().equals(AdtConstants.FN_COMPILED_RESOURCE_CLASS)) {
loadAndParseRClass(file.getProject());
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectResources.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectResources.java
index 6d15f89..6967e67 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectResources.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectResources.java
@@ -72,7 +72,6 @@ public class ProjectResources extends ResourceRepository {
private final IProject mProject;
-
/**
* Makes a ProjectResources for a given <var>project</var>.
* @param project the project.
@@ -288,5 +287,9 @@ public class ProjectResources extends ResourceRepository {
mResourceValueMap = resourceValueMap;
mResIdValueToNameMap = resIdValueToNameMap;
mStyleableValueToNameMap = styleableValueMap;
+
+ // Our resource IDs should now be in sync
+ setIdsRefreshed();
}
+
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceManager.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceManager.java
index c014601..edf55cd 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceManager.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceManager.java
@@ -26,6 +26,8 @@ import com.android.ide.eclipse.adt.internal.resources.ResourceHelper;
import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor.IProjectListener;
import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor.IRawDeltaListener;
import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor.IResourceEventListener;
+import com.android.ide.eclipse.adt.internal.sdk.ProjectState;
+import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.ide.eclipse.adt.io.IFileWrapper;
import com.android.ide.eclipse.adt.io.IFolderWrapper;
import com.android.io.FolderWrapper;
@@ -337,10 +339,10 @@ public final class ResourceManager {
return;
}
- // checks if the file is under res/something.
+ // checks if the file is under res/something or bin/res/something
IPath path = file.getFullPath();
- if (path.segmentCount() == 4) {
+ if (path.segmentCount() == 4 || path.segmentCount() == 5) {
if (isInResFolder(path)) {
IContainer container = file.getParent();
if (container instanceof IFolder) {
@@ -464,6 +466,50 @@ public final class ResourceManager {
}
/**
+ * Checks the ResourceRepositories associated with the given project and its dependencies
+ * and returns whether or not a resource regeneration is needed for that project
+ * @param project the project to check
+ * @return true if the project or any of its dependencies says it has new or deleted resources
+ */
+ public boolean projectNeedsIdGeneration(IProject project) {
+ // Get a list of repositories to check through
+ List<ProjectResources> repositories = getAllProjectResourcesAssociatedWith(project);
+ for (ProjectResources repository : repositories) {
+ if (repository.needsIdRefresh()) {
+ return true;
+ }
+ }
+ // If we've gotten to here, all repositories are in sync, return false
+ return false;
+ }
+
+ /**
+ * Get all the resource repositories representing this project and any included libraries
+ * @param project the project to get along with its dependencies
+ * @return a list of all ProjectResources ordered lowest to highest priority that need to be
+ * included in this project.
+ */
+ private List<ProjectResources> getAllProjectResourcesAssociatedWith(IProject project) {
+ List<ProjectResources> toRet = new ArrayList<ProjectResources>();
+ // if the project contains libraries, we need to add the libraries resources here
+ if (project != null) {
+ ProjectState state = Sdk.getProjectState(project);
+ if (state != null) {
+ List<IProject> libraries = state.getFullLibraryProjects();
+ for (IProject library : libraries) {
+ ProjectResources libRes = mMap.get(library);
+ if (libRes != null) {
+ toRet.add(libRes);
+ }
+ }
+ }
+ }
+ // Add the queried current project last
+ toRet.add(mMap.get(project));
+ return toRet;
+ }
+
+ /**
* Initial project parsing to gather resource info.
* @param project
*/
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlAutoEditStrategyTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlAutoEditStrategyTest.java
index ec0e73f..31e3ddb 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlAutoEditStrategyTest.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlAutoEditStrategyTest.java
@@ -27,6 +27,7 @@ import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.ide.IDE;
+@SuppressWarnings("javadoc")
public class AndroidXmlAutoEditStrategyTest extends AdtProjectTest {
public void checkInsertNewline(String before, String after) throws Exception {
@@ -101,12 +102,14 @@ public class AndroidXmlAutoEditStrategyTest extends AdtProjectTest {
public void testCornerCase2() throws Exception {
checkInsertNewline(
"\n^",
+
"\n\n^");
}
public void testCornerCase3() throws Exception {
checkInsertNewline(
" ^",
+
" \n" +
" ^");
}
@@ -114,6 +117,7 @@ public class AndroidXmlAutoEditStrategyTest extends AdtProjectTest {
public void testSimpleIndentation1() throws Exception {
checkInsertNewline(
" ^ ",
+
" \n" +
" ^ ");
}
@@ -122,6 +126,7 @@ public class AndroidXmlAutoEditStrategyTest extends AdtProjectTest {
checkInsertNewline(
"\n" +
" foo^\n",
+
"\n" +
" foo\n" +
" ^\n");
@@ -131,6 +136,7 @@ public class AndroidXmlAutoEditStrategyTest extends AdtProjectTest {
checkInsertNewline(
"\n" +
" <newtag>^\n",
+
"\n" +
" <newtag>\n" +
" ^\n");
@@ -140,6 +146,7 @@ public class AndroidXmlAutoEditStrategyTest extends AdtProjectTest {
checkInsertNewline(
"\n" +
" <newtag/>^\n",
+
"\n" +
" <newtag/>\n" +
" ^\n");
@@ -158,6 +165,7 @@ public class AndroidXmlAutoEditStrategyTest extends AdtProjectTest {
checkInsertNewline(
"\n" +
" <newtag ^attribute='value'/>\n",
+
"\n" +
" <newtag \n" +
" ^attribute='value'/>\n");
@@ -167,6 +175,7 @@ public class AndroidXmlAutoEditStrategyTest extends AdtProjectTest {
// Make sure that inside a comment we ignore tags etc
checkInsertNewline(
"<!--\n foo^\n--->\n",
+
"<!--\n foo\n ^\n--->\n");
}
@@ -177,6 +186,7 @@ public class AndroidXmlAutoEditStrategyTest extends AdtProjectTest {
"<!--\n" +
"<foo><^\n" +
"-->\n",
+
"\n" +
"<!--\n" +
"<foo><\n" +
@@ -188,6 +198,7 @@ public class AndroidXmlAutoEditStrategyTest extends AdtProjectTest {
checkInsertNewline(
"\n" +
" <item>^</item>\n",
+
"\n" +
" <item>\n" +
" ^\n" +
@@ -201,6 +212,7 @@ public class AndroidXmlAutoEditStrategyTest extends AdtProjectTest {
"\n" +
" <foo\n" +
" name='value'>^</foo>\n",
+
"\n" +
" <foo\n" +
" name='value'>\n" +
@@ -214,6 +226,7 @@ public class AndroidXmlAutoEditStrategyTest extends AdtProjectTest {
"\n" +
" <foo\n" +
" name='value'/>^\n",
+
"\n" +
" <foo\n" +
" name='value'/>\n" +
@@ -225,6 +238,7 @@ public class AndroidXmlAutoEditStrategyTest extends AdtProjectTest {
"\n" +
" <foo\n" +
" name='value'></foo>^\n",
+
"\n" +
" <foo\n" +
" name='value'></foo>\n" +
@@ -243,6 +257,7 @@ public class AndroidXmlAutoEditStrategyTest extends AdtProjectTest {
"\n" +
" <foo\n" +
" name='value'><bar></bar><baz/></foo>^\n",
+
"\n" +
" <foo\n" +
" name='value'><bar></bar><baz/></foo>\n" +
@@ -257,6 +272,7 @@ public class AndroidXmlAutoEditStrategyTest extends AdtProjectTest {
" android:layout_height=\"wrap_content\"\n" +
" android:text=\"Button\" >^\n" +
" </Button>\n",
+
" <Button\n" +
" android:id=\"@+id/button1\"\n" +
" android:layout_width=\"wrap_content\"\n" +
@@ -270,12 +286,137 @@ public class AndroidXmlAutoEditStrategyTest extends AdtProjectTest {
checkInsertNewline(
" <Button\n" +
" attr=\"value\"></Button>^\n",
+
" <Button\n" +
" attr=\"value\"></Button>\n" +
" ^\n" +
"");
}
+ public void testLineBeginning1() throws Exception {
+ // Test that if you insert on a blank line, we just add a newline and indent
+ checkInsertNewline(
+ "<foo>\n" +
+ "^\n" +
+ "</foo>",
+
+ "<foo>\n" +
+ "\n" +
+ " ^\n" +
+ "</foo>");
+ }
+
+ public void testLineBeginning2() throws Exception {
+ // Test that if you insert with the caret on the beginning of a line that has
+ // content, we insert an indent correctly
+ checkInsertNewline(
+ "<foo>\n" +
+ "^ <bar/>\n" +
+ "</foo>",
+
+ "<foo>\n" +
+ "\n" +
+ " ^<bar/>\n" +
+ "</foo>");
+ }
+
+ public void testLineBeginning3() throws Exception {
+ checkInsertNewline(
+ "<foo>\n" +
+ " <bar>\n" +
+ "^\n" +
+ " <baz/>\n" +
+ " </bar>\n" +
+ "</foo>",
+
+ "<foo>\n" +
+ " <bar>\n" +
+ "\n" +
+ " ^\n" +
+ " <baz/>\n" +
+ " </bar>\n" +
+ "</foo>");
+
+ }
+
+ public void testLineBeginning4() throws Exception {
+ // Test that if you insert with the caret on the beginning of a line that has
+ // content, we insert an indent correctly
+ checkInsertNewline(
+ "<foo>\n" +
+ " <bar>\n" +
+ "\n" +
+ "^ <baz/>\n" +
+ " </bar>\n" +
+ "</foo>",
+
+ "<foo>\n" +
+ " <bar>\n" +
+ "\n" +
+ "\n" +
+ " ^<baz/>\n" +
+ " </bar>\n" +
+ "</foo>");
+ }
+
+ public void testLineBeginning5() throws Exception {
+ // Test that if you insert with the caret on the beginning of a line that has
+ // content, we insert an indent correctly
+ checkInsertNewline(
+ "<foo>\n" +
+ " <bar>\n" +
+ "\n" +
+ " ^ <baz/>\n" +
+ " </bar>\n" +
+ "</foo>",
+
+ "<foo>\n" +
+ " <bar>\n" +
+ "\n" +
+ " \n" +
+ " ^<baz/>\n" +
+ " </bar>\n" +
+ "</foo>");
+ }
+
+ public void testLineBeginning6() throws Exception {
+
+ checkInsertNewline(
+ " <foo>\n" +
+ " <bar>\n" +
+ " \n" +
+ " \n" +
+ "^ </bar>\n" +
+ " </foo>\n",
+
+ " <foo>\n" +
+ " <bar>\n" +
+ " \n" +
+ " \n" +
+ "\n" +
+ " ^</bar>\n" +
+ " </foo>\n");
+ }
+
+ public void testBlankContinuation() throws Exception {
+
+ checkInsertNewline(
+ " <foo>\n" +
+ " <bar>\n" +
+ " ^\n" +
+ " </bar>\n" +
+ " </foo>\n" +
+ "",
+
+ " <foo>\n" +
+ " <bar>\n" +
+ " \n" +
+ " ^\n" +
+ " </bar>\n" +
+ " </foo>\n" +
+ "");
+ }
+
/**
* To test
* When you press / after < I should reindent the current line. For example,
diff --git a/files/ant/build.xml b/files/ant/build.xml
index ca4cfc9..638eb3c 100644
--- a/files/ant/build.xml
+++ b/files/ant/build.xml
@@ -545,7 +545,8 @@
androidjar="${android.jar}"
rfolder="${gen.absolute.dir}"
projectLibrariesResName="project.libraries.res"
- projectLibrariesPackageName="project.libraries.package">
+ projectLibrariesPackageName="project.libraries.package"
+ restricttouchedextensionsto="xml">
<res path="${resource.absolute.dir}" />
</aapt>
</do-only-if-manifest-hasCode>
diff --git a/hierarchyviewer/src/com/android/hierarchyviewer/HierarchyViewer.java b/hierarchyviewer/src/com/android/hierarchyviewer/HierarchyViewer.java
index 59ce67f..e945987 100644
--- a/hierarchyviewer/src/com/android/hierarchyviewer/HierarchyViewer.java
+++ b/hierarchyviewer/src/com/android/hierarchyviewer/HierarchyViewer.java
@@ -27,6 +27,12 @@ public class HierarchyViewer {
private static final CharSequence OS_WINDOWS = "Windows";
private static final CharSequence OS_MACOSX = "Mac OS X";
+ private static boolean sProfilingEnabled = true;
+
+ public static boolean isProfilingEnabled() {
+ return sProfilingEnabled;
+ }
+
private static void initUserInterface() {
System.setProperty("apple.laf.useScreenMenuBar", "true");
System.setProperty("apple.awt.brushMetalLook", "true");
@@ -52,6 +58,10 @@ public class HierarchyViewer {
}
public static void main(String[] args) {
+ if (args.length > 0) {
+ sProfilingEnabled = !args[0].equalsIgnoreCase("-profiling=false");
+ }
+
initUserInterface();
DeviceBridge.initDebugBridge();
diff --git a/hierarchyviewer/src/com/android/hierarchyviewer/scene/ProfilesLoader.java b/hierarchyviewer/src/com/android/hierarchyviewer/scene/ProfilesLoader.java
index b91db79..4b9d524 100644
--- a/hierarchyviewer/src/com/android/hierarchyviewer/scene/ProfilesLoader.java
+++ b/hierarchyviewer/src/com/android/hierarchyviewer/scene/ProfilesLoader.java
@@ -17,6 +17,7 @@
package com.android.hierarchyviewer.scene;
import com.android.ddmlib.IDevice;
+import com.android.hierarchyviewer.HierarchyViewer;
import com.android.hierarchyviewer.device.Window;
import com.android.hierarchyviewer.device.DeviceBridge;
@@ -30,6 +31,10 @@ import java.io.InputStreamReader;
public class ProfilesLoader {
public static double[] loadProfiles(IDevice device, Window window, String params) {
+ if (!HierarchyViewer.isProfilingEnabled()) {
+ return new double[] { 0.0, 0.0, 0.0 };
+ }
+
Socket socket = null;
BufferedReader in = null;
BufferedWriter out = null;
diff --git a/ide_common/src/com/android/ide/common/resources/IdGeneratingResourceFile.java b/ide_common/src/com/android/ide/common/resources/IdGeneratingResourceFile.java
index fa8d0e7..6706715 100644
--- a/ide_common/src/com/android/ide/common/resources/IdGeneratingResourceFile.java
+++ b/ide_common/src/com/android/ide/common/resources/IdGeneratingResourceFile.java
@@ -31,6 +31,7 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
+import java.util.Set;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
@@ -88,8 +89,8 @@ public final class IdGeneratingResourceFile extends ResourceFile
@Override
protected void update() {
- // remove this file from all existing ResourceItem.
- getFolder().getRepository().removeFile(mResourceTypeList, this);
+ // Copy the previous list of ID names
+ Set<String> oldIdNames = mIdResources.keySet();
// reset current content.
mIdResources.clear();
@@ -97,14 +98,21 @@ public final class IdGeneratingResourceFile extends ResourceFile
// need to parse the file and find the IDs.
parseFileForIds();
- // Notify the repository about any changes
- updateResourceItems();
+ // We only need to update the repository if our IDs have changed
+ if (oldIdNames.equals(mIdResources.keySet()) == false) {
+ updateResourceItems();
+ }
}
@Override
protected void dispose() {
+ ResourceRepository repository = getRepository();
+
// Remove declarations from this file from the repository
- getFolder().getRepository().removeFile(mResourceTypeList, this);
+ repository.removeFile(mResourceTypeList, this);
+
+ // Ask for an ID refresh since we'll be taking away ID generating items
+ repository.markForIdRefresh();
}
@Override
@@ -155,6 +163,9 @@ public final class IdGeneratingResourceFile extends ResourceFile
private void updateResourceItems() {
ResourceRepository repository = getRepository();
+ // remove this file from all existing ResourceItem.
+ repository.removeFile(mResourceTypeList, this);
+
// First add this as a layout file
ResourceItem item = repository.getResourceItem(mFileType, mFileName);
item.add(this);
@@ -165,6 +176,9 @@ public final class IdGeneratingResourceFile extends ResourceFile
// add this file to the list of files generating ID resources.
item.add(this);
}
+
+ // Ask the repository for an ID refresh
+ repository.markForIdRefresh();
}
/**
diff --git a/ide_common/src/com/android/ide/common/resources/MultiResourceFile.java b/ide_common/src/com/android/ide/common/resources/MultiResourceFile.java
index 6d8ca0a..b3e35d9 100644
--- a/ide_common/src/com/android/ide/common/resources/MultiResourceFile.java
+++ b/ide_common/src/com/android/ide/common/resources/MultiResourceFile.java
@@ -54,6 +54,10 @@ public final class MultiResourceFile extends ResourceFile implements IValueResou
super(file, folder);
}
+ // Boolean flag to track whether a named element has been added or removed, thus requiring
+ // a new ID table to be generated
+ private boolean mNeedIdRefresh;
+
@Override
protected void load() {
// need to parse the file and find the content.
@@ -62,14 +66,21 @@ public final class MultiResourceFile extends ResourceFile implements IValueResou
// create new ResourceItems for the new content.
mResourceTypeList = Collections.unmodifiableCollection(mResourceItems.keySet());
+ // We need an ID generation step
+ mNeedIdRefresh = true;
+
// create/update the resource items.
updateResourceItems();
}
@Override
protected void update() {
- // remove this file from all existing ResourceItem.
- getFolder().getRepository().removeFile(mResourceTypeList, this);
+ // Reset the ID generation flag
+ mNeedIdRefresh = false;
+
+ // Copy the previous version of our list of ResourceItems and types
+ Map<ResourceType, Map<String, ResourceValue>> oldResourceItems
+ = new EnumMap<ResourceType, Map<String, ResourceValue>>(mResourceItems);
// reset current content.
mResourceItems.clear();
@@ -80,14 +91,34 @@ public final class MultiResourceFile extends ResourceFile implements IValueResou
// create new ResourceItems for the new content.
mResourceTypeList = Collections.unmodifiableCollection(mResourceItems.keySet());
+ // Check to see if any names have changed. If so, mark the flag so updateResourceItems
+ // can notify the ResourceRepository that an ID refresh is needed
+ if (oldResourceItems.keySet().equals(mResourceItems.keySet())) {
+ for (ResourceType type : mResourceTypeList) {
+ // We just need to check the names of the items.
+ // If there are new or removed names then we'll have to regenerate IDs
+ if (mResourceItems.get(type).keySet()
+ .equals(oldResourceItems.get(type).keySet()) == false) {
+ mNeedIdRefresh = true;
+ }
+ }
+ } else {
+ // If our type list is different, obviously the names will be different
+ mNeedIdRefresh = true;
+ }
// create/update the resource items.
updateResourceItems();
}
@Override
protected void dispose() {
+ ResourceRepository repository = getRepository();
+
// only remove this file from all existing ResourceItem.
- getFolder().getRepository().removeFile(mResourceTypeList, this);
+ repository.removeFile(mResourceTypeList, this);
+
+ // We'll need an ID refresh because we deleted items
+ repository.markForIdRefresh();
// don't need to touch the content, it'll get reclaimed as this objects disappear.
// In the mean time other objects may need to access it.
@@ -106,6 +137,10 @@ public final class MultiResourceFile extends ResourceFile implements IValueResou
private void updateResourceItems() {
ResourceRepository repository = getRepository();
+
+ // remove this file from all existing ResourceItem.
+ repository.removeFile(mResourceTypeList, this);
+
for (ResourceType type : mResourceTypeList) {
Map<String, ResourceValue> list = mResourceItems.get(type);
@@ -119,6 +154,11 @@ public final class MultiResourceFile extends ResourceFile implements IValueResou
}
}
}
+
+ // If we need an ID refresh, ask the repository for that now
+ if (mNeedIdRefresh) {
+ repository.markForIdRefresh();
+ }
}
/**
diff --git a/ide_common/src/com/android/ide/common/resources/ResourceRepository.java b/ide_common/src/com/android/ide/common/resources/ResourceRepository.java
index fa533cb..4af4a1a 100644
--- a/ide_common/src/com/android/ide/common/resources/ResourceRepository.java
+++ b/ide_common/src/com/android/ide/common/resources/ResourceRepository.java
@@ -68,6 +68,7 @@ public abstract class ResourceRepository {
protected final IntArrayWrapper mWrapper = new IntArrayWrapper(null);
+ private boolean mNeedsIdRefresh;
/**
* Makes a resource repository
@@ -195,6 +196,29 @@ public abstract class ResourceRepository {
protected abstract ResourceItem createResourceItem(String name);
/**
+ * Sets a flag which determines whether aapt needs to be run to regenerate resource IDs
+ */
+ protected void markForIdRefresh() {
+ mNeedsIdRefresh = true;
+ }
+
+ /**
+ * Returns whether this repository has been marked as "dirty"; if one or more of the constituent
+ * files have declared that the resource item names that they provide have changed.
+ */
+ public boolean needsIdRefresh() {
+ return mNeedsIdRefresh;
+ }
+
+ /**
+ * Indicates that the resources IDs have been regenerated, so the repository is now in a clean
+ * state
+ */
+ public void setIdsRefreshed() {
+ mNeedsIdRefresh = false;
+ }
+
+ /**
* Processes a folder and adds it to the list of existing folders.
* @param folder the folder to process
* @return the ResourceFolder created from this folder, or null if the process failed.
diff --git a/ide_common/src/com/android/ide/common/resources/SingleResourceFile.java b/ide_common/src/com/android/ide/common/resources/SingleResourceFile.java
index 9c8977e..b589b35 100644
--- a/ide_common/src/com/android/ide/common/resources/SingleResourceFile.java
+++ b/ide_common/src/com/android/ide/common/resources/SingleResourceFile.java
@@ -41,8 +41,8 @@ public class SingleResourceFile extends ResourceFile {
sParserFactory.setNamespaceAware(true);
}
- private String mResourceName;
- private ResourceType mType;
+ private final String mResourceName;
+ private final ResourceType mType;
private ResourceValue mValue;
public SingleResourceFile(IAbstractFile file, ResourceFolder folder) {
@@ -79,6 +79,9 @@ public class SingleResourceFile extends ResourceFile {
// add this file to the list of files generating this resource item.
item.add(this);
+
+ // Ask for an ID refresh since we're adding an item that will generate an ID
+ getRepository().markForIdRefresh();
}
@Override
@@ -92,6 +95,9 @@ public class SingleResourceFile extends ResourceFile {
// only remove this file from the existing ResourceItem.
getFolder().getRepository().removeFile(mType, this);
+ // Ask for an ID refresh since we're removing an item that previously generated an ID
+ getRepository().markForIdRefresh();
+
// don't need to touch the content, it'll get reclaimed as this objects disappear.
// In the mean time other objects may need to access it.
}
diff --git a/sdkmanager/app/tests/com/android/sdkmanager/AvdManagerTest.java b/sdkmanager/app/tests/com/android/sdkmanager/AvdManagerTest.java
index a5a8289..93b12c1 100644
--- a/sdkmanager/app/tests/com/android/sdkmanager/AvdManagerTest.java
+++ b/sdkmanager/app/tests/com/android/sdkmanager/AvdManagerTest.java
@@ -19,6 +19,7 @@ package com.android.sdkmanager;
import com.android.io.FileWrapper;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.SdkConstants;
+import com.android.sdklib.SdkManagerTestCase;
import com.android.sdklib.internal.avd.AvdInfo;
import com.android.sdklib.internal.project.ProjectProperties;
diff --git a/sdkmanager/app/tests/com/android/sdkmanager/MainTest.java b/sdkmanager/app/tests/com/android/sdkmanager/MainTest.java
index 4a17e32..ef5a715 100644
--- a/sdkmanager/app/tests/com/android/sdkmanager/MainTest.java
+++ b/sdkmanager/app/tests/com/android/sdkmanager/MainTest.java
@@ -19,6 +19,7 @@ package com.android.sdkmanager;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.SdkConstants;
+import com.android.sdklib.SdkManagerTestCase;
import com.android.sdklib.internal.avd.AvdInfo;
import com.android.sdklib.repository.SdkAddonConstants;
import com.android.sdklib.repository.SdkRepoConstants;
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/PlatformTarget.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/PlatformTarget.java
index 467529d..b8226c0 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/PlatformTarget.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/PlatformTarget.java
@@ -16,6 +16,7 @@
package com.android.sdklib;
+import com.android.sdklib.SdkManager.LayoutlibVersion;
import com.android.sdklib.util.SparseArray;
import java.io.File;
@@ -45,22 +46,31 @@ final class PlatformTarget implements IAndroidTarget {
private String[] mSkins;
private String[] mAbis;
private boolean mAbiCompatibilityMode;
-
+ private final LayoutlibVersion mLayoutlibVersion;
/**
* Creates a Platform target.
+ *
* @param sdkOsPath the root folder of the SDK
* @param platformOSPath the root folder of the platform component
* @param apiLevel the API Level
* @param codeName the codename. can be null.
* @param versionName the version name of the platform.
* @param revision the revision of the platform component.
+ * @param layoutlibVersion The {@link LayoutlibVersion}. May be null.
* @param abis the list of supported abis
* @param properties the platform properties
*/
@SuppressWarnings("deprecation")
- PlatformTarget(String sdkOsPath, String platformOSPath, int apiLevel,
- String codeName, String versionName, int revision, String[] abis,
+ PlatformTarget(
+ String sdkOsPath,
+ String platformOSPath,
+ int apiLevel,
+ String codeName,
+ String versionName,
+ int revision,
+ LayoutlibVersion layoutlibVersion,
+ String[] abis,
Map<String, String> properties) {
if (platformOSPath.endsWith(File.separator) == false) {
platformOSPath = platformOSPath + File.separator;
@@ -70,6 +80,7 @@ final class PlatformTarget implements IAndroidTarget {
mVersion = new AndroidVersion(apiLevel, codeName);
mVersionName = versionName;
mRevision = revision;
+ mLayoutlibVersion = layoutlibVersion;
if (mVersion.isPreview()) {
mName = String.format(PLATFORM_NAME_PREVIEW, mVersionName);
@@ -123,7 +134,13 @@ final class PlatformTarget implements IAndroidTarget {
mAbiCompatibilityMode = true;
mAbis = new String[] { SdkConstants.ABI_ARMEABI };
}
+ }
+ /**
+ * Returns the {@link LayoutlibVersion}. May be null.
+ */
+ public LayoutlibVersion getLayoutlibVersion() {
+ return mLayoutlibVersion;
}
/**
@@ -151,9 +168,9 @@ final class PlatformTarget implements IAndroidTarget {
return mRootFolderOsPath;
}
- /*
- * (non-Javadoc)
- *
+ /**
+ * {@inheritDoc}
+ * <p/>
* For Platform, the vendor name is always "Android".
*
* @see com.android.sdklib.IAndroidTarget#getVendor()
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java
index 43e9ac9..987ae21 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java
@@ -85,8 +85,35 @@ public class SdkManager {
/** The location of the SDK as an OS path */
private final String mOsSdkPath;
- /** Valid targets that have been loaded. */
- private IAndroidTarget[] mTargets;
+ /** Valid targets that have been loaded. Can be empty but not null. */
+ private IAndroidTarget[] mTargets = new IAndroidTarget[0];
+
+ public static class LayoutlibVersion implements Comparable<LayoutlibVersion> {
+ private final int mApi;
+ private final int mRevision;
+
+ public static final int NOT_SPECIFIED = 0;
+
+ public LayoutlibVersion(int api, int revision) {
+ mApi = api;
+ mRevision = revision;
+ }
+
+ public int getApi() {
+ return mApi;
+ }
+
+ public int getRevision() {
+ return mRevision;
+ }
+
+ public int compareTo(LayoutlibVersion rhs) {
+ boolean useRev = this.mRevision > NOT_SPECIFIED && rhs.mRevision > NOT_SPECIFIED;
+ int lhsValue = (this.mApi << 16) + (useRev ? this.mRevision : 0);
+ int rhsValue = (rhs.mApi << 16) + (useRev ? rhs.mRevision : 0);
+ return lhsValue - rhsValue;
+ }
+ }
/**
* Create a new {@link SdkManager} instance.
@@ -232,6 +259,39 @@ public class SdkManager {
}
/**
+ * Returns the greatest {@link LayoutlibVersion} found amongst all platform
+ * targets currently loaded in the SDK.
+ * <p/>
+ * We only started recording Layoutlib Versions recently in the platform meta data
+ * so it's possible to have an SDK with many platforms loaded but no layoutlib
+ * version defined.
+ *
+ * @return The greatest {@link LayoutlibVersion} or null if none is found.
+ * @deprecated This helper method is provisional. I am marking it as deprecated for
+ * lack of a better tag (e.g. "@future"?). It's deprecated in the sense that
+ * we're not using it yet and should NOT be considered a stable API yet.
+ * We'll probably need to revisit it when the want to actually use it.
+ * If it's convenient as-is then this deprecation message shall be removed.
+ */
+ @Deprecated
+ public LayoutlibVersion getMaxLayoutlibVersion() {
+ LayoutlibVersion maxVersion = null;
+
+ for (IAndroidTarget target : getTargets()) {
+ if (target instanceof PlatformTarget) {
+ LayoutlibVersion lv = ((PlatformTarget) target).getLayoutlibVersion();
+ if (lv != null) {
+ if (maxVersion == null || lv.compareTo(maxVersion) > 0) {
+ maxVersion = lv;
+ }
+ }
+ }
+ }
+
+ return maxVersion;
+ }
+
+ /**
* Loads the Platforms from the SDK.
* Creates the "platforms" folder if necessary.
*
@@ -331,18 +391,40 @@ public class SdkManager {
// codename is irrelevant at this point.
}
- // platform rev number
+ // platform rev number & layoutlib version are extracted from the source.properties
+ // saved by the SDK Manager when installing the package.
+
int revision = 1;
- FileWrapper sourcePropFile = new FileWrapper(platformFolder,
- SdkConstants.FN_SOURCE_PROP);
+ LayoutlibVersion layoutlibVersion = null;
+
+ FileWrapper sourcePropFile =
+ new FileWrapper(platformFolder, SdkConstants.FN_SOURCE_PROP);
+
Map<String, String> sourceProp = ProjectProperties.parsePropertyFile(
sourcePropFile, log);
+
if (sourceProp != null) {
try {
revision = Integer.parseInt(sourceProp.get("Pkg.Revision")); //$NON-NLS-1$
} catch (NumberFormatException e) {
// do nothing, we'll keep the default value of 1.
}
+
+ try {
+ String propApi = sourceProp.get("Layoutlib.Api"); //$NON-NLS-1$
+ String propRev = sourceProp.get("Layoutlib.Revision"); //$NON-NLS-1$
+ int llApi = propApi == null ? LayoutlibVersion.NOT_SPECIFIED :
+ Integer.parseInt(propApi);
+ int llRev = propRev == null ? LayoutlibVersion.NOT_SPECIFIED :
+ Integer.parseInt(propRev);
+ if (llApi > LayoutlibVersion.NOT_SPECIFIED &&
+ llRev >= LayoutlibVersion.NOT_SPECIFIED) {
+ layoutlibVersion = new LayoutlibVersion(llApi, llRev);
+ }
+ } catch (NumberFormatException e) {
+ // do nothing, we'll ignore the layoutlib version if it's invalid
+ }
+
map.putAll(sourceProp);
}
@@ -371,6 +453,7 @@ public class SdkManager {
apiCodename,
apiName,
revision,
+ layoutlibVersion,
abiList,
map);
diff --git a/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/LayoutlibVersionTest.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/LayoutlibVersionTest.java
new file mode 100755
index 0000000..0c197aa
--- /dev/null
+++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/LayoutlibVersionTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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.sdklib;
+
+import com.android.sdklib.SdkManager.LayoutlibVersion;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit test for {@link LayoutlibVersion}.
+ */
+public class LayoutlibVersionTest extends TestCase {
+
+ public void testLayoutlibVersion_create() {
+ LayoutlibVersion lv = new LayoutlibVersion(1, 2);
+ assertEquals(1, lv.getApi());
+ assertEquals(2, lv.getRevision());
+ }
+
+ public void testLayoutlibVersion_compare() {
+ assertTrue(new LayoutlibVersion(1, 1).compareTo(new LayoutlibVersion(1, 1)) == 0);
+ assertTrue(new LayoutlibVersion(1, 2).compareTo(new LayoutlibVersion(1, 1)) > 0);
+ assertTrue(new LayoutlibVersion(1, 1).compareTo(new LayoutlibVersion(1, 2)) < 0);
+ assertTrue(new LayoutlibVersion(2, 2).compareTo(new LayoutlibVersion(1, 3)) > 0);
+
+ // the lack of an API (== 0) naturally sorts as the lowest value possible.
+ assertTrue(new LayoutlibVersion(0, 1).compareTo(new LayoutlibVersion(0, 2)) < 0);
+ assertTrue(new LayoutlibVersion(0, 1).compareTo(new LayoutlibVersion(1, 2)) < 0);
+ assertTrue(new LayoutlibVersion(0, 3).compareTo(new LayoutlibVersion(1, 2)) < 0);
+ assertTrue(new LayoutlibVersion(1, 2).compareTo(new LayoutlibVersion(0, 3)) > 0);
+
+ // if we lack the revision number, we don't use it in comparison
+ assertTrue(new LayoutlibVersion(2, 0).compareTo(new LayoutlibVersion(2, 2)) == 0);
+ assertTrue(new LayoutlibVersion(2, 2).compareTo(new LayoutlibVersion(2, 0)) == 0);
+ }
+
+}
diff --git a/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/SdkManagerTest.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/SdkManagerTest.java
new file mode 100755
index 0000000..bf8600c
--- /dev/null
+++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/SdkManagerTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.sdklib;
+
+
+import com.android.sdklib.SdkManager.LayoutlibVersion;
+
+public class SdkManagerTest extends SdkManagerTestCase {
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ public void testSdkManager_LayoutlibVersion() {
+ SdkManager sdkman = getSdkManager();
+ IAndroidTarget t = sdkman.getTargets()[0];
+
+ assertTrue(t instanceof PlatformTarget);
+
+ LayoutlibVersion lv = ((PlatformTarget) t).getLayoutlibVersion();
+ assertNotNull(lv);
+ assertEquals(5, lv.getApi());
+ assertEquals(2, lv.getRevision());
+
+ assertSame(lv, sdkman.getMaxLayoutlibVersion());
+ }
+}
diff --git a/sdkmanager/app/tests/com/android/sdkmanager/SdkManagerTestCase.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/SdkManagerTestCase.java
index 9fdd852..e4feacf 100755
--- a/sdkmanager/app/tests/com/android/sdkmanager/SdkManagerTestCase.java
+++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/SdkManagerTestCase.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.sdkmanager;
+package com.android.sdklib;
import com.android.prefs.AndroidLocation;
@@ -126,8 +126,10 @@ public abstract class SdkManagerTestCase extends TestCase {
*/
private File makeFakeSdk() throws IOException {
+ // First we create a temp file to "reserve" the temp directory name we want to use.
File tmpFile = File.createTempFile(
this.getClass().getSimpleName() + '_' + this.getName(), null);
+ // Then erase the file and make the directory
tmpFile.delete();
tmpFile.mkdirs();
@@ -145,15 +147,24 @@ public abstract class SdkManagerTestCase extends TestCase {
targetDir.mkdirs();
new File(targetDir, SdkConstants.FN_FRAMEWORK_LIBRARY).createNewFile();
new File(targetDir, SdkConstants.FN_FRAMEWORK_AIDL).createNewFile();
- new File(targetDir, SdkConstants.FN_SOURCE_PROP).createNewFile();
+
+ File sourceProp = new File(targetDir, SdkConstants.FN_SOURCE_PROP);
+ sourceProp.createNewFile();
+ FileWriter out = new FileWriter(sourceProp);
+ out.write("Layoutlib.Api=5\n");
+ out.write("Layoutlib.Revision=2\n");
+ out.close();
+
File buildProp = new File(targetDir, SdkConstants.FN_BUILD_PROP);
- FileWriter out = new FileWriter(buildProp);
+ out = new FileWriter(buildProp);
out.write(SdkManager.PROP_VERSION_RELEASE + "=0.0\n");
out.write(SdkManager.PROP_VERSION_SDK + "=0\n");
out.write(SdkManager.PROP_VERSION_CODENAME + "=REL\n");
out.close();
+
File imagesDir = new File(targetDir, "images");
imagesDir.mkdirs();
+
new File(imagesDir, "userdata.img").createNewFile();
File skinsDir = new File(targetDir, "skins");
File hvgaDir = new File(skinsDir, "HVGA");
diff --git a/traceview/src/com/android/traceview/DmTraceReader.java b/traceview/src/com/android/traceview/DmTraceReader.java
index ac44a09..d75ba8c 100644
--- a/traceview/src/com/android/traceview/DmTraceReader.java
+++ b/traceview/src/com/android/traceview/DmTraceReader.java
@@ -20,8 +20,8 @@ import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
-import java.io.FileReader;
import java.io.IOException;
+import java.io.InputStreamReader;
import java.nio.BufferUnderflowException;
import java.nio.ByteOrder;
import java.nio.MappedByteBuffer;
@@ -404,7 +404,8 @@ public class DmTraceReader extends TraceReader {
long parseKeys() throws IOException {
BufferedReader in = null;
try {
- in = new BufferedReader(new FileReader(mTraceFileName));
+ in = new BufferedReader(new InputStreamReader(
+ new FileInputStream(mTraceFileName), "US-ASCII"));
} catch (FileNotFoundException ex) {
System.err.println(ex.getMessage());
}