aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--anttasks/src/com/android/ant/AaptExecTask.java38
-rw-r--r--apigenerator/src/com/android/apigenerator/AndroidJarReader.java253
-rw-r--r--apigenerator/src/com/android/apigenerator/ApiClass.java32
-rw-r--r--apigenerator/src/com/android/apigenerator/ApiParseException.java59
-rw-r--r--apigenerator/src/com/android/apigenerator/EnumParser.java147
-rw-r--r--apigenerator/src/com/android/apigenerator/Main.java99
-rw-r--r--apigenerator/src/com/android/apigenerator/NewApiParser.java619
-rw-r--r--apigenerator/src/com/android/apigenerator/ParserState.java174
-rw-r--r--apigenerator/src/com/android/apigenerator/XmlApiParser.java126
-rw-r--r--apigenerator/src/com/android/apigenerator/enumfix/AndroidJarReader.java132
-rw-r--r--apigenerator/src/com/android/apigenerator/enums.xml596
-rw-r--r--eclipse/dictionary.txt2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/Hyperlinks.java71
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/ElementDescriptor.java13
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditorMatchingStrategy.java9
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/CustomViewDescriptorService.java104
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasTransform.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ImageOverlay.java64
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlinePage.java3
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/uimodel/UiViewElementNode.java3
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/ActivityPage.java20
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectWizard.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplatePage.java40
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplateWizard.java6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplateWizardState.java14
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/Parameter.java36
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/TemplateHandler.java37
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/TemplateMetadata.java145
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/template.xml25
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/templates/MasterDetailFlow/template.xml7
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/templates/NewAndroidApplication/template.xml7
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/HyperlinksTest.java8
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigation1-expected-navigate15.txt7
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigation1.xml1
-rw-r--r--files/ant/build.xml4
-rw-r--r--files/proguard-android.txt29
-rw-r--r--templates/build.template16
-rw-r--r--testapps/customPropAnimTest/.classpath8
-rw-r--r--testapps/customPropAnimTest/.project33
-rw-r--r--testapps/customPropAnimTest/AndroidManifest.xml23
-rw-r--r--testapps/customPropAnimTest/build.xml90
-rw-r--r--testapps/customPropAnimTest/proguard-project.txt20
-rw-r--r--testapps/customPropAnimTest/project.properties15
-rw-r--r--testapps/customPropAnimTest/res/drawable-hdpi/ic_launcher.pngbin0 -> 9397 bytes
-rw-r--r--testapps/customPropAnimTest/res/drawable-ldpi/ic_launcher.pngbin0 -> 2729 bytes
-rw-r--r--testapps/customPropAnimTest/res/drawable-mdpi/ic_launcher.pngbin0 -> 5237 bytes
-rw-r--r--testapps/customPropAnimTest/res/drawable-xhdpi/ic_launcher.pngbin0 -> 14383 bytes
-rw-r--r--testapps/customPropAnimTest/res/layout/main.xml8
-rw-r--r--testapps/customPropAnimTest/res/values/strings.xml7
-rw-r--r--testapps/customPropAnimTest/src/com/android/custompropertyanimation/CustomPropertyAnimationActivity.java22
-rw-r--r--testapps/customPropAnimTest/src/com/android/custompropertyanimation/MyView.java24
-rw-r--r--testapps/customViewTest/libWithCustomView/build.xml39
-rw-r--r--testapps/customViewTest/mainProject/build.xml39
-rw-r--r--testapps/customViewTest/mainProject/proguard.cfg40
-rw-r--r--testapps/customViewTest/mainProject/project.properties3
-rw-r--r--testapps/customViewTest/mainProject/res/values/strings.xml2
-rw-r--r--testapps/libsAndJarTest/app/build.xml11
-rw-r--r--testapps/libsAndJarTest/app/project.properties6
-rw-r--r--testapps/libsAndJarTest/lib1/build.xml11
-rw-r--r--testapps/libsAndJarTest/lib2/build.xml11
60 files changed, 1120 insertions, 2242 deletions
diff --git a/anttasks/src/com/android/ant/AaptExecTask.java b/anttasks/src/com/android/ant/AaptExecTask.java
index 2b57277..c96f7d0 100644
--- a/anttasks/src/com/android/ant/AaptExecTask.java
+++ b/anttasks/src/com/android/ant/AaptExecTask.java
@@ -97,6 +97,7 @@ public final class AaptExecTask extends SingleDependencyTask {
private String mLibraryPackagesRefid;
private boolean mNonConstantId;
private String mIgnoreAssets;
+ private String mProguardFile;
/**
* Input path that ignores the same folders/files that aapt does.
@@ -321,6 +322,10 @@ public final class AaptExecTask extends SingleDependencyTask {
mLibraryPackagesRefid = libraryPackagesRefid;
}
+ public void setProguardFile(Path proguardFile) {
+ mProguardFile = TaskHelper.checkSinglePath("proguardFile", proguardFile);
+ }
+
/**
* Returns an object representing a nested <var>nocompress</var> element.
*/
@@ -344,6 +349,11 @@ public final class AaptExecTask extends SingleDependencyTask {
return path;
}
+ @Override
+ protected String getExecTaskName() {
+ return "aapt";
+ }
+
/*
* (non-Javadoc)
*
@@ -375,24 +385,6 @@ public final class AaptExecTask extends SingleDependencyTask {
libPkgProp = libPkgProp.replace(';', ':');
}
}
- // Call aapt. If there are libraries, we'll pass a non-null string of libs.
- callAapt(libPkgProp);
- }
-
- @Override
- protected String getExecTaskName() {
- return "aapt";
- }
-
- /**
- * Calls aapt with the given parameters.
- * @param resourceFilter the resource configuration filter to pass to aapt (if configName is
- * non null)
- * @param extraPackages an optional list of colon-separated packages. Can be null
- * Ex: com.foo.one:com.foo.two:com.foo.lib
- */
- private void callAapt(String extraPackages) {
- Project taskProject = getProject();
final boolean generateRClass = mRFolder != null && new File(mRFolder).isDirectory();
@@ -538,9 +530,9 @@ public final class AaptExecTask extends SingleDependencyTask {
}
}
- if (mNonConstantId == false && extraPackages != null && extraPackages.length() > 0) {
+ if (mNonConstantId == false && libPkgProp != null && libPkgProp.length() > 0) {
task.createArg().setValue("--extra-packages");
- task.createArg().setValue(extraPackages);
+ task.createArg().setValue(libPkgProp);
}
// if the project contains libraries, force auto-add-overlay
@@ -635,6 +627,12 @@ public final class AaptExecTask extends SingleDependencyTask {
// Use dependency generation
task.createArg().setValue("--generate-dependencies");
+ // use the proguard file
+ if (mProguardFile != null && mProguardFile.length() > 0) {
+ task.createArg().setValue("-G");
+ task.createArg().setValue(mProguardFile);
+ }
+
// final setup of the task
task.setProject(taskProject);
task.setOwningTarget(getOwningTarget());
diff --git a/apigenerator/src/com/android/apigenerator/AndroidJarReader.java b/apigenerator/src/com/android/apigenerator/AndroidJarReader.java
new file mode 100644
index 0000000..89924a5
--- /dev/null
+++ b/apigenerator/src/com/android/apigenerator/AndroidJarReader.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2012 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.apigenerator;
+
+import com.android.util.Pair;
+
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.tree.ClassNode;
+import org.objectweb.asm.tree.FieldNode;
+import org.objectweb.asm.tree.MethodNode;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+/**
+ * Reads all the android.jar files found in an SDK and generate a map of {@link ApiClass}.
+ *
+ */
+public class AndroidJarReader {
+
+ private static final byte[] BUFFER = new byte[65535];
+
+ private final String mSdkFolder;
+
+ public AndroidJarReader(String sdkFolder) {
+ mSdkFolder = sdkFolder;
+ }
+
+ public Map<String, ApiClass> getClasses() {
+ HashMap<String, ApiClass> map = new HashMap<String, ApiClass>();
+
+ // Get all the android.jar. They are in platforms-#
+ int apiLevel = 0;
+ while (true) {
+ apiLevel++;
+ try {
+ File jar = new File(mSdkFolder, "platforms/android-" + apiLevel + "/android.jar");
+ if (jar.exists() == false) {
+ System.out.println("Last API level found: " + (apiLevel-1));
+ break;
+ }
+
+ FileInputStream fis = new FileInputStream(jar);
+ ZipInputStream zis = new ZipInputStream(fis);
+ ZipEntry entry = zis.getNextEntry();
+ while (entry != null) {
+ String name = entry.getName();
+
+ if (name.endsWith(".class")) {
+
+ int index = 0;
+ do {
+ int size = zis.read(BUFFER, index, BUFFER.length - index);
+ if (size >= 0) {
+ index += size;
+ } else {
+ break;
+ }
+ } while (true);
+
+ byte[] b = new byte[index];
+ System.arraycopy(BUFFER, 0, b, 0, index);
+
+ ClassReader reader = new ClassReader(b);
+ ClassNode classNode = new ClassNode();
+ reader.accept(classNode, 0 /*flags*/);
+
+ if (classNode != null) {
+ ApiClass theClass = addClass(map, classNode.name, apiLevel);
+
+ // super class
+ if (classNode.superName != null) {
+ theClass.addSuperClass(classNode.superName, apiLevel);
+ }
+
+ // interfaces
+ for (Object interfaceName : classNode.interfaces) {
+ theClass.addInterface((String) interfaceName, apiLevel);
+ }
+
+ // fields
+ for (Object field : classNode.fields) {
+ FieldNode fieldNode = (FieldNode) field;
+ if ((fieldNode.access & Opcodes.ACC_PRIVATE) != 0) {
+ continue;
+ }
+ if (fieldNode.name.startsWith("this$") == false &&
+ fieldNode.name.equals("$VALUES") == false) {
+ theClass.addField(fieldNode.name, apiLevel);
+ }
+ }
+
+ // methods
+ for (Object method : classNode.methods) {
+ MethodNode methodNode = (MethodNode) method;
+ if ((methodNode.access & Opcodes.ACC_PRIVATE) != 0) {
+ continue;
+ }
+ if (methodNode.name.equals("<clinit>") == false) {
+ theClass.addMethod(methodNode.name + methodNode.desc, apiLevel);
+ }
+ }
+ }
+ }
+ entry = zis.getNextEntry();
+ }
+
+ } catch (MalformedURLException e) {
+ e.printStackTrace();
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+
+ }
+ }
+
+ postProcessClasses(map);
+
+ return map;
+ }
+
+ private void postProcessClasses(Map<String, ApiClass> classes) {
+ for (ApiClass theClass : classes.values()) {
+ Map<String, Integer> methods = theClass.getMethods();
+ Map<String, Integer> fixedMethods = new HashMap<String, Integer>();
+
+ List<Pair<String, Integer>> superClasses = theClass.getSuperClasses();
+ List<Pair<String, Integer>> interfaces = theClass.getInterfaces();
+
+ methodLoop: for (Entry<String, Integer> method : methods.entrySet()) {
+ String methodName = method.getKey();
+ int apiLevel = method.getValue();
+
+ if (methodName.startsWith("<init>(") == false) {
+
+ for (Pair<String, Integer> parent : superClasses) {
+ // only check the parent if it was a parent class at the introduction
+ // of the method.
+ if (parent.getSecond() <= apiLevel) {
+ ApiClass parentClass = classes.get(parent.getFirst());
+ assert parentClass != null;
+ if (parentClass != null &&
+ checkClassContains(theClass.getName(),
+ methodName, apiLevel,
+ classes, parentClass)) {
+ continue methodLoop;
+ }
+ }
+ }
+
+ for (Pair<String, Integer> parent : interfaces) {
+ // only check the parent if it was a parent class at the introduction
+ // of the method.
+ if (parent.getSecond() <= apiLevel) {
+ ApiClass parentClass = classes.get(parent.getFirst());
+ assert parentClass != null;
+ if (parentClass != null &&
+ checkClassContains(theClass.getName(),
+ methodName, apiLevel,
+ classes, parentClass)) {
+ continue methodLoop;
+ }
+ }
+ }
+ }
+
+ // if we reach here. the method isn't an override
+ fixedMethods.put(methodName, method.getValue());
+ }
+
+ theClass.replaceMethods(fixedMethods);
+ }
+ }
+
+ private boolean checkClassContains(String className, String methodName, int apiLevel,
+ Map<String, ApiClass> classMap, ApiClass parentClass) {
+
+ Integer parentMethodApiLevel = parentClass.getMethods().get(methodName);
+ if (parentMethodApiLevel != null && parentMethodApiLevel <= apiLevel) {
+ // the parent class has the method and it was introduced in the parent at the
+ // same api level as the method, or before.
+ return true;
+ }
+
+ // check on this class parents.
+ List<Pair<String, Integer>> superClasses = parentClass.getSuperClasses();
+ List<Pair<String, Integer>> interfaces = parentClass.getInterfaces();
+
+ for (Pair<String, Integer> parent : superClasses) {
+ // only check the parent if it was a parent class at the introduction
+ // of the method.
+ if (parent.getSecond() <= apiLevel) {
+ ApiClass superParentClass = classMap.get(parent.getFirst());
+ assert superParentClass != null;
+ if (superParentClass != null && checkClassContains(className, methodName, apiLevel,
+ classMap, superParentClass)) {
+ return true;
+ }
+ }
+ }
+
+ for (Pair<String, Integer> parent : interfaces) {
+ // only check the parent if it was a parent class at the introduction
+ // of the method.
+ if (parent.getSecond() <= apiLevel) {
+ ApiClass superParentClass = classMap.get(parent.getFirst());
+ assert superParentClass != null;
+ if (superParentClass != null && checkClassContains(className, methodName, apiLevel,
+ classMap, superParentClass)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private ApiClass addClass(HashMap<String, ApiClass> classes, String name, int apiLevel) {
+ ApiClass theClass = classes.get(name);
+ if (theClass == null) {
+ theClass = new ApiClass(name, apiLevel);
+ classes.put(name, theClass);
+ }
+
+ return theClass;
+ }
+}
diff --git a/apigenerator/src/com/android/apigenerator/ApiClass.java b/apigenerator/src/com/android/apigenerator/ApiClass.java
index 13b2d42..ccdc075 100644
--- a/apigenerator/src/com/android/apigenerator/ApiClass.java
+++ b/apigenerator/src/com/android/apigenerator/ApiClass.java
@@ -20,6 +20,8 @@ import com.android.util.Pair;
import java.io.PrintStream;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -49,6 +51,10 @@ public class ApiClass {
mSince = since;
}
+ public String getName() {
+ return mName;
+ }
+
int getSince() {
return mSince;
}
@@ -67,18 +73,35 @@ public class ApiClass {
}
}
+ public Map<String, Integer> getMethods() {
+ return mMethods;
+ }
+
+ public void replaceMethods(Map<String, Integer> fixedMethods) {
+ mMethods.clear();
+ mMethods.putAll(fixedMethods);
+ }
+
public void addSuperClass(String superClass, int since) {
addToArray(mSuperClasses, superClass, since);
}
+ public List<Pair<String, Integer>> getSuperClasses() {
+ return mSuperClasses;
+ }
+
public void addInterface(String interfaceClass, int since) {
addToArray(mInterfaces, interfaceClass, since);
}
+ public List<Pair<String, Integer>> getInterfaces() {
+ return mInterfaces;
+ }
+
void addToArray(List<Pair<String, Integer>> list, String name, int value) {
// check if we already have that name (at a lower level)
for (Pair<String, Integer> pair : list) {
- if (name.equals(pair.getFirst())) {
+ if (name.equals(pair.getFirst()) && pair.getSecond() < value) {
return;
}
}
@@ -102,6 +125,13 @@ public class ApiClass {
}
private void print(List<Pair<String, Integer> > list, String name, PrintStream stream) {
+ Collections.sort(list, new Comparator<Pair<String, Integer> >() {
+
+ @Override
+ public int compare(Pair<String, Integer> o1, Pair<String, Integer> o2) {
+ return o1.getFirst().compareTo(o2.getFirst());
+ }
+ });
for (Pair<String, Integer> pair : list) {
if (mSince == pair.getSecond()) {
diff --git a/apigenerator/src/com/android/apigenerator/ApiParseException.java b/apigenerator/src/com/android/apigenerator/ApiParseException.java
deleted file mode 100644
index 7fc8bde..0000000
--- a/apigenerator/src/com/android/apigenerator/ApiParseException.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2012 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.apigenerator;
-
-
-/**
- * Basic exception used by {@link NewApiParser}.
- *
- * This is adapted from doclava.
- *
- */
-public final class ApiParseException extends Exception {
- private static final long serialVersionUID = 1L;
-
- public String file;
- public int line;
-
- public ApiParseException() {
- }
-
- public ApiParseException(String message) {
- super(message);
- }
-
- public ApiParseException(String message, Exception cause) {
- super(message, cause);
- if (cause instanceof ApiParseException) {
- this.line = ((ApiParseException) cause).line;
- }
- }
-
- public ApiParseException(String message, int line) {
- super(message);
- this.line = line;
- }
-
- @Override
- public String getMessage() {
- if (line > 0) {
- return super.getMessage() + " line " + line;
- } else {
- return super.getMessage();
- }
- }
-}
diff --git a/apigenerator/src/com/android/apigenerator/EnumParser.java b/apigenerator/src/com/android/apigenerator/EnumParser.java
deleted file mode 100644
index 18c0a94..0000000
--- a/apigenerator/src/com/android/apigenerator/EnumParser.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright (C) 2012 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.apigenerator;
-
-import org.xml.sax.Attributes;
-import org.xml.sax.SAXException;
-import org.xml.sax.helpers.DefaultHandler;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.parsers.SAXParser;
-import javax.xml.parsers.SAXParserFactory;
-
-/**
- * Parser for the simplified XML API format version 1.
- */
-public class EnumParser extends DefaultHandler {
-
- private final static String NODE_API = "api";
- private final static String NODE_CLASS = "class";
- private final static String NODE_FIELD = "field";
- private final static String NODE_METHOD = "method";
- private final static String NODE_EXTENDS = "extends";
- private final static String NODE_IMPLEMENTS = "implements";
-
- private final static String ATTR_NAME = "name";
- private final static String ATTR_SINCE = "since";
-
- private final Map<String, ApiClass> mClasses = new HashMap<String, ApiClass>();
-
- private ApiClass mCurrentClass;
-
- public EnumParser() {
- }
-
- public Map<String, ApiClass> getClasses() {
- return mClasses;
- }
-
- @Override
- public void startElement(String uri, String localName, String qName, Attributes attributes)
- throws SAXException {
-
- if (localName == null || localName.length() == 0) {
- localName = qName;
- }
-
- try {
- if (NODE_API.equals(localName)) {
- // do nothing.
-
- } else if (NODE_CLASS.equals(localName)) {
- String name = attributes.getValue(ATTR_NAME);
- int since = Integer.parseInt(attributes.getValue(ATTR_SINCE));
-
- mCurrentClass = addClass(name, since);
-
- } else if (NODE_EXTENDS.equals(localName)) {
- String name = attributes.getValue(ATTR_NAME);
- int since = getSince(attributes);
-
- mCurrentClass.addSuperClass(name, since);
-
- } else if (NODE_IMPLEMENTS.equals(localName)) {
- String name = attributes.getValue(ATTR_NAME);
- int since = getSince(attributes);
-
- mCurrentClass.addInterface(name, since);
-
- } else if (NODE_METHOD.equals(localName)) {
- String name = attributes.getValue(ATTR_NAME);
- int since = getSince(attributes);
-
- mCurrentClass.addMethod(name, since);
-
- } else if (NODE_FIELD.equals(localName)) {
- String name = attributes.getValue(ATTR_NAME);
- int since = getSince(attributes);
-
- mCurrentClass.addField(name, since);
-
- }
-
- } finally {
- super.startElement(uri, localName, qName, attributes);
- }
- }
-
- private ApiClass addClass(String name, int apiLevel) {
- ApiClass theClass = mClasses.get(name);
- if (theClass == null) {
- theClass = new ApiClass(name, apiLevel);
- mClasses.put(name, theClass);
- }
-
- return theClass;
- }
-
- private int getSince(Attributes attributes) {
- int since = mCurrentClass.getSince();
- String sinceAttr = attributes.getValue(ATTR_SINCE);
-
- if (sinceAttr != null) {
- since = Integer.parseInt(sinceAttr);
- }
-
- return since;
- }
-
- public static Map<String, ApiClass> parseApi(InputStream stream) {
- try {
- SAXParserFactory parserFactory = SAXParserFactory.newInstance();
- SAXParser parser = parserFactory.newSAXParser();
- EnumParser apiParser = new EnumParser();
- parser.parse(stream, apiParser);
-
- return apiParser.getClasses();
- } catch (ParserConfigurationException e) {
- e.printStackTrace();
- } catch (SAXException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
-
- return null;
- }
-
-}
diff --git a/apigenerator/src/com/android/apigenerator/Main.java b/apigenerator/src/com/android/apigenerator/Main.java
index 5c26e14..4ce7ac9 100644
--- a/apigenerator/src/com/android/apigenerator/Main.java
+++ b/apigenerator/src/com/android/apigenerator/Main.java
@@ -17,24 +17,13 @@
package com.android.apigenerator;
-import com.android.apigenerator.enumfix.AndroidJarReader;
-
-import org.xml.sax.SAXException;
import java.io.File;
-import java.io.FileInputStream;
import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
import java.io.PrintStream;
-import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.parsers.SAXParser;
-import javax.xml.parsers.SAXParserFactory;
-
/**
* Main class for command line command to convert the existing API XML/TXT files into diff-based
* simple text files.
@@ -46,98 +35,22 @@ public class Main {
* @param args
*/
public static void main(String[] args) {
- if (args.length < 2 || args.length > 3) {
+ if (args.length != 2) {
printUsage();
}
- if (args.length == 3) {
- if (args[0].equals("enum")) {
- AndroidJarReader reader = new AndroidJarReader(args[1]);
- Map<String, ApiClass> classes = reader.getEnumClasses();
- createApiFile(new File(args[2]), classes);
- } else {
- printUsage();
- }
- } else {
- Map<String, ApiClass> classes = parsePlatformApiFiles(new File(args[0]));
- createApiFile(new File(args[1]), classes);
- }
-
+ AndroidJarReader reader = new AndroidJarReader(args[0]);
+ Map<String, ApiClass> classes = reader.getClasses();
+ createApiFile(new File(args[1]), classes);
}
private static void printUsage() {
- System.err.println("Convert API files into a more manageable file\n");
+ System.err.println("Generates a single API file from the content of an SDK.\n");
System.err.println("Usage\n");
- System.err.println("\tApiCheck [enum] FOLDER OUTFILE\n");
+ System.err.println("\tApiCheck SDKFOLDER OUTFILE\n");
System.exit(1);
}
-
- /**
- * Parses platform API files.
- * @param apiFolder the folder containing the files.
- * @return a top level {@link ApiInfo} object for the highest available API level.
- */
- private static Map<String, ApiClass> parsePlatformApiFiles(File apiFolder) {
- int apiLevel = 1;
-
- Map<String, ApiClass> map = new HashMap<String, ApiClass>();
-
- InputStream stream = Main.class.getResourceAsStream(
- "enums.xml");
- if (stream != null) {
- map = EnumParser.parseApi(stream);
- }
-
- if (map == null) {
- map = new HashMap<String, ApiClass>();
- }
-
- while (true) {
- File file = new File(apiFolder, Integer.toString(apiLevel) + ".xml");
- if (file.exists()) {
- parseXmlApiFile(file, apiLevel, map);
- apiLevel++;
- } else {
- file = new File(apiFolder, Integer.toString(apiLevel) + ".txt");
- if (file.exists()) {
- parseTxtApiFile(file, apiLevel, map);
- apiLevel++;
-
- } else {
- break;
- }
- }
- }
-
- return map;
- }
-
- private static void parseTxtApiFile(File apiFile, int api, Map<String, ApiClass> map) {
- try {
- NewApiParser.parseApi(apiFile.getName(), new FileInputStream(apiFile), map, api);
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- } catch (ApiParseException e) {
- e.printStackTrace();
- }
- }
-
- private static void parseXmlApiFile(File apiFile, int apiLevel,
- Map<String, ApiClass> map) {
- try {
- SAXParserFactory parserFactory = SAXParserFactory.newInstance();
- SAXParser parser = parserFactory.newSAXParser();
- parser.parse(new FileInputStream(apiFile), new XmlApiParser(map, apiLevel));
- } catch (ParserConfigurationException e) {
- e.printStackTrace();
- } catch (SAXException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
/**
* Creates the simplified diff-based API level.
* @param outFolder the out folder.
diff --git a/apigenerator/src/com/android/apigenerator/NewApiParser.java b/apigenerator/src/com/android/apigenerator/NewApiParser.java
deleted file mode 100644
index 91cb1e2..0000000
--- a/apigenerator/src/com/android/apigenerator/NewApiParser.java
+++ /dev/null
@@ -1,619 +0,0 @@
-/*
- * Copyright (C) 2012 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.apigenerator;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Map;
-
-/**
- * Parser for the new format of platform API files. This is adapted from the Doclava code.
- *
- */
-class NewApiParser {
-
- public static void parseApi(String filename, InputStream stream,
- Map<String, ApiClass> classes, int api) throws ApiParseException {
- final int CHUNK = 1024 * 1024;
- int hint = 0;
- try {
- hint = stream.available() + CHUNK;
- } catch (IOException ex) {
- }
-
- if (hint < CHUNK) {
- hint = CHUNK;
- }
-
- byte[] buf = new byte[hint];
- int size = 0;
-
- try {
- while (true) {
- if (size == buf.length) {
- byte[] tmp = new byte[buf.length + CHUNK];
- System.arraycopy(buf, 0, tmp, 0, buf.length);
- buf = tmp;
- }
- int amt = stream.read(buf, size, (buf.length - size));
- if (amt < 0) {
- break;
- } else {
- size += amt;
- }
- }
- } catch (IOException ex) {
- throw new ApiParseException("Error reading API file", ex);
- }
-
- final Tokenizer tokenizer = new Tokenizer(filename,
- (new String(buf, 0, size)).toCharArray());
-
- final ParserState state = new ParserState(classes, api);
-
- while (true) {
- String token = tokenizer.getToken();
- if (token == null) {
- break;
- }
- if ("package".equals(token)) {
- parsePackage(state, tokenizer);
- } else {
- throw new ApiParseException("expected package got " + token, tokenizer.getLine());
- }
- }
- }
-
- private static void parsePackage(ParserState state, Tokenizer tokenizer)
- throws ApiParseException {
- String token;
- String name;
-
- token = tokenizer.requireToken();
- assertIdent(tokenizer, token);
- name = token;
-
- state.addPackage(name);
-
- token = tokenizer.requireToken();
- if (!"{".equals(token)) {
- throw new ApiParseException("expected '{' got " + token, tokenizer.getLine());
- }
- while (true) {
- token = tokenizer.requireToken();
- if ("}".equals(token)) {
- break;
- } else {
- parseClass(state, tokenizer, token);
- }
- }
-
- state.finishPackage();
- }
-
- private static void parseClass(ParserState state, Tokenizer tokenizer, String token)
- throws ApiParseException {
- boolean pub = false;
- boolean prot = false;
- boolean pkgpriv = false;
- boolean stat = false;
- boolean fin = false;
- boolean abs = false;
- boolean dep = false;
- boolean iface;
- String name;
- String qname;
-
- // even though we don't care about all those parameters, we keep this parsing logic
- // to make sure we go through all the tokens.
-
- if ("public".equals(token)) {
- pub = true;
- token = tokenizer.requireToken();
- } else if ("protected".equals(token)) {
- prot = true;
- token = tokenizer.requireToken();
- } else {
- pkgpriv = true;
- }
- if ("static".equals(token)) {
- stat = true;
- token = tokenizer.requireToken();
- }
- if ("final".equals(token)) {
- fin = true;
- token = tokenizer.requireToken();
- }
- if ("abstract".equals(token)) {
- abs = true;
- token = tokenizer.requireToken();
- }
- if ("deprecated".equals(token)) {
- dep = true;
- token = tokenizer.requireToken();
- }
- if ("class".equals(token)) {
- iface = false;
- token = tokenizer.requireToken();
- } else if ("interface".equals(token)) {
- iface = true;
- token = tokenizer.requireToken();
- } else {
- throw new ApiParseException("missing class or interface. got: " + token,
- tokenizer.getLine());
- }
- assertIdent(tokenizer, token);
- name = token;
- token = tokenizer.requireToken();
-
- state.addClass(name);
-
- // even though we don't care about all those parameters, we keep this parsing logic
- // to make sure we go through all the tokens.
-
-
- if ("extends".equals(token)) {
- token = tokenizer.requireToken();
- assertIdent(tokenizer, token);
- state.addSuperClass(token);
- token = tokenizer.requireToken();
- }
-
- // Resolve superclass after done parsing
- if ("implements".equals(token)) {
- while (true) {
- token = tokenizer.requireToken();
- if ("{".equals(token)) {
- break;
- } else {
- if (!",".equals(token)) {
- state.addInterface(token);
- }
- }
- }
- }
-
- if (!"{".equals(token)) {
- throw new ApiParseException("expected {", tokenizer.getLine());
- }
-
- token = tokenizer.requireToken();
- while (true) {
- if ("}".equals(token)) {
- break;
- } else if ("ctor".equals(token)) {
- token = tokenizer.requireToken();
- parseConstructor(tokenizer, state, token);
- } else if ("method".equals(token)) {
- token = tokenizer.requireToken();
- parseMethod(tokenizer, state, token);
- } else if ("field".equals(token)) {
- token = tokenizer.requireToken();
- parseField(tokenizer, state, token, false);
- } else if ("enum_constant".equals(token)) {
- token = tokenizer.requireToken();
- parseField(tokenizer, state, token, true);
- } else {
- throw new ApiParseException("expected ctor, enum_constant, field or method",
- tokenizer.getLine());
- }
- token = tokenizer.requireToken();
- }
-
- state.finishClass();
- }
-
- private static void parseConstructor(Tokenizer tokenizer, ParserState state, String token)
- throws ApiParseException {
- boolean pub = false;
- boolean prot = false;
- boolean pkgpriv = false;
- boolean dep = false;
- String name;
-
- if ("public".equals(token)) {
- pub = true;
- token = tokenizer.requireToken();
- } else if ("protected".equals(token)) {
- prot = true;
- token = tokenizer.requireToken();
- } else {
- pkgpriv = true;
- }
- if ("deprecated".equals(token)) {
- dep = true;
- token = tokenizer.requireToken();
- }
- assertIdent(tokenizer, token);
- name = token;
- token = tokenizer.requireToken();
- if (!"(".equals(token)) {
- throw new ApiParseException("expected (", tokenizer.getLine());
- }
-
- state.startNewConstructor();
-
- token = tokenizer.requireToken();
- parseParameterList(tokenizer, state, token);
- token = tokenizer.requireToken();
- if ("throws".equals(token)) {
- token = parseThrows(tokenizer, state);
- }
- if (!";".equals(token)) {
- throw new ApiParseException("expected ; found " + token, tokenizer.getLine());
- }
-
- state.finishMethod();
- }
-
- private static void parseMethod(Tokenizer tokenizer, ParserState state, String token)
- throws ApiParseException {
- boolean pub = false;
- boolean prot = false;
- boolean pkgpriv = false;
- boolean stat = false;
- boolean fin = false;
- boolean abs = false;
- boolean dep = false;
- boolean syn = false;
- String type;
- String name;
- String ext = null;
-
- if ("public".equals(token)) {
- pub = true;
- token = tokenizer.requireToken();
- } else if ("protected".equals(token)) {
- prot = true;
- token = tokenizer.requireToken();
- } else {
- pkgpriv = true;
- }
- if ("static".equals(token)) {
- stat = true;
- token = tokenizer.requireToken();
- }
- if ("final".equals(token)) {
- fin = true;
- token = tokenizer.requireToken();
- }
- if ("abstract".equals(token)) {
- abs = true;
- token = tokenizer.requireToken();
- }
- if ("deprecated".equals(token)) {
- dep = true;
- token = tokenizer.requireToken();
- }
- if ("synchronized".equals(token)) {
- syn = true;
- token = tokenizer.requireToken();
- }
- assertIdent(tokenizer, token);
- type = token;
- token = tokenizer.requireToken();
- assertIdent(tokenizer, token);
- name = token;
-
- state.startNewMethod(name, type);
-
- token = tokenizer.requireToken();
- if (!"(".equals(token)) {
- throw new ApiParseException("expected (", tokenizer.getLine());
- }
- token = tokenizer.requireToken();
- parseParameterList(tokenizer, state, token);
- token = tokenizer.requireToken();
- if ("throws".equals(token)) {
- token = parseThrows(tokenizer, state);
- }
- if (!";".equals(token)) {
- throw new ApiParseException("expected ; found " + token, tokenizer.getLine());
- }
-
- state.finishMethod();
- }
-
- private static void parseField(Tokenizer tokenizer, ParserState state, String token, boolean isEnum)
- throws ApiParseException {
- boolean pub = false;
- boolean prot = false;
- boolean pkgpriv = false;
- boolean stat = false;
- boolean fin = false;
- boolean dep = false;
- boolean trans = false;
- boolean vol = false;
- String type;
- String name;
- String val = null;
- Object v;
-
- if ("public".equals(token)) {
- pub = true;
- token = tokenizer.requireToken();
- } else if ("protected".equals(token)) {
- prot = true;
- token = tokenizer.requireToken();
- } else {
- pkgpriv = true;
- }
- if ("static".equals(token)) {
- stat = true;
- token = tokenizer.requireToken();
- }
- if ("final".equals(token)) {
- fin = true;
- token = tokenizer.requireToken();
- }
- if ("deprecated".equals(token)) {
- dep = true;
- token = tokenizer.requireToken();
- }
- if ("transient".equals(token)) {
- trans = true;
- token = tokenizer.requireToken();
- }
- if ("volatile".equals(token)) {
- vol = true;
- token = tokenizer.requireToken();
- }
- assertIdent(tokenizer, token);
- type = token;
- token = tokenizer.requireToken();
- assertIdent(tokenizer, token);
- name = token;
- token = tokenizer.requireToken();
- if ("=".equals(token)) {
- token = tokenizer.requireToken(false);
- val = token;
- token = tokenizer.requireToken();
- }
- if (!";".equals(token)) {
- throw new ApiParseException("expected ; found " + token, tokenizer.getLine());
- }
-
- if (isEnum) {
- state.addField(name);
- } else {
- state.addField(name);
- }
- }
-
- private static void parseParameterList(Tokenizer tokenizer, ParserState state,
- String token) throws ApiParseException {
- while (true) {
- if (")".equals(token)) {
- return;
- }
-
- String type = token;
- String name = null;
- token = tokenizer.requireToken();
- if (isIdent(token)) {
- name = token;
- token = tokenizer.requireToken();
- }
- if (",".equals(token)) {
- token = tokenizer.requireToken();
- } else if (")".equals(token)) {
- } else {
- throw new ApiParseException("expected , found " + token, tokenizer.getLine());
- }
- state.addMethodParameter(type);
-// method.addParameter(new ParameterInfo(name, type, Converter.obtainTypeFromString(type),
-// type.endsWith("..."), tokenizer.pos()));
- }
- }
-
- private static String parseThrows(Tokenizer tokenizer, ParserState state)
- throws ApiParseException {
- String token = tokenizer.requireToken();
- boolean comma = true;
- while (true) {
- if (";".equals(token)) {
- return token;
- } else if (",".equals(token)) {
- if (comma) {
- throw new ApiParseException("Expected exception, got ','", tokenizer.getLine());
- }
- comma = true;
- } else {
- if (!comma) {
- throw new ApiParseException("Expected ',' or ';' got " + token,
- tokenizer.getLine());
- }
- comma = false;
- }
- token = tokenizer.requireToken();
- }
- }
-
-// private static String qualifiedName(String pkg, String className, ClassInfo parent) {
-// String parentQName = (parent != null) ? (parent.qualifiedName() + ".") : "";
-// return pkg + "." + parentQName + className;
-// }
-
- private static boolean isIdent(String token) {
- return isident(token.charAt(0));
- }
-
- private static void assertIdent(Tokenizer tokenizer, String token) throws ApiParseException {
- if (!isident(token.charAt(0))) {
- throw new ApiParseException("Expected identifier: " + token, tokenizer.getLine());
- }
- }
-
- static class Tokenizer {
- char[] mBuf;
-
- String mFilename;
-
- int mPos;
-
- int mLine = 1;
-
- Tokenizer(String filename, char[] buf) {
- mFilename = filename;
- mBuf = buf;
- }
-
- public int getLine() {
- return mLine;
- }
-
- boolean eatWhitespace() {
- boolean ate = false;
- while (mPos < mBuf.length && isspace(mBuf[mPos])) {
- if (mBuf[mPos] == '\n') {
- mLine++;
- }
- mPos++;
- ate = true;
- }
- return ate;
- }
-
- boolean eatComment() {
- if (mPos + 1 < mBuf.length) {
- if (mBuf[mPos] == '/' && mBuf[mPos + 1] == '/') {
- mPos += 2;
- while (mPos < mBuf.length && !isnewline(mBuf[mPos])) {
- mPos++;
- }
- return true;
- }
- }
- return false;
- }
-
- void eatWhitespaceAndComments() {
- while (eatWhitespace() || eatComment()) {
- }
- }
-
- public String requireToken() throws ApiParseException {
- return requireToken(true);
- }
-
- public String requireToken(boolean parenIsSep) throws ApiParseException {
- final String token = getToken(parenIsSep);
- if (token != null) {
- return token;
- } else {
- throw new ApiParseException("Unexpected end of file", mLine);
- }
- }
-
- public String getToken() throws ApiParseException {
- return getToken(true);
- }
-
- public String getToken(boolean parenIsSep) throws ApiParseException {
- eatWhitespaceAndComments();
- if (mPos >= mBuf.length) {
- return null;
- }
- final int line = mLine;
- final char c = mBuf[mPos];
- final int start = mPos;
- mPos++;
- if (c == '"') {
- final int STATE_BEGIN = 0;
- final int STATE_ESCAPE = 1;
- int state = STATE_BEGIN;
- while (true) {
- if (mPos >= mBuf.length) {
- throw new ApiParseException("Unexpected end of file for \" starting at "
- + line, mLine);
- }
- final char k = mBuf[mPos];
- if (k == '\n' || k == '\r') {
- throw new ApiParseException(
- "Unexpected newline for \" starting at " + line, mLine);
- }
- mPos++;
- switch (state) {
- case STATE_BEGIN:
- switch (k) {
- case '\\':
- state = STATE_ESCAPE;
- mPos++;
- break;
- case '"':
- return new String(mBuf, start, mPos - start);
- }
- case STATE_ESCAPE:
- state = STATE_BEGIN;
- break;
- }
- }
- } else if (issep(c, parenIsSep)) {
- return "" + c;
- } else {
- int genericDepth = 0;
- do {
- while (mPos < mBuf.length && !isspace(mBuf[mPos])
- && !issep(mBuf[mPos], parenIsSep)) {
- mPos++;
- }
- if (mPos < mBuf.length) {
- if (mBuf[mPos] == '<') {
- genericDepth++;
- mPos++;
- } else if (mBuf[mPos] == '>') {
- genericDepth--;
- mPos++;
- } else if (genericDepth != 0) {
- mPos++;
- }
- }
- } while (mPos < mBuf.length
- && ((!isspace(mBuf[mPos]) && !issep(mBuf[mPos], parenIsSep)) || genericDepth != 0));
- if (mPos >= mBuf.length) {
- throw new ApiParseException(
- "Unexpected end of file for \" starting at " + line, mLine);
- }
- return new String(mBuf, start, mPos - start);
- }
- }
- }
-
- static boolean isspace(char c) {
- return c == ' ' || c == '\t' || c == '\n' || c == '\r';
- }
-
- static boolean isnewline(char c) {
- return c == '\n' || c == '\r';
- }
-
- static boolean issep(char c, boolean parenIsSep) {
- if (parenIsSep) {
- if (c == '(' || c == ')') {
- return true;
- }
- }
- return c == '{' || c == '}' || c == ',' || c == ';' || c == '<' || c == '>';
- }
-
- static boolean isident(char c) {
- if (c == '"' || issep(c, true)) {
- return false;
- }
- return true;
- }
-}
diff --git a/apigenerator/src/com/android/apigenerator/ParserState.java b/apigenerator/src/com/android/apigenerator/ParserState.java
deleted file mode 100644
index 7ffb57a..0000000
--- a/apigenerator/src/com/android/apigenerator/ParserState.java
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Copyright (C) 2012 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.apigenerator;
-
-import java.util.Map;
-
-/**
- * Parser state used during parsing of the platform API files.
- *
- */
-class ParserState {
-
- private final int mApiLevel;
-
- private final Map<String, ApiClass> mClasses;
-
- private String mCurrentPackage;
- private ApiClass mCurrentClass;
-
- private String mMethodName;
- private StringBuilder mMethodParams = new StringBuilder();
- private String mMethodReturnType;
-
- ParserState(Map<String, ApiClass> classes, int apiLevel) {
- mClasses = classes;
- mApiLevel = apiLevel;
- }
-
- Map<String, ApiClass> getClasses() {
- return mClasses;
- }
-
- void addPackage(String packageName) {
- mCurrentPackage = packageName;
- }
-
- void addClass(String className) {
- String fqcn = makeJavaClass(mCurrentPackage + "." + className);
- mCurrentClass = addClass(fqcn, mApiLevel);
- }
-
- void addSuperClass(String superClass) {
- mCurrentClass.addSuperClass(makeJavaClass(superClass), mApiLevel);
- }
-
- void addInterface(String interfaceClass) {
- mCurrentClass.addInterface(makeJavaClass(interfaceClass), mApiLevel);
- }
-
- void startNewConstructor() {
- mMethodParams.setLength(0);
- mMethodName = "<init>";
- mMethodReturnType = "V";
- }
-
- void startNewMethod(String name, String returnType) {
- mMethodParams.setLength(0);
- mMethodName = name;
- mMethodReturnType = parseType(returnType);
- }
-
- void addMethodParameter(String parameter) {
- mMethodParams.append(parseType(parameter));
- }
-
- void finishMethod() {
- addMethod(mMethodName + "(" + mMethodParams.toString() + ")" +
- (mMethodReturnType != null ? mMethodReturnType : ""));
- }
-
- void addMethod(String methodSignature) {
- mCurrentClass.addMethod(methodSignature, mApiLevel);
- }
-
- void addField(String fieldName) {
- mCurrentClass.addField(fieldName, mApiLevel);
- }
-
- void finishClass() {
- mCurrentClass = null;
- }
-
- void finishPackage() {
- finishClass();
- mCurrentPackage = null;
- }
-
- void done() {
- finishPackage();
- }
-
- private ApiClass addClass(String name, int apiLevel) {
- ApiClass theClass = mClasses.get(name);
- if (theClass == null) {
- theClass = new ApiClass(name, apiLevel);
- mClasses.put(name, theClass);
- }
-
- return theClass;
- }
-
-
- private String makeJavaClass(String fqcn) {
- final int length = fqcn.length();
-
- StringBuilder sb = new StringBuilder(length);
-
- boolean isClass = Character.isUpperCase(fqcn.charAt(0));
- for (int i = 0 ; i < length ; i++) {
- if (fqcn.charAt(i) == '.') {
- if (isClass) {
- sb.append('$');
- } else {
- sb.append('/');
- }
-
- if (i < length -1 ) {
- isClass = Character.isUpperCase(fqcn.charAt(i+1));
- }
- } else {
- if (fqcn.charAt(i) == '<') {
- break;
- }
-
- sb.append(fqcn.charAt(i));
- }
- }
-
- return sb.toString();
- }
-
- private String parseType(String type) {
- StringBuilder result = new StringBuilder();
-
- if (type.endsWith("...")) {
- result.append('[');
- type = type.substring(0, type.length() - 3);
- }
-
- while (type.endsWith("[]")) {
- result.append('[');
- type = type.substring(0, type.length() - 2);
- }
-
- if ("byte".equals(type)) result.append('B');
- else if ("char".equals(type)) result.append('C');
- else if ("double".equals(type)) result.append('D');
- else if ("float".equals(type)) result.append('F');
- else if ("int".equals(type)) result.append('I');
- else if ("long".equals(type)) result.append('J');
- else if ("short".equals(type)) result.append('S');
- else if ("void".equals(type)) result.append('V');
- else if ("boolean".equals(type)) result.append('Z');
- else {
- result.append('L').append(makeJavaClass(type)).append(';');
- }
-
- return result.toString();
- }
-}
diff --git a/apigenerator/src/com/android/apigenerator/XmlApiParser.java b/apigenerator/src/com/android/apigenerator/XmlApiParser.java
deleted file mode 100644
index 840272c..0000000
--- a/apigenerator/src/com/android/apigenerator/XmlApiParser.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2012 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.apigenerator;
-
-import org.xml.sax.Attributes;
-import org.xml.sax.SAXException;
-import org.xml.sax.helpers.DefaultHandler;
-
-import java.util.Map;
-
-/**
- * Parser for the old, XML-based format of platform API files.
- */
-class XmlApiParser extends DefaultHandler {
-
- private final static String NODE_API = "api";
- private final static String NODE_PACKAGE = "package";
- private final static String NODE_CLASS = "class";
- private final static String NODE_INTERFACE = "interface";
- private final static String NODE_IMPLEMENTS = "implements";
- private final static String NODE_FIELD = "field";
- private final static String NODE_CONSTRUCTOR = "constructor";
- private final static String NODE_METHOD = "method";
- private final static String NODE_PARAMETER = "parameter";
-
- private final static String ATTR_NAME = "name";
- private final static String ATTR_TYPE = "type";
- private final static String ATTR_RETURN = "return";
- private final static String ATTR_EXTENDS = "extends";
-
- private final ParserState mParserState;
-
- XmlApiParser(Map<String, ApiClass> map, int apiLevel) {
- mParserState = new ParserState(map, apiLevel);
- }
-
- @Override
- public void startElement(String uri, String localName, String qName, Attributes attributes)
- throws SAXException {
-
- if (localName == null || localName.length() == 0) {
- localName = qName;
- }
-
- try {
-
- if (NODE_API.equals(localName)) {
- } else if (NODE_PACKAGE.equals(localName)) {
- mParserState.addPackage(attributes.getValue(ATTR_NAME));
-
- } else if (NODE_CLASS.equals(localName) || NODE_INTERFACE.equals(localName)) {
- mParserState.addClass(attributes.getValue(ATTR_NAME));
-
- String extendsAttr = attributes.getValue(ATTR_EXTENDS);
- if (extendsAttr != null) {
- mParserState.addSuperClass(extendsAttr);
- }
-
- } else if (NODE_IMPLEMENTS.equals(localName)) {
- mParserState.addInterface(attributes.getValue(ATTR_NAME));
-
- } else if (NODE_FIELD.equals(localName)) {
- mParserState.addField(attributes.getValue(ATTR_NAME));
-
- } else if (NODE_CONSTRUCTOR.equals(localName)) {
- parseConstructor(attributes);
-
- } else if (NODE_METHOD.equals(localName)) {
- parseMethod(attributes);
-
- } else if (NODE_PARAMETER.equals(localName)) {
- parseParameter(attributes);
- }
-
- } finally {
- super.startElement(uri, localName, qName, attributes);
- }
- }
-
- private void parseConstructor(Attributes attributes) {
- mParserState.startNewConstructor();
- }
-
- private void parseMethod(Attributes attributes) {
- mParserState.startNewMethod(attributes.getValue(ATTR_NAME),
- attributes.getValue(ATTR_RETURN));
- }
-
- private void parseParameter(Attributes attributes) {
- mParserState.addMethodParameter(attributes.getValue(ATTR_TYPE));
- }
-
- @Override
- public void endElement(String uri, String localName, String qName) throws SAXException {
- if (localName == null || localName.length() == 0) {
- localName = qName;
- }
-
- try {
-
- if (NODE_METHOD.equals(localName) || NODE_CONSTRUCTOR.equals(localName)) {
- mParserState.finishMethod();
-
- } else if (NODE_API.equals(localName)) {
- mParserState.done();
- }
-
- } finally {
- super.endElement(uri, localName, qName);
- }
- }
-} \ No newline at end of file
diff --git a/apigenerator/src/com/android/apigenerator/enumfix/AndroidJarReader.java b/apigenerator/src/com/android/apigenerator/enumfix/AndroidJarReader.java
deleted file mode 100644
index 7669786..0000000
--- a/apigenerator/src/com/android/apigenerator/enumfix/AndroidJarReader.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2012 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.apigenerator.enumfix;
-
-import com.android.apigenerator.ApiClass;
-
-import org.objectweb.asm.ClassReader;
-import org.objectweb.asm.tree.ClassNode;
-import org.objectweb.asm.tree.FieldNode;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.net.MalformedURLException;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipInputStream;
-
-/**
- * This codes looks at all the android.jar in an SDK and use ASM to figure out when enums
- * where introduced. This is a one time thing that creates the file
- * /com/android/apichecker/generator/enums.xml which is then used to create the final API file.
- *
- */
-public class AndroidJarReader {
-
- // this the last API until we switched to a new API format that included enum values.
- private final static int MAX_API = 13;
- private static final byte[] BUFFER = new byte[65535];
-
- private final String mSdkFolder;
-
- public AndroidJarReader(String sdkFolder) {
- mSdkFolder = sdkFolder;
- }
-
- public Map<String, ApiClass> getEnumClasses() {
- HashMap<String, ApiClass> map = new HashMap<String, ApiClass>();
-
- // Get all the android.jar. They are in platforms-#
- for (int apiLevel = 1 ; apiLevel <= MAX_API ; apiLevel++) {
- try {
- File jar = new File(mSdkFolder, "platforms/android-" + apiLevel + "/android.jar");
- if (jar.exists() == false) {
- System.err.println("Missing android.jar for API level " + apiLevel);
- continue;
- }
-
- FileInputStream fis = new FileInputStream(jar);
- ZipInputStream zis = new ZipInputStream(fis);
- ZipEntry entry = zis.getNextEntry();
- while (entry != null) {
- String name = entry.getName();
-
- if (name.endsWith(".class")) {
-
- int index = 0;
- do {
- int size = zis.read(BUFFER, index, BUFFER.length - index);
- if (size >= 0) {
- index += size;
- } else {
- break;
- }
- } while (true);
-
- byte[] b = new byte[index];
- System.arraycopy(BUFFER, 0, b, 0, index);
-
- ClassReader reader = new ClassReader(b);
- ClassNode classNode = new ClassNode();
- reader.accept(classNode, 0 /*flags*/);
-
- if (classNode != null && classNode.superName != null &&
- classNode.superName.equals("java/lang/Enum")) {
-
- ApiClass theClass = addClass(map, classNode.name, apiLevel);
- theClass.addSuperClass("java/lang/Enum", apiLevel);
-
- List fields = classNode.fields;
- for (Object f : fields) {
- FieldNode fnode = (FieldNode) f;
- if (fnode.desc.substring(1, fnode.desc.length() - 1).equals(classNode.name)) {
- theClass.addField(fnode.name, apiLevel);
- }
- }
- }
- }
- entry = zis.getNextEntry();
- }
- } catch (MalformedURLException e) {
- e.printStackTrace();
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
-
- }
- }
-
- return map;
- }
-
- private ApiClass addClass(HashMap<String, ApiClass> classes, String name, int apiLevel) {
- ApiClass theClass = classes.get(name);
- if (theClass == null) {
- theClass = new ApiClass(name, apiLevel);
- classes.put(name, theClass);
- }
-
- return theClass;
- }
-
-}
diff --git a/apigenerator/src/com/android/apigenerator/enums.xml b/apigenerator/src/com/android/apigenerator/enums.xml
deleted file mode 100644
index 54a2a21..0000000
--- a/apigenerator/src/com/android/apigenerator/enums.xml
+++ /dev/null
@@ -1,596 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<api version="1">
- <class name="android/database/CursorJoiner$Result" since="1">
- <extends name="java/lang/Enum" />
- <field name="BOTH" />
- <field name="LEFT" />
- <field name="RIGHT" />
- </class>
- <class name="android/graphics/AvoidXfermode$Mode" since="1">
- <extends name="java/lang/Enum" />
- <field name="AVOID" />
- <field name="TARGET" />
- </class>
- <class name="android/graphics/Bitmap$CompressFormat" since="1">
- <extends name="java/lang/Enum" />
- <field name="JPEG" />
- <field name="PNG" />
- </class>
- <class name="android/graphics/Bitmap$Config" since="1">
- <extends name="java/lang/Enum" />
- <field name="ALPHA_8" />
- <field name="ARGB_4444" />
- <field name="ARGB_8888" />
- <field name="RGB_565" />
- </class>
- <class name="android/graphics/BlurMaskFilter$Blur" since="1">
- <extends name="java/lang/Enum" />
- <field name="INNER" />
- <field name="NORMAL" />
- <field name="OUTER" />
- <field name="SOLID" />
- </class>
- <class name="android/graphics/Canvas$EdgeType" since="1">
- <extends name="java/lang/Enum" />
- <field name="AA" />
- <field name="BW" />
- </class>
- <class name="android/graphics/Canvas$VertexMode" since="1">
- <extends name="java/lang/Enum" />
- <field name="TRIANGLES" />
- <field name="TRIANGLE_FAN" />
- <field name="TRIANGLE_STRIP" />
- </class>
- <class name="android/graphics/Interpolator$Result" since="1">
- <extends name="java/lang/Enum" />
- <field name="FREEZE_END" />
- <field name="FREEZE_START" />
- <field name="NORMAL" />
- </class>
- <class name="android/graphics/Matrix$ScaleToFit" since="1">
- <extends name="java/lang/Enum" />
- <field name="CENTER" />
- <field name="END" />
- <field name="FILL" />
- <field name="START" />
- </class>
- <class name="android/graphics/Paint$Align" since="1">
- <extends name="java/lang/Enum" />
- <field name="CENTER" />
- <field name="LEFT" />
- <field name="RIGHT" />
- </class>
- <class name="android/graphics/Paint$Cap" since="1">
- <extends name="java/lang/Enum" />
- <field name="BUTT" />
- <field name="ROUND" />
- <field name="SQUARE" />
- </class>
- <class name="android/graphics/Paint$Join" since="1">
- <extends name="java/lang/Enum" />
- <field name="BEVEL" />
- <field name="MITER" />
- <field name="ROUND" />
- </class>
- <class name="android/graphics/Paint$Style" since="1">
- <extends name="java/lang/Enum" />
- <field name="FILL" />
- <field name="FILL_AND_STROKE" />
- <field name="STROKE" />
- </class>
- <class name="android/graphics/Path$Direction" since="1">
- <extends name="java/lang/Enum" />
- <field name="CCW" />
- <field name="CW" />
- </class>
- <class name="android/graphics/Path$FillType" since="1">
- <extends name="java/lang/Enum" />
- <field name="EVEN_ODD" />
- <field name="INVERSE_EVEN_ODD" />
- <field name="INVERSE_WINDING" />
- <field name="WINDING" />
- </class>
- <class name="android/graphics/PathDashPathEffect$Style" since="1">
- <extends name="java/lang/Enum" />
- <field name="MORPH" />
- <field name="ROTATE" />
- <field name="TRANSLATE" />
- </class>
- <class name="android/graphics/PorterDuff$Mode" since="1">
- <extends name="java/lang/Enum" />
- <field name="ADD" since="11" />
- <field name="CLEAR" />
- <field name="DARKEN" />
- <field name="DST" />
- <field name="DST_ATOP" />
- <field name="DST_IN" />
- <field name="DST_OUT" />
- <field name="DST_OVER" />
- <field name="LIGHTEN" />
- <field name="MULTIPLY" />
- <field name="OVERLAY" since="11" />
- <field name="SCREEN" />
- <field name="SRC" />
- <field name="SRC_ATOP" />
- <field name="SRC_IN" />
- <field name="SRC_OUT" />
- <field name="SRC_OVER" />
- <field name="XOR" />
- </class>
- <class name="android/graphics/Region$Op" since="1">
- <extends name="java/lang/Enum" />
- <field name="DIFFERENCE" />
- <field name="INTERSECT" />
- <field name="REPLACE" />
- <field name="REVERSE_DIFFERENCE" />
- <field name="UNION" />
- <field name="XOR" />
- </class>
- <class name="android/graphics/Shader$TileMode" since="1">
- <extends name="java/lang/Enum" />
- <field name="CLAMP" />
- <field name="MIRROR" />
- <field name="REPEAT" />
- </class>
- <class name="android/graphics/drawable/GradientDrawable$Orientation" since="1">
- <extends name="java/lang/Enum" />
- <field name="BL_TR" />
- <field name="BOTTOM_TOP" />
- <field name="BR_TL" />
- <field name="LEFT_RIGHT" />
- <field name="RIGHT_LEFT" />
- <field name="TL_BR" />
- <field name="TOP_BOTTOM" />
- <field name="TR_BL" />
- </class>
- <class name="android/net/LocalSocketAddress$Namespace" since="1">
- <extends name="java/lang/Enum" />
- <field name="ABSTRACT" />
- <field name="FILESYSTEM" />
- <field name="RESERVED" />
- </class>
- <class name="android/net/NetworkInfo$DetailedState" since="1">
- <extends name="java/lang/Enum" />
- <field name="AUTHENTICATING" />
- <field name="CONNECTED" />
- <field name="CONNECTING" />
- <field name="DISCONNECTED" />
- <field name="DISCONNECTING" />
- <field name="FAILED" />
- <field name="IDLE" />
- <field name="OBTAINING_IPADDR" />
- <field name="SCANNING" />
- <field name="SUSPENDED" />
- </class>
- <class name="android/net/NetworkInfo$State" since="1">
- <extends name="java/lang/Enum" />
- <field name="CONNECTED" />
- <field name="CONNECTING" />
- <field name="DISCONNECTED" />
- <field name="DISCONNECTING" />
- <field name="SUSPENDED" />
- <field name="UNKNOWN" />
- </class>
- <class name="android/net/wifi/SupplicantState" since="1">
- <extends name="java/lang/Enum" />
- <field name="ASSOCIATED" />
- <field name="ASSOCIATING" />
- <field name="COMPLETED" />
- <field name="DISCONNECTED" />
- <field name="DORMANT" />
- <field name="FOUR_WAY_HANDSHAKE" />
- <field name="GROUP_HANDSHAKE" />
- <field name="INACTIVE" />
- <field name="INVALID" />
- <field name="SCANNING" />
- <field name="UNINITIALIZED" />
- </class>
- <class name="android/os/AsyncTask$Status" since="3">
- <extends name="java/lang/Enum" />
- <field name="FINISHED" />
- <field name="PENDING" />
- <field name="RUNNING" />
- </class>
- <class name="android/renderscript/Allocation$MipmapControl" since="11">
- <extends name="java/lang/Enum" />
- <field name="MIPMAP_FULL" />
- <field name="MIPMAP_NONE" />
- <field name="MIPMAP_ON_SYNC_TO_TEXTURE" />
- </class>
- <class name="android/renderscript/Element$DataKind" since="11">
- <extends name="java/lang/Enum" />
- <field name="PIXEL_A" />
- <field name="PIXEL_L" />
- <field name="PIXEL_LA" />
- <field name="PIXEL_RGB" />
- <field name="PIXEL_RGBA" />
- <field name="USER" />
- </class>
- <class name="android/renderscript/Element$DataType" since="11">
- <extends name="java/lang/Enum" />
- <field name="BOOLEAN" />
- <field name="FLOAT_32" />
- <field name="FLOAT_64" />
- <field name="MATRIX_2X2" />
- <field name="MATRIX_3X3" />
- <field name="MATRIX_4X4" />
- <field name="RS_ALLOCATION" />
- <field name="RS_ELEMENT" />
- <field name="RS_MESH" />
- <field name="RS_PROGRAM_FRAGMENT" />
- <field name="RS_PROGRAM_RASTER" />
- <field name="RS_PROGRAM_STORE" />
- <field name="RS_PROGRAM_VERTEX" />
- <field name="RS_SAMPLER" />
- <field name="RS_SCRIPT" />
- <field name="RS_TYPE" />
- <field name="SIGNED_16" />
- <field name="SIGNED_32" />
- <field name="SIGNED_64" />
- <field name="SIGNED_8" />
- <field name="UNSIGNED_16" />
- <field name="UNSIGNED_32" />
- <field name="UNSIGNED_4_4_4_4" />
- <field name="UNSIGNED_5_5_5_1" />
- <field name="UNSIGNED_5_6_5" />
- <field name="UNSIGNED_64" />
- <field name="UNSIGNED_8" />
- </class>
- <class name="android/renderscript/FileA3D$EntryType" since="11">
- <extends name="java/lang/Enum" />
- <field name="MESH" />
- <field name="UNKNOWN" />
- </class>
- <class name="android/renderscript/Font$Style" since="11">
- <extends name="java/lang/Enum" />
- <field name="BOLD" />
- <field name="BOLD_ITALIC" />
- <field name="ITALIC" />
- <field name="NORMAL" />
- </class>
- <class name="android/renderscript/Mesh$Primitive" since="11">
- <extends name="java/lang/Enum" />
- <field name="LINE" />
- <field name="LINE_STRIP" />
- <field name="POINT" />
- <field name="TRIANGLE" />
- <field name="TRIANGLE_FAN" />
- <field name="TRIANGLE_STRIP" />
- </class>
- <class name="android/renderscript/Program$TextureType" since="11">
- <extends name="java/lang/Enum" />
- <field name="TEXTURE_2D" />
- <field name="TEXTURE_CUBE" />
- </class>
- <class name="android/renderscript/ProgramFragmentFixedFunction$Builder$EnvMode" since="11">
- <extends name="java/lang/Enum" />
- <field name="DECAL" />
- <field name="MODULATE" />
- <field name="REPLACE" />
- </class>
- <class name="android/renderscript/ProgramFragmentFixedFunction$Builder$Format" since="11">
- <extends name="java/lang/Enum" />
- <field name="ALPHA" />
- <field name="LUMINANCE_ALPHA" />
- <field name="RGB" />
- <field name="RGBA" />
- </class>
- <class name="android/renderscript/ProgramRaster$CullMode" since="11">
- <extends name="java/lang/Enum" />
- <field name="BACK" />
- <field name="FRONT" />
- <field name="NONE" />
- </class>
- <class name="android/renderscript/ProgramStore$BlendDstFunc" since="11">
- <extends name="java/lang/Enum" />
- <field name="DST_ALPHA" />
- <field name="ONE" />
- <field name="ONE_MINUS_DST_ALPHA" />
- <field name="ONE_MINUS_SRC_ALPHA" />
- <field name="ONE_MINUS_SRC_COLOR" />
- <field name="SRC_ALPHA" />
- <field name="SRC_COLOR" />
- <field name="ZERO" />
- </class>
- <class name="android/renderscript/ProgramStore$BlendSrcFunc" since="11">
- <extends name="java/lang/Enum" />
- <field name="DST_ALPHA" />
- <field name="DST_COLOR" />
- <field name="ONE" />
- <field name="ONE_MINUS_DST_ALPHA" />
- <field name="ONE_MINUS_DST_COLOR" />
- <field name="ONE_MINUS_SRC_ALPHA" />
- <field name="SRC_ALPHA" />
- <field name="SRC_ALPHA_SATURATE" />
- <field name="ZERO" />
- </class>
- <class name="android/renderscript/ProgramStore$DepthFunc" since="11">
- <extends name="java/lang/Enum" />
- <field name="ALWAYS" />
- <field name="EQUAL" />
- <field name="GREATER" />
- <field name="GREATER_OR_EQUAL" />
- <field name="LESS" />
- <field name="LESS_OR_EQUAL" />
- <field name="NOT_EQUAL" />
- </class>
- <class name="android/renderscript/RenderScript$Priority" since="11">
- <extends name="java/lang/Enum" />
- <field name="LOW" />
- <field name="NORMAL" />
- </class>
- <class name="android/renderscript/Sampler$Value" since="11">
- <extends name="java/lang/Enum" />
- <field name="CLAMP" />
- <field name="LINEAR" />
- <field name="LINEAR_MIP_LINEAR" />
- <field name="LINEAR_MIP_NEAREST" />
- <field name="NEAREST" />
- <field name="WRAP" />
- </class>
- <class name="android/renderscript/Type$CubemapFace" since="11">
- <extends name="java/lang/Enum" />
- <field name="NEGATIVE_X" />
- <field name="NEGATIVE_Y" />
- <field name="NEGATIVE_Z" />
- <field name="POSITVE_X" />
- <field name="POSITVE_Y" />
- <field name="POSITVE_Z" />
- </class>
- <class name="android/telephony/SmsMessage$MessageClass" since="4">
- <extends name="java/lang/Enum" />
- <field name="CLASS_0" />
- <field name="CLASS_1" />
- <field name="CLASS_2" />
- <field name="CLASS_3" />
- <field name="UNKNOWN" />
- </class>
- <class name="android/telephony/gsm/SmsMessage$MessageClass" since="1">
- <extends name="java/lang/Enum" />
- <field name="CLASS_0" />
- <field name="CLASS_1" />
- <field name="CLASS_2" />
- <field name="CLASS_3" />
- <field name="UNKNOWN" />
- </class>
- <class name="android/text/Layout$Alignment" since="1">
- <extends name="java/lang/Enum" />
- <field name="ALIGN_CENTER" />
- <field name="ALIGN_NORMAL" />
- <field name="ALIGN_OPPOSITE" />
- </class>
- <class name="android/text/TextUtils$TruncateAt" since="1">
- <extends name="java/lang/Enum" />
- <field name="END" />
- <field name="MARQUEE" since="2" />
- <field name="MIDDLE" />
- <field name="START" />
- </class>
- <class name="android/text/method/TextKeyListener$Capitalize" since="1">
- <extends name="java/lang/Enum" />
- <field name="CHARACTERS" />
- <field name="NONE" />
- <field name="SENTENCES" />
- <field name="WORDS" />
- </class>
- <class name="android/util/JsonToken" since="11">
- <extends name="java/lang/Enum" />
- <field name="BEGIN_ARRAY" />
- <field name="BEGIN_OBJECT" />
- <field name="BOOLEAN" />
- <field name="END_ARRAY" />
- <field name="END_DOCUMENT" />
- <field name="END_OBJECT" />
- <field name="NAME" />
- <field name="NULL" />
- <field name="NUMBER" />
- <field name="STRING" />
- </class>
- <class name="android/util/Xml$Encoding" since="1">
- <extends name="java/lang/Enum" />
- <field name="ISO_8859_1" />
- <field name="US_ASCII" />
- <field name="UTF_16" />
- <field name="UTF_8" />
- </class>
- <class name="android/view/ViewDebug$HierarchyTraceType" since="1">
- <extends name="java/lang/Enum" />
- <field name="BUILD_CACHE" />
- <field name="DRAW" />
- <field name="INVALIDATE" />
- <field name="INVALIDATE_CHILD" />
- <field name="INVALIDATE_CHILD_IN_PARENT" />
- <field name="ON_LAYOUT" />
- <field name="ON_MEASURE" />
- <field name="REQUEST_LAYOUT" />
- </class>
- <class name="android/view/ViewDebug$RecyclerTraceType" since="1">
- <extends name="java/lang/Enum" />
- <field name="BIND_VIEW" />
- <field name="MOVE_FROM_ACTIVE_TO_SCRAP_HEAP" />
- <field name="MOVE_TO_ACTIVE_HEAP" />
- <field name="MOVE_TO_SCRAP_HEAP" />
- <field name="NEW_VIEW" />
- <field name="RECYCLE_FROM_ACTIVE_HEAP" />
- <field name="RECYCLE_FROM_SCRAP_HEAP" />
- </class>
- <class name="android/webkit/ConsoleMessage$MessageLevel" since="8">
- <extends name="java/lang/Enum" />
- <field name="DEBUG" />
- <field name="ERROR" />
- <field name="LOG" />
- <field name="TIP" />
- <field name="WARNING" />
- </class>
- <class name="android/webkit/WebSettings$LayoutAlgorithm" since="1">
- <extends name="java/lang/Enum" />
- <field name="NARROW_COLUMNS" />
- <field name="NORMAL" />
- <field name="SINGLE_COLUMN" />
- </class>
- <class name="android/webkit/WebSettings$PluginState" since="8">
- <extends name="java/lang/Enum" />
- <field name="OFF" />
- <field name="ON" />
- <field name="ON_DEMAND" />
- </class>
- <class name="android/webkit/WebSettings$RenderPriority" since="1">
- <extends name="java/lang/Enum" />
- <field name="HIGH" />
- <field name="LOW" />
- <field name="NORMAL" />
- </class>
- <class name="android/webkit/WebSettings$TextSize" since="1">
- <extends name="java/lang/Enum" />
- <field name="LARGER" />
- <field name="LARGEST" />
- <field name="NORMAL" />
- <field name="SMALLER" />
- <field name="SMALLEST" />
- </class>
- <class name="android/webkit/WebSettings$ZoomDensity" since="7">
- <extends name="java/lang/Enum" />
- <field name="CLOSE" />
- <field name="FAR" />
- <field name="MEDIUM" />
- </class>
- <class name="android/widget/ImageView$ScaleType" since="1">
- <extends name="java/lang/Enum" />
- <field name="CENTER" />
- <field name="CENTER_CROP" />
- <field name="CENTER_INSIDE" />
- <field name="FIT_CENTER" />
- <field name="FIT_END" />
- <field name="FIT_START" />
- <field name="FIT_XY" />
- <field name="MATRIX" />
- </class>
- <class name="android/widget/TextView$BufferType" since="1">
- <extends name="java/lang/Enum" />
- <field name="EDITABLE" />
- <field name="NORMAL" />
- <field name="SPANNABLE" />
- </class>
- <class name="com/google/android/maps/MapView$ReticleDrawMode" since="1">
- <extends name="java/lang/Enum" />
- <field name="DRAW_RETICLE_NEVER" />
- <field name="DRAW_RETICLE_OVER" />
- <field name="DRAW_RETICLE_UNDER" />
- </class>
- <class name="java/lang/Thread$State" since="1">
- <extends name="java/lang/Enum" />
- <field name="BLOCKED" />
- <field name="NEW" />
- <field name="RUNNABLE" />
- <field name="TERMINATED" />
- <field name="TIMED_WAITING" />
- <field name="WAITING" />
- </class>
- <class name="java/lang/annotation/ElementType" since="1">
- <extends name="java/lang/Enum" />
- <field name="ANNOTATION_TYPE" />
- <field name="CONSTRUCTOR" />
- <field name="FIELD" />
- <field name="LOCAL_VARIABLE" />
- <field name="METHOD" />
- <field name="PACKAGE" />
- <field name="PARAMETER" />
- <field name="TYPE" />
- </class>
- <class name="java/lang/annotation/RetentionPolicy" since="1">
- <extends name="java/lang/Enum" />
- <field name="CLASS" />
- <field name="RUNTIME" />
- <field name="SOURCE" />
- </class>
- <class name="java/math/RoundingMode" since="1">
- <extends name="java/lang/Enum" />
- <field name="CEILING" />
- <field name="DOWN" />
- <field name="FLOOR" />
- <field name="HALF_DOWN" />
- <field name="HALF_EVEN" />
- <field name="HALF_UP" />
- <field name="UNNECESSARY" />
- <field name="UP" />
- </class>
- <class name="java/net/Authenticator$RequestorType" since="1">
- <extends name="java/lang/Enum" />
- <field name="PROXY" />
- <field name="SERVER" />
- </class>
- <class name="java/net/Proxy$Type" since="1">
- <extends name="java/lang/Enum" />
- <field name="DIRECT" />
- <field name="HTTP" />
- <field name="SOCKS" />
- </class>
- <class name="java/security/KeyRep$Type" since="1">
- <extends name="java/lang/Enum" />
- <field name="PRIVATE" />
- <field name="PUBLIC" />
- <field name="SECRET" />
- </class>
- <class name="java/sql/ClientInfoStatus" since="9">
- <extends name="java/lang/Enum" />
- <field name="REASON_UNKNOWN" />
- <field name="REASON_UNKNOWN_PROPERTY" />
- <field name="REASON_VALUE_INVALID" />
- <field name="REASON_VALUE_TRUNCATED" />
- </class>
- <class name="java/sql/RowIdLifetime" since="9">
- <extends name="java/lang/Enum" />
- <field name="ROWID_UNSUPPORTED" />
- <field name="ROWID_VALID_FOREVER" />
- <field name="ROWID_VALID_OTHER" />
- <field name="ROWID_VALID_SESSION" />
- <field name="ROWID_VALID_TRANSACTION" />
- </class>
- <class name="java/text/Normalizer$Form" since="9">
- <extends name="java/lang/Enum" />
- <field name="NFC" />
- <field name="NFD" />
- <field name="NFKC" />
- <field name="NFKD" />
- </class>
- <class name="java/util/Formatter$BigDecimalLayoutForm" since="1">
- <extends name="java/lang/Enum" />
- <field name="DECIMAL_FLOAT" />
- <field name="SCIENTIFIC" />
- </class>
- <class name="java/util/concurrent/TimeUnit" since="1">
- <extends name="java/lang/Enum" />
- <field name="DAYS" since="9" />
- <field name="HOURS" since="9" />
- <field name="MICROSECONDS" />
- <field name="MILLISECONDS" />
- <field name="MINUTES" since="9" />
- <field name="NANOSECONDS" />
- <field name="SECONDS" />
- </class>
- <class name="javax/net/ssl/SSLEngineResult$HandshakeStatus" since="1">
- <extends name="java/lang/Enum" />
- <field name="FINISHED" />
- <field name="NEED_TASK" />
- <field name="NEED_UNWRAP" />
- <field name="NEED_WRAP" />
- <field name="NOT_HANDSHAKING" />
- </class>
- <class name="javax/net/ssl/SSLEngineResult$Status" since="1">
- <extends name="java/lang/Enum" />
- <field name="BUFFER_OVERFLOW" />
- <field name="BUFFER_UNDERFLOW" />
- <field name="CLOSED" />
- <field name="OK" />
- </class>
- <class name="org/apache/http/conn/routing/RouteInfo$LayerType" since="1">
- <extends name="java/lang/Enum" />
- <field name="LAYERED" />
- <field name="PLAIN" />
- </class>
- <class name="org/apache/http/conn/routing/RouteInfo$TunnelType" since="1">
- <extends name="java/lang/Enum" />
- <field name="PLAIN" />
- <field name="TUNNELLED" />
- </class>
-</api>
diff --git a/eclipse/dictionary.txt b/eclipse/dictionary.txt
index cbf3d10..dbb51e5 100644
--- a/eclipse/dictionary.txt
+++ b/eclipse/dictionary.txt
@@ -292,7 +292,9 @@ textfields
thematically
themed
thumbnail
+thumbnails
timestamp
+timestamps
tmp
toolbar
tooltip
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/Hyperlinks.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/Hyperlinks.java
index 6e035af..c63ed18 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/Hyperlinks.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/Hyperlinks.java
@@ -16,14 +16,15 @@
package com.android.ide.eclipse.adt.internal.editors;
-import static com.android.util.XmlUtils.ANDROID_URI;
import static com.android.ide.common.layout.LayoutConstants.ATTR_CLASS;
import static com.android.ide.common.layout.LayoutConstants.ATTR_NAME;
import static com.android.ide.common.layout.LayoutConstants.ATTR_ON_CLICK;
import static com.android.ide.common.layout.LayoutConstants.NEW_ID_PREFIX;
import static com.android.ide.common.layout.LayoutConstants.VIEW;
import static com.android.ide.common.resources.ResourceResolver.PREFIX_ANDROID_RESOURCE_REF;
+import static com.android.ide.common.resources.ResourceResolver.PREFIX_ANDROID_THEME_REF;
import static com.android.ide.common.resources.ResourceResolver.PREFIX_RESOURCE_REF;
+import static com.android.ide.common.resources.ResourceResolver.PREFIX_THEME_REF;
import static com.android.ide.eclipse.adt.AdtConstants.ANDROID_PKG;
import static com.android.ide.eclipse.adt.AdtConstants.EXT_XML;
import static com.android.ide.eclipse.adt.AdtConstants.FN_RESOURCE_BASE;
@@ -38,6 +39,10 @@ import static com.android.sdklib.xml.AndroidManifest.ATTRIBUTE_NAME;
import static com.android.sdklib.xml.AndroidManifest.ATTRIBUTE_PACKAGE;
import static com.android.sdklib.xml.AndroidManifest.NODE_ACTIVITY;
import static com.android.sdklib.xml.AndroidManifest.NODE_SERVICE;
+import static com.android.tools.lint.detector.api.LintConstants.ANDROID_STYLE_RESOURCE_PREFIX;
+import static com.android.tools.lint.detector.api.LintConstants.NEW_ID_RESOURCE_PREFIX;
+import static com.android.tools.lint.detector.api.LintConstants.STYLE_RESOURCE_PREFIX;
+import static com.android.util.XmlUtils.ANDROID_URI;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
@@ -210,14 +215,14 @@ public class Hyperlinks {
}
String value = attribute.getValue();
- if (value.startsWith("@+")) { //$NON-NLS-1$
+ if (value.startsWith(NEW_ID_RESOURCE_PREFIX)) {
// It's a value -declaration-, nowhere else to jump
// (though we could consider jumping to the R-file; would that
// be helpful?)
return false;
}
- Pair<ResourceType,String> resource = ResourceHelper.parseResource(value);
+ Pair<ResourceType,String> resource = parseResource(value);
if (resource != null) {
ResourceType type = resource.getFirst();
if (type != null) {
@@ -1026,23 +1031,28 @@ public class Hyperlinks {
if (type == ResourceType.ID) {
// Ids are recorded in <item> tags instead of <id> tags
targetTag = "item"; //$NON-NLS-1$
- } else if (type == ResourceType.ATTR) {
+ }
+
+ Pair<File, Integer> result = findTag(name, file, parser, document, targetTag);
+ if (result == null && type == ResourceType.ATTR) {
// Attributes seem to be defined in <public> tags
targetTag = "public"; //$NON-NLS-1$
+ result = findTag(name, file, parser, document, targetTag);
}
- Element root = document.getDocumentElement();
- if (root.getTagName().equals(ROOT_ELEMENT)) {
- NodeList children = root.getChildNodes();
- for (int i = 0, n = children.getLength(); i < n; i++) {
- Node child = children.item(i);
- if (child.getNodeType() == Node.ELEMENT_NODE) {
- Element element = (Element) child;
- if (element.getTagName().equals(targetTag)) {
- String elementName = element.getAttribute(NAME_ATTR);
- if (elementName.equals(name)) {
+ return result;
+ }
- return Pair.of(file, parser.getOffset(element));
- }
+ private static Pair<File, Integer> findTag(String name, File file, OffsetTrackingParser parser,
+ Document document, String targetTag) {
+ NodeList children = document.getElementsByTagName(targetTag);
+ for (int i = 0, n = children.getLength(); i < n; i++) {
+ Node child = children.item(i);
+ if (child.getNodeType() == Node.ELEMENT_NODE) {
+ Element element = (Element) child;
+ if (element.getTagName().equals(targetTag)) {
+ String elementName = element.getAttribute(NAME_ATTR);
+ if (elementName.equals(name)) {
+ return Pair.of(file, parser.getOffset(element));
}
}
}
@@ -1067,15 +1077,17 @@ public class Hyperlinks {
}
}
- Pair<ResourceType,String> resource = ResourceHelper.parseResource(url);
+ Pair<ResourceType,String> resource = parseResource(url);
if (resource == null) {
- String androidStyle = "@android:style/"; //$NON-NLS-1$
+ String androidStyle = ANDROID_STYLE_RESOURCE_PREFIX;
if (url.startsWith(PREFIX_ANDROID_RESOURCE_REF)) {
url = androidStyle + url.substring(PREFIX_ANDROID_RESOURCE_REF.length());
+ } else if (url.startsWith(PREFIX_ANDROID_THEME_REF)) {
+ url = androidStyle + url.substring(PREFIX_ANDROID_THEME_REF.length());
} else if (url.startsWith(ANDROID_PKG + ':')) {
url = androidStyle + url.substring(ANDROID_PKG.length() + 1);
} else {
- url = "@style/" + url; //$NON-NLS-1$
+ url = STYLE_RESOURCE_PREFIX + url;
}
}
return getResourceLinks(range, url);
@@ -1087,6 +1099,16 @@ public class Hyperlinks {
return getResourceLinks(range, url, project, configuration);
}
+ /** Parse a resource reference or a theme reference and return the individual parts */
+ private static Pair<ResourceType,String> parseResource(String url) {
+ if (url.startsWith(PREFIX_THEME_REF)) {
+ url = PREFIX_RESOURCE_REF + url.substring(PREFIX_THEME_REF.length());
+ return ResourceHelper.parseResource(url);
+ }
+
+ return ResourceHelper.parseResource(url);
+ }
+
/**
* Computes hyperlinks to resource definitions for resource urls (e.g.
* {@code @android:string/ok} or {@code @layout/foo}. May create multiple links.
@@ -1101,14 +1123,15 @@ public class Hyperlinks {
@NonNull IProject project, @Nullable FolderConfiguration configuration) {
List<IHyperlink> links = new ArrayList<IHyperlink>();
- Pair<ResourceType,String> resource = ResourceHelper.parseResource(url);
+ Pair<ResourceType,String> resource = parseResource(url);
if (resource == null || resource.getFirst() == null) {
return null;
}
ResourceType type = resource.getFirst();
String name = resource.getSecond();
- boolean isFramework = url.startsWith("@android"); //$NON-NLS-1$
+ boolean isFramework = url.startsWith(PREFIX_ANDROID_RESOURCE_REF)
+ || url.startsWith(PREFIX_ANDROID_THEME_REF);
if (project == null) {
// Local reference *within* a framework
isFramework = true;
@@ -1217,10 +1240,10 @@ public class Hyperlinks {
return getStyleLinks(context, range, attribute.getValue());
}
if (attribute != null
- && attribute.getValue().startsWith(PREFIX_RESOURCE_REF)) {
+ && (attribute.getValue().startsWith(PREFIX_RESOURCE_REF)
+ || attribute.getValue().startsWith(PREFIX_THEME_REF))) {
// Instantly create links for resources since we can use the existing
// resolved maps for this and offer multiple choices for the user
-
String url = attribute.getValue();
return getResourceLinks(range, url);
}
@@ -1253,7 +1276,7 @@ public class Hyperlinks {
int offset = caretOffset;
while (offset > lineStart) {
char c = document.getChar(offset);
- if (c == '@') {
+ if (c == '@' || c == '?') {
urlStart = offset;
break;
} else if (!isValidResourceUrlChar(c)) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/ElementDescriptor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/ElementDescriptor.java
index 613a68f..7cfe791 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/ElementDescriptor.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/ElementDescriptor.java
@@ -469,4 +469,17 @@ public class ElementDescriptor implements Comparable<ElementDescriptor> {
public int compareTo(ElementDescriptor o) {
return mUiName.compareToIgnoreCase(o.mUiName);
}
+
+ /**
+ * Ensures that this view descriptor's attribute list is up to date. This is
+ * always the case for all the builtin descriptors, but for example for a
+ * custom view, it could be changing dynamically so caches may have to be
+ * recomputed. This method will return true if nothing changed, and false if
+ * it recomputed its info.
+ *
+ * @return true if the attributes are already up to date and nothing changed
+ */
+ public boolean syncAttributes() {
+ return true;
+ }
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditorMatchingStrategy.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditorMatchingStrategy.java
index 6a6b99c..4ea49f9 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditorMatchingStrategy.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditorMatchingStrategy.java
@@ -42,7 +42,8 @@ public class LayoutEditorMatchingStrategy implements IEditorMatchingStrategy {
// get the IFile object and check it's in one of the layout folders.
IFile iFile = fileInput.getFile();
- ResourceFolder resFolder = ResourceManager.getInstance().getResourceFolder(iFile);
+ ResourceManager manager = ResourceManager.getInstance();
+ ResourceFolder resFolder = manager.getResourceFolder(iFile);
// Per the IEditorMatchingStrategy documentation, editorRef.getEditorInput()
// is expensive so try exclude files that definitely don't match, such
@@ -61,6 +62,12 @@ public class LayoutEditorMatchingStrategy implements IEditorMatchingStrategy {
FileEditorInput editorFileInput = (FileEditorInput)editorInput;
IFile editorIFile = editorFileInput.getFile();
+ ResourceFolder editorFolder = manager.getResourceFolder(editorIFile);
+ if (editorFolder == null
+ || editorFolder.getType() != ResourceFolderType.LAYOUT) {
+ return false;
+ }
+
return editorIFile.getProject().equals(iFile.getProject())
&& editorIFile.getName().equals(iFile.getName());
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/CustomViewDescriptorService.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/CustomViewDescriptorService.java
index a110c65..ab4e1e9 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/CustomViewDescriptorService.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/CustomViewDescriptorService.java
@@ -16,11 +16,11 @@
package com.android.ide.eclipse.adt.internal.editors.layout.descriptors;
-import static com.android.util.XmlUtils.ANDROID_URI;
import static com.android.sdklib.SdkConstants.CLASS_VIEWGROUP;
import static com.android.tools.lint.detector.api.LintConstants.AUTO_URI;
import static com.android.tools.lint.detector.api.LintConstants.URI_PREFIX;
import static com.android.util.XmlUtils.ANDROID_NS_NAME_PREFIX;
+import static com.android.util.XmlUtils.ANDROID_URI;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
@@ -44,12 +44,14 @@ import com.android.ide.eclipse.adt.internal.sdk.ProjectState;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.resources.ResourceType;
import com.android.sdklib.IAndroidTarget;
+import com.google.common.collect.Maps;
import com.google.common.collect.ObjectArrays;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jdt.core.IClassFile;
@@ -193,7 +195,8 @@ public final class CustomViewDescriptorService {
// we have a valid parent, lets create a new ViewElementDescriptor.
List<AttributeDescriptor> attrList = new ArrayList<AttributeDescriptor>();
List<AttributeDescriptor> paramList = new ArrayList<AttributeDescriptor>();
- findCustomDescriptors(project, type, attrList, paramList);
+ Map<ResourceFile, Long> files = findCustomDescriptors(project, type,
+ attrList, paramList);
AttributeDescriptor[] attributes =
getAttributeDescriptor(type, parentDescriptor);
@@ -209,7 +212,8 @@ public final class CustomViewDescriptorService {
ViewElementDescriptor descriptor = new CustomViewDescriptor(name, fqcn,
attributes,
layoutAttributes,
- parentDescriptor.getChildren());
+ parentDescriptor.getChildren(),
+ project, files);
descriptor.setSuperClass(parentDescriptor);
synchronized (mCustomDescriptorMap) {
@@ -270,7 +274,7 @@ public final class CustomViewDescriptorService {
}
/** Compute/find the styleable resources for the given type, if possible */
- private void findCustomDescriptors(
+ private Map<ResourceFile, Long> findCustomDescriptors(
IProject project,
IType type,
List<AttributeDescriptor> customAttributes,
@@ -286,6 +290,8 @@ public final class CustomViewDescriptorService {
Set<ResourceFile> resourceFiles = findAttrsFiles(library, className);
if (resourceFiles != null && resourceFiles.size() > 0) {
String appUri = getAppResUri(project);
+ Map<ResourceFile, Long> timestamps =
+ Maps.newHashMapWithExpectedSize(resourceFiles.size());
for (ResourceFile file : resourceFiles) {
AttrsXmlParser attrsXmlParser = getParser(file);
String fqcn = type.getFullyQualifiedName();
@@ -300,8 +306,14 @@ public final class CustomViewDescriptorService {
classInfo, "Layout", null /*superClassInfo*/); //$NON-NLS-1$
attrsXmlParser.loadLayoutParamsAttributes(layoutInfo);
appendAttributes(customLayoutAttributes, layoutInfo.getAttributes(), appUri);
+
+ timestamps.put(file, file.getFile().getModificationStamp());
}
+
+ return timestamps;
}
+
+ return null;
}
/**
@@ -309,7 +321,7 @@ public final class CustomViewDescriptorService {
* attributes for the given class name
*/
@Nullable
- private Set<ResourceFile> findAttrsFiles(IProject library, String className) {
+ private static Set<ResourceFile> findAttrsFiles(IProject library, String className) {
Set<ResourceFile> resourceFiles = null;
ResourceManager manager = ResourceManager.getInstance();
ProjectResources resources = manager.getProjectResources(library);
@@ -338,7 +350,7 @@ public final class CustomViewDescriptorService {
* attrs.xml file in the same project.
*/
@Nullable
- private IProject getProjectDeclaringType(IType type) {
+ private static IProject getProjectDeclaringType(IType type) {
IClassFile classFile = type.getClassFile();
if (classFile != null) {
IPath path = classFile.getPath();
@@ -358,7 +370,7 @@ public final class CustomViewDescriptorService {
}
/** Returns the name space to use for application attributes */
- private String getAppResUri(IProject project) {
+ private static String getAppResUri(IProject project) {
String appResource;
ProjectState projectState = Sdk.getProjectState(project);
if (projectState != null && projectState.isLibrary()) {
@@ -459,7 +471,7 @@ public final class CustomViewDescriptorService {
ViewElementDescriptor descriptor = new CustomViewDescriptor(name, fqcn,
getAttributeDescriptor(type, parentDescriptor),
getLayoutAttributeDescriptors(type, parentDescriptor),
- children);
+ children, project, null);
descriptor.setSuperClass(parentDescriptor);
// add it to the map
@@ -504,10 +516,14 @@ public final class CustomViewDescriptorService {
return parentDescriptor.getLayoutAttributes();
}
- private static class CustomViewDescriptor extends ViewElementDescriptor {
+ private class CustomViewDescriptor extends ViewElementDescriptor {
+ private Map<ResourceFile, Long> mTimeStamps;
+ private IProject mProject;
+
public CustomViewDescriptor(String name, String fqcn, AttributeDescriptor[] attributes,
AttributeDescriptor[] layoutAttributes,
- ElementDescriptor[] children) {
+ ElementDescriptor[] children, IProject project,
+ Map<ResourceFile, Long> timestamps) {
super(
fqcn, // xml name
name, // ui name
@@ -519,6 +535,8 @@ public final class CustomViewDescriptorService {
children,
false // mandatory
);
+ mTimeStamps = timestamps;
+ mProject = project;
}
@Override
@@ -533,5 +551,71 @@ public final class CustomViewDescriptorService {
return iconFactory.getIcon("customView"); //$NON-NLS-1$
}
+
+ @Override
+ public boolean syncAttributes() {
+ // Check if any of the descriptors
+ if (mTimeStamps != null) {
+ // Prevent checking actual file timestamps too frequently on rapid burst calls
+ long now = System.currentTimeMillis();
+ if (now - sLastCheck < 1000) {
+ return true;
+ }
+ sLastCheck = now;
+
+ // Check whether the resource files (typically just one) which defined
+ // custom attributes for this custom view have changed, and if so,
+ // refresh the attribute descriptors.
+ // This doesn't work the cases where you add descriptors for a custom
+ // view after using it, or add attributes in a separate file, but those
+ // scenarios aren't quite as common (and would require a bit more expensive
+ // analysis.)
+ for (Map.Entry<ResourceFile, Long> entry : mTimeStamps.entrySet()) {
+ ResourceFile file = entry.getKey();
+ Long timestamp = entry.getValue();
+ boolean recompute = false;
+ if (file.getFile().getModificationStamp() > timestamp.longValue()) {
+ // One or more attributes changed: recompute
+ recompute = true;
+ mParserCache.remove(file);
+ }
+
+ if (recompute) {
+ IJavaProject javaProject = JavaCore.create(mProject);
+ String fqcn = getFullClassName();
+ IType type = null;
+ try {
+ type = javaProject.findType(fqcn);
+ } catch (CoreException e) {
+ AdtPlugin.log(e, null);
+ }
+ if (type == null || !type.exists()) {
+ return true;
+ }
+
+ List<AttributeDescriptor> attrList = new ArrayList<AttributeDescriptor>();
+ List<AttributeDescriptor> paramList = new ArrayList<AttributeDescriptor>();
+
+ mTimeStamps = findCustomDescriptors(mProject, type, attrList, paramList);
+
+ ViewElementDescriptor parentDescriptor = getSuperClassDesc();
+ AttributeDescriptor[] attributes =
+ getAttributeDescriptor(type, parentDescriptor);
+ if (!attrList.isEmpty()) {
+ attributes = join(attrList, attributes);
+ }
+ attributes = attrList.toArray(new AttributeDescriptor[attrList.size()]);
+ setAttributes(attributes);
+
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
}
+
+ /** Timestamp of the most recent {@link CustomViewDescriptor#syncAttributes} check */
+ private static long sLastCheck;
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasTransform.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasTransform.java
index 7cbb8f2..e367f9e 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasTransform.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasTransform.java
@@ -104,7 +104,7 @@ public class CanvasTransform {
*
* @return The scaled image size in pixels.
*/
- public int getScalledImgSize() {
+ public int getScaledImgSize() {
return (int) (mImgSize * mScale);
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ImageOverlay.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ImageOverlay.java
index a5df4bd..d079ff4 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ImageOverlay.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ImageOverlay.java
@@ -32,6 +32,7 @@ import org.eclipse.swt.graphics.PaletteData;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.awt.image.WritableRaster;
+import java.lang.ref.SoftReference;
/**
* The {@link ImageOverlay} class renders an image as an overlay.
@@ -56,7 +57,17 @@ public class ImageOverlay extends Overlay implements IImageFactory {
/** Current background AWT image. This is created by {@link #getImage()}, which is called
* by the LayoutLib. */
- private BufferedImage mAwtImage;
+ private SoftReference<BufferedImage> mAwtImage = new SoftReference<BufferedImage>(null);
+
+ /**
+ * Strong reference to the image in the above soft reference, to prevent
+ * garbage collection when {@link PRESCALE} is set, until the scaled image
+ * is created (lazily as part of the next paint call, where this strong
+ * reference is nulled out and the above soft reference becomes eligible to
+ * be reclaimed when memory is low.)
+ */
+ @SuppressWarnings("unused") // Used by the garbage collector to keep mAwtImage non-soft
+ private BufferedImage mAwtImageStrongRef;
/** The associated {@link LayoutCanvas}. */
private LayoutCanvas mCanvas;
@@ -109,8 +120,10 @@ public class ImageOverlay extends Overlay implements IImageFactory {
* @return The corresponding SWT image, or null.
*/
public synchronized Image setImage(BufferedImage awtImage, boolean isAlphaChannelImage) {
- if (awtImage != mAwtImage || awtImage == null) {
- mAwtImage = null;
+ BufferedImage oldAwtImage = mAwtImage.get();
+ if (awtImage != oldAwtImage || awtImage == null) {
+ mAwtImage.clear();
+ mAwtImageStrongRef = null;
if (mImage != null) {
mImage.dispose();
@@ -165,7 +178,12 @@ public class ImageOverlay extends Overlay implements IImageFactory {
*/
@Nullable
BufferedImage getAwtImage() {
- return mAwtImage;
+ BufferedImage awtImage = mAwtImage.get();
+ if (awtImage == null && mImage != null) {
+ awtImage = SwtUtils.convertToAwt(mImage);
+ }
+
+ return awtImage;
}
@Override
@@ -184,11 +202,12 @@ public class ImageOverlay extends Overlay implements IImageFactory {
// This is done lazily in paint rather than when the image changes because
// the image must be rescaled each time the zoom level changes, which varies
// independently from when the image changes.
- if (PRESCALE && mAwtImage != null) {
+ BufferedImage awtImage = mAwtImage.get();
+ if (PRESCALE && awtImage != null) {
if (mPreScaledImage == null ||
- mPreScaledImage.getImageData().width != hi.getScalledImgSize()) {
- double xScale = hi.getScalledImgSize() / (double) mAwtImage.getWidth();
- double yScale = vi.getScalledImgSize() / (double) mAwtImage.getHeight();
+ mPreScaledImage.getImageData().width != hi.getScaledImgSize()) {
+ double xScale = hi.getScaledImgSize() / (double) awtImage.getWidth();
+ double yScale = vi.getScaledImgSize() / (double) awtImage.getHeight();
BufferedImage scaledAwtImage;
// NOTE: == comparison on floating point numbers is okay
@@ -198,16 +217,18 @@ public class ImageOverlay extends Overlay implements IImageFactory {
// of rounding errors.
if (xScale == 1.0 && yScale == 1.0) {
// Scaling to 100% is easy!
- scaledAwtImage = mAwtImage;
+ scaledAwtImage = awtImage;
} else {
- scaledAwtImage = ImageUtils.scale(mAwtImage, xScale, yScale);
+ scaledAwtImage = ImageUtils.scale(awtImage, xScale, yScale);
}
- assert scaledAwtImage.getWidth() == hi.getScalledImgSize();
+ assert scaledAwtImage.getWidth() == hi.getScaledImgSize();
if (mPreScaledImage != null && !mPreScaledImage.isDisposed()) {
mPreScaledImage.dispose();
}
mPreScaledImage = SwtUtils.convertToSwt(mCanvas.getDisplay(), scaledAwtImage,
true /*transferAlpha*/, -1);
+ // We can't just clear the mAwtImageStrongRef here, because if the
+ // zooming factor changes, we may need to use it again
}
if (mPreScaledImage != null) {
@@ -230,8 +251,8 @@ public class ImageOverlay extends Overlay implements IImageFactory {
vi.getImgSize(), // srcHeight
hi.translate(0), // destX
vi.translate(0), // destY
- hi.getScalledImgSize(), // destWidth
- vi.getScalledImgSize()); // destHeight
+ hi.getScaledImgSize(), // destWidth
+ vi.getScaledImgSize()); // destHeight
if (oldAlias != -2) {
gc_setAntialias(gc, oldAlias);
@@ -349,14 +370,19 @@ public class ImageOverlay extends Overlay implements IImageFactory {
*/
@Override
public BufferedImage getImage(int w, int h) {
- if (mAwtImage == null ||
- mAwtImage.getWidth() != w ||
- mAwtImage.getHeight() != h) {
-
- mAwtImage = SwtReadyBufferedImage.createImage(w, h, getDevice());
+ BufferedImage awtImage = mAwtImage.get();
+ if (awtImage == null ||
+ awtImage.getWidth() != w ||
+ awtImage.getHeight() != h) {
+ mAwtImage.clear();
+ awtImage = SwtReadyBufferedImage.createImage(w, h, getDevice());
+ mAwtImage = new SoftReference<BufferedImage>(awtImage);
+ if (PRESCALE) {
+ mAwtImageStrongRef = awtImage;
+ }
}
- return mAwtImage;
+ return awtImage;
}
/**
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlinePage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlinePage.java
index 3524970..9261aff 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlinePage.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlinePage.java
@@ -736,7 +736,8 @@ public class OutlinePage extends ContentOutlinePage
// Temporary diagnostics code when developing GridLayout
if (GridLayoutRule.sDebugGridLayout) {
String namespace;
- if (e.getParentNode().getNodeName().equals(GRID_LAYOUT)) {
+ if (e.getParentNode() != null
+ && e.getParentNode().getNodeName().equals(GRID_LAYOUT)) {
namespace = ANDROID_URI;
} else {
IProject project = mGraphicalEditorPart.getProject();
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/uimodel/UiViewElementNode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/uimodel/UiViewElementNode.java
index ec19ea7..fef6022 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/uimodel/UiViewElementNode.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/uimodel/UiViewElementNode.java
@@ -66,6 +66,9 @@ public class UiViewElementNode extends UiElementNode {
*/
@Override
public AttributeDescriptor[] getAttributeDescriptors() {
+ if (!getDescriptor().syncAttributes()) {
+ mCachedAttributeDescriptors = null;
+ }
if (mCachedAttributeDescriptors != null) {
return mCachedAttributeDescriptors;
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/ActivityPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/ActivityPage.java
index 22eb81a..9a61b4f 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/ActivityPage.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/ActivityPage.java
@@ -16,9 +16,6 @@
package com.android.ide.eclipse.adt.internal.wizards.templates;
import static com.android.ide.eclipse.adt.internal.wizards.templates.NewTemplateWizard.ACTIVITY_TEMPLATES;
-import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_DESCRIPTION;
-import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_NAME;
-import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_THUMB;
import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.PREVIEW_PADDING;
import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.PREVIEW_WIDTH;
@@ -41,8 +38,6 @@ import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.List;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
import java.io.InputStream;
@@ -121,18 +116,17 @@ class ActivityPage extends WizardPage implements SelectionListener {
setPreview(mValues.activityValues.getTemplateName());
}
- private void setPreview(String template) {
+ private void setPreview(String templateName) {
Image oldImage = mPreviewImage;
mPreviewImage = null;
String title = "";
String description = "";
- Document doc = TemplateHandler.getMetadataDocument(template);
- if (doc != null) {
- Element root = doc.getDocumentElement();
- String thumb = root.getAttribute(ATTR_THUMB);
+ TemplateMetadata template = TemplateHandler.getTemplate(templateName);
+ if (template != null) {
+ String thumb = template.getThumbnailPath();
if (thumb != null && !thumb.isEmpty()) {
- String filePath = TemplateHandler.getTemplatePath(template) + '/' + thumb;
+ String filePath = TemplateHandler.getTemplatePath(templateName) + '/' + thumb;
InputStream input = AdtPlugin.readEmbeddedFileAsStream(filePath);
if (input != null) {
try {
@@ -143,8 +137,8 @@ class ActivityPage extends WizardPage implements SelectionListener {
}
}
}
- title = root.getAttribute(ATTR_NAME);
- description = root.getAttribute(ATTR_DESCRIPTION);
+ title = template.getTitle();
+ description = template.getDescription();
}
mHeading.setText(title);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectWizard.java
index 9df974f..60f7a9e 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectWizard.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectWizard.java
@@ -317,7 +317,7 @@ public class NewProjectWizard extends Wizard implements INewWizard {
parameters.put(ATTR_TARGET_API, paramMap.get(ATTR_TARGET_API));
- TemplateHandler activityTemplate = activityValues.getTemplate();
+ TemplateHandler activityTemplate = activityValues.getTemplateHandler();
activityTemplate.setBackupMergedFiles(false);
activityTemplate.render(outputPath, parameters);
List<String> filesToOpen = activityTemplate.getFilesToOpen();
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplatePage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplatePage.java
index f89fd11..a6fdf48 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplatePage.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplatePage.java
@@ -16,10 +16,8 @@
package com.android.ide.eclipse.adt.internal.wizards.templates;
import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_DEFAULT;
-import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_DESCRIPTION;
import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_ID;
import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_NAME;
-import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_THUMB;
import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.PREVIEW_PADDING;
import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.PREVIEW_WIDTH;
@@ -57,7 +55,6 @@ import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
-import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
@@ -160,16 +157,15 @@ public class NewTemplatePage extends WizardPage
// Add parameters
mFirst = null;
- Document doc = mValues.getTemplate().getMetadataDocument();
+ TemplateMetadata template = mValues.getTemplateHandler().getTemplate();
String thumb = null;
- if (doc != null) {
- Element root = doc.getDocumentElement();
- thumb = root.getAttribute(ATTR_THUMB);
- String title = root.getAttribute(ATTR_NAME);
+ if (template != null) {
+ thumb = template.getThumbnailPath();
+ String title = template.getTitle();
if (!title.isEmpty()) {
setTitle(title);
}
- String description = root.getAttribute(ATTR_DESCRIPTION);
+ String description = template.getDescription();
if (!description.isEmpty()) {
setDescription(description);
}
@@ -180,8 +176,7 @@ public class NewTemplatePage extends WizardPage
seen = new HashSet<String>();
}
-
- List<Parameter> parameters = Parameter.getParameters(doc);
+ List<Parameter> parameters = template.getParameters();
mParameters = new ArrayList<Parameter>(parameters.size());
for (Parameter parameter : parameters) {
Parameter.Type type = parameter.type;
@@ -288,19 +283,14 @@ public class NewTemplatePage extends WizardPage
int selected = 0;
List<String> ids = Lists.newArrayList();
List<String> labels = Lists.newArrayList();
- List<String> thumbs = Lists.newArrayList();
for (int i = 0, n = options.size(); i < n; i++) {
Element option = options.get(i);
String optionId = option.getAttribute(ATTR_ID);
assert optionId != null && !optionId.isEmpty() : ATTR_ID;
- String optionThumb = option.getAttribute(ATTR_THUMB);
String isDefault = option.getAttribute(ATTR_DEFAULT);
if (isDefault != null && !isDefault.isEmpty() &&
Boolean.valueOf(isDefault)) {
selected = i;
- if (optionThumb != null && !optionThumb.isEmpty()) {
- thumb = optionThumb;
- }
}
NodeList childNodes = option.getChildNodes();
assert childNodes.getLength() == 1 &&
@@ -308,11 +298,9 @@ public class NewTemplatePage extends WizardPage
String optionLabel = childNodes.item(0).getNodeValue().trim();
ids.add(optionId);
labels.add(optionLabel);
- thumbs.add(optionThumb);
}
combo.setData(parameter);
parameter.control = combo;
- combo.setData(ATTR_THUMB, thumbs.toArray(new String[thumbs.size()]));
combo.setData(ATTR_ID, ids.toArray(new String[ids.size()]));
assert labels.size() > 0;
combo.setItems(labels.toArray(new String[labels.size()]));
@@ -371,10 +359,14 @@ public class NewTemplatePage extends WizardPage
}
private void setPreview(String thumb) {
+ if (thumb == null) {
+ return;
+ }
+
Image oldImage = mPreviewImage;
mPreviewImage = null;
- byte[] data = mValues.getTemplate().readTemplateResource(thumb);
+ byte[] data = mValues.getTemplateHandler().readTemplateResource(thumb);
if (data != null) {
try {
mPreviewImage = new Image(getControl().getDisplay(),
@@ -546,15 +538,15 @@ public class NewTemplatePage extends WizardPage
if (index != -1 && index < optionIds.length) {
String optionId = optionIds[index];
editParameter(combo, optionId);
- String[] thumbs = (String[]) combo.getData(ATTR_THUMB);
- String thumb = thumbs[index];
- if (thumb != null && !thumb.isEmpty()) {
- setPreview(thumb);
- }
+ TemplateMetadata template = mValues.getTemplateHandler().getTemplate();
+ setPreview(template.getThumbnailPath());
}
} else if (source instanceof Button) {
Button button = (Button) source;
editParameter(button, button.getSelection());
+
+ TemplateMetadata template = mValues.getTemplateHandler().getTemplate();
+ setPreview(template.getThumbnailPath());
}
validatePage();
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplateWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplateWizard.java
index 5fe91c5..b7ad998 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplateWizard.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplateWizard.java
@@ -119,8 +119,8 @@ public class NewTemplateWizard extends Wizard implements INewWizard {
parameters.put(ATTR_TARGET_API, manifest.getTargetSdkVersion());
File outputPath = AdtUtils.getAbsolutePath(project).toFile();
- TemplateHandler template = mValues.getTemplate();
- template.render(outputPath, parameters);
+ TemplateHandler handler = mValues.getTemplateHandler();
+ handler.render(outputPath, parameters);
try {
project.refreshLocal(DEPTH_INFINITE, new NullProgressMonitor());
@@ -128,7 +128,7 @@ public class NewTemplateWizard extends Wizard implements INewWizard {
AdtPlugin.log(e, null);
}
- List<String> filesToOpen = template.getFilesToOpen();
+ List<String> filesToOpen = handler.getFilesToOpen();
NewTemplateWizard.openFiles(project, filesToOpen, mWorkbench);
return true;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplateWizardState.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplateWizardState.java
index cb32a4f..dc75a71 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplateWizardState.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplateWizardState.java
@@ -37,7 +37,7 @@ public class NewTemplateWizardState {
private String mTemplateName = BLANK_ACTIVITY;
/** Template handler responsible for instantiating templates and reading resources */
- private TemplateHandler mTemplate;
+ private TemplateHandler mTemplateHandler;
/** Configured parameters, by id */
public final Map<String, Object> parameters = new HashMap<String, Object>();
@@ -78,23 +78,23 @@ public class NewTemplateWizardState {
if (!templateName.equals(mTemplateName)) {
mTemplateName = templateName;
mTemplateLocation = null;
- mTemplate = null;
+ mTemplateHandler = null;
}
}
@NonNull
- TemplateHandler getTemplate() {
- if (mTemplate == null) {
+ TemplateHandler getTemplateHandler() {
+ if (mTemplateHandler == null) {
File inputPath;
if (mTemplateLocation != null) {
inputPath = mTemplateLocation;
} else {
inputPath = new File(TemplateHandler.getTemplatePath(mTemplateName));
}
- mTemplate = TemplateHandler.createFromPath(inputPath);
+ mTemplateHandler = TemplateHandler.createFromPath(inputPath);
}
- return mTemplate;
+ return mTemplateHandler;
}
// For template development/testing only
@@ -102,7 +102,7 @@ public class NewTemplateWizardState {
if (!file.equals(mTemplateLocation)) {
mTemplateLocation = file;
mTemplateName = null;
- mTemplate = null;
+ mTemplateHandler = null;
}
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/Parameter.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/Parameter.java
index 1fad0e1..0b8b952 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/Parameter.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/Parameter.java
@@ -21,7 +21,6 @@ import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHan
import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_ID;
import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_NAME;
import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_SUGGEST;
-import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.TAG_PARAMETER;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
@@ -37,13 +36,9 @@ import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.dialogs.IInputValidator;
import org.eclipse.jface.fieldassist.ControlDecoration;
import org.eclipse.swt.widgets.Control;
-import org.w3c.dom.Document;
import org.w3c.dom.Element;
-import org.w3c.dom.NodeList;
-import java.util.ArrayList;
import java.util.EnumSet;
-import java.util.List;
import java.util.Locale;
/**
@@ -142,24 +137,7 @@ class Parameter {
}
}
- /** The type of parameter. Must be one of
- * <ul>
- * <li> string
- * <li> id
- * <li> class
- * <li> boolean
- * <li> package
- * <li> apiLevel
- * <li> enum (must contain option children)
- * <li> Resource types:
- * <ul>
- * <li> layout
- * <li> <i>more to come</i>
- * </ul>
- * </ul>
- * <p>
- * TODO: Switch to an enum
- */
+ /** The type of parameter */
@NonNull
public final Type type;
@@ -346,16 +324,4 @@ class Parameter {
return mValidator;
}
-
- @NonNull
- static List<Parameter> getParameters(@NonNull Document document) {
- NodeList parameters = document.getElementsByTagName(TAG_PARAMETER);
- List<Parameter> list = new ArrayList<Parameter>(parameters.getLength());
- for (int index = 0, max = parameters.getLength(); index < max; index++) {
- Element element = (Element) parameters.item(index);
- list.add(new Parameter(element));
- }
-
- return list;
- }
} \ No newline at end of file
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/TemplateHandler.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/TemplateHandler.java
index 8864502..dc0c898 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/TemplateHandler.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/TemplateHandler.java
@@ -117,6 +117,8 @@ class TemplateHandler {
static final String TAG_COPY = "copy"; //$NON-NLS-1$
static final String TAG_INSTANTIATE = "instantiate"; //$NON-NLS-1$
static final String TAG_OPEN = "open"; //$NON-NLS-1$
+ static final String TAG_THUMB = "thumb"; //$NON-NLS-1$
+ static final String TAG_THUMBS = "thumbs"; //$NON-NLS-1$
static final String ATTR_VALUE = "value"; //$NON-NLS-1$
static final String ATTR_DEFAULT = "default"; //$NON-NLS-1$
static final String ATTR_SUGGEST = "suggest"; //$NON-NLS-1$
@@ -129,7 +131,6 @@ class TemplateHandler {
static final String ATTR_TO = "to"; //$NON-NLS-1$
static final String ATTR_FROM = "from"; //$NON-NLS-1$
static final String ATTR_CONSTRAINTS = "constraints";//$NON-NLS-1$
- static final String ATTR_THUMB = "thumb"; //$NON-NLS-1$
/** Default padding to apply in wizards around the thumbnail preview images */
static final int PREVIEW_PADDING = 10;
@@ -161,6 +162,11 @@ class TemplateHandler {
*/
private boolean mBackupMergedFiles = true;
+ /**
+ * Template metadata
+ */
+ private TemplateMetadata mTemplate;
+
/** Creates a new {@link TemplateHandler} for the given root path */
static TemplateHandler createFromPath(File rootPath) {
return new TemplateHandler(rootPath);
@@ -222,25 +228,38 @@ class TemplateHandler {
}
@Nullable
- public Document getMetadataDocument() {
- String xml = readTemplateTextResource(TEMPLATE_XML);
- if (xml != null) {
- return DomUtilities.parseDocument(xml, true);
- } else {
- return null;
+ public TemplateMetadata getTemplate() {
+ if (mTemplate == null) {
+ String xml = readTemplateTextResource(TEMPLATE_XML);
+ if (xml != null) {
+ Document doc = DomUtilities.parseDocument(xml, true);
+ if (doc != null && doc.getDocumentElement() != null) {
+ mTemplate = new TemplateMetadata(doc);
+ }
+ }
}
+
+ return mTemplate;
}
- public static Document getMetadataDocument(String templateName) {
+ @Nullable
+ public static TemplateMetadata getTemplate(String templateName) {
String relative = getTemplatePath(templateName) + '/' +TEMPLATE_XML;
String xml = AdtPlugin.readEmbeddedTextFile(relative);
- return DomUtilities.parseDocument(xml, true);
+ Document doc = DomUtilities.parseDocument(xml, true);
+ if (doc != null && doc.getDocumentElement() != null) {
+ return new TemplateMetadata(doc);
+ }
+
+ return null;
}
+ @NonNull
public static String getTemplatePath(String templateName) {
return TEMPLATE_PREFIX + templateName;
}
+ @NonNull
public String getResourcePath(String templateName) {
return new File(mRootPath.getPath(), templateName).getPath();
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/TemplateMetadata.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/TemplateMetadata.java
new file mode 100644
index 0000000..eac818a
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/TemplateMetadata.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ide.eclipse.adt.internal.wizards.templates;
+
+import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_DESCRIPTION;
+import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_NAME;
+import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.TAG_PARAMETER;
+import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.TAG_THUMB;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.ide.eclipse.adt.AdtPlugin;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/** An ADT template along with metadata */
+class TemplateMetadata {
+ private final Document mDocument;
+ private final List<Parameter> mParameters;
+ private final Map<String, Parameter> mParameterMap;
+
+ TemplateMetadata(@NonNull Document document) {
+ mDocument = document;
+
+ NodeList parameters = mDocument.getElementsByTagName(TAG_PARAMETER);
+ mParameters = new ArrayList<Parameter>(parameters.getLength());
+ mParameterMap = new HashMap<String, Parameter>(parameters.getLength());
+ for (int index = 0, max = parameters.getLength(); index < max; index++) {
+ Element element = (Element) parameters.item(index);
+ Parameter parameter = new Parameter(element);
+ mParameters.add(parameter);
+ if (parameter.id != null) {
+ mParameterMap.put(parameter.id, parameter);
+ }
+ }
+ }
+
+ @Nullable
+ String getTitle() {
+ String name = mDocument.getDocumentElement().getAttribute(ATTR_NAME);
+ if (name != null && !name.isEmpty()) {
+ return name;
+ }
+
+ return null;
+ }
+
+ @Nullable
+ String getDescription() {
+ String description = mDocument.getDocumentElement().getAttribute(ATTR_DESCRIPTION);
+ if (description != null && !description.isEmpty()) {
+ return description;
+ }
+
+ return null;
+ }
+
+ @Nullable
+ String getThumbnailPath() {
+ // Apply selector logic. Pick the thumb first thumb that satisfies the largest number
+ // of conditions.
+ NodeList thumbs = mDocument.getElementsByTagName(TAG_THUMB);
+ if (thumbs.getLength() == 0) {
+ return null;
+ }
+
+
+ int bestMatchCount = 0;
+ Element bestMatch = null;
+
+ for (int i = 0, n = thumbs.getLength(); i < n; i++) {
+ Element thumb = (Element) thumbs.item(i);
+
+ NamedNodeMap attributes = thumb.getAttributes();
+ if (bestMatch == null && attributes.getLength() == 0) {
+ bestMatch = thumb;
+ } else if (attributes.getLength() <= bestMatchCount) {
+ // Already have a match with this number of attributes, no point checking
+ continue;
+ } else {
+ boolean match = true;
+ for (int j = 0, max = attributes.getLength(); j < max; j++) {
+ Attr attribute = (Attr) attributes.item(j);
+ Parameter parameter = mParameterMap.get(attribute.getName());
+ if (parameter == null) {
+ AdtPlugin.log(null, "Unexpected parameter in template thumbnail: %1$s",
+ attribute.getName());
+ continue;
+ }
+ String thumbNailValue = attribute.getValue();
+ String editedValue = parameter.value != null ? parameter.value.toString() : "";
+ if (!thumbNailValue.equals(editedValue)) {
+ match = false;
+ break;
+ }
+ }
+ if (match) {
+ bestMatch = thumb;
+ bestMatchCount = attributes.getLength();
+ }
+ }
+ }
+
+ if (bestMatch != null) {
+ NodeList children = bestMatch.getChildNodes();
+ for (int i = 0, n = children.getLength(); i < n; i++) {
+ Node child = children.item(i);
+ if (child.getNodeType() == Node.TEXT_NODE) {
+ return child.getNodeValue().trim();
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /** Returns the list of available parameters */
+ @NonNull
+ List<Parameter> getParameters() {
+ return mParameters;
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/template.xml b/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/template.xml
index 6294592..302e2cc 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/template.xml
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/template.xml
@@ -1,8 +1,7 @@
<?xml version="1.0"?>
<template
name="New Blank Activity"
- description="Creates a new blank activity, with optional inner navigation."
- thumb="template_blank_activity.png">
+ description="Creates a new blank activity, with optional inner navigation.">
<category value="Activities" />
@@ -30,11 +29,11 @@
type="enum"
default="none"
help="The type of navigation to use for the activity" >
- <option id="none" default="true" thumb="template_blank_activity.png">None</option>
- <option id="tabs" thumb="template_blank_activity_tabs.png">Tabs</option>
- <option id="tabs_pager" thumb="template_blank_activity_tabs_pager.png">Tabs + Swipe</option>
- <option id="pager_strip" thumb="template_blank_activity_pager.png">Swipe Views + Title Strip</option>
- <option id="dropdown" thumb="template_blank_activity_dropdown.png">Dropdown</option>
+ <option id="none" default="true">None</option>
+ <option id="tabs">Tabs</option>
+ <option id="tabs_pager">Tabs + Swipe</option>
+ <option id="pager_strip">Swipe Views + Title Strip</option>
+ <option id="dropdown">Dropdown</option>
</parameter>
<parameter
@@ -52,6 +51,18 @@
constraints="package"
default="com.mycompany.myapp" />
+ <!-- 128x128 thumbnails relative to template.xml -->
+ <thumbs>
+ <!-- default thumbnail is required -->
+ <thumb>template_blank_activity.png</thumb>
+ <!-- attributes act as selectors based on chosen parameters -->
+ <thumb navType="none">template_blank_activity.png</thumb>
+ <thumb navType="tabs">template_blank_activity_tabs.png</thumb>
+ <thumb navType="tabs_pager">template_blank_activity_tabs_pager.png</thumb>
+ <thumb navType="pager_strip">template_blank_activity_pager.png</thumb>
+ <thumb navType="dropdown">template_blank_activity_dropdown.png</thumb>
+ </thumbs>
+
<globals file="globals.xml.ftl" />
<execute file="recipe.xml.ftl" />
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/MasterDetailFlow/template.xml b/eclipse/plugins/com.android.ide.eclipse.adt/templates/MasterDetailFlow/template.xml
index bc01747..0eed682 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/MasterDetailFlow/template.xml
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/templates/MasterDetailFlow/template.xml
@@ -1,8 +1,11 @@
<?xml version="1.0"?>
<template
name="New Master/Detail Flow"
- description="Creates a new master/detail flow, which is two columns on tablets, and one column on smaller screens. This creates a master fragment, detail fragment, and two activities."
- thumb="template_master_detail.png">
+ description="Creates a new master/detail flow, which is two columns on tablets, and one column on smaller screens. This creates a master fragment, detail fragment, and two activities.">
+
+ <thumbs>
+ <thumb>template_master_detail.png</thumb>
+ </thumbs>
<category value="Flows" />
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/NewAndroidApplication/template.xml b/eclipse/plugins/com.android.ide.eclipse.adt/templates/NewAndroidApplication/template.xml
index 60f5363..84ba6c7 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/NewAndroidApplication/template.xml
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/templates/NewAndroidApplication/template.xml
@@ -1,8 +1,11 @@
<?xml version="1.0"?>
<template
name="New Android Application"
- description="Creates a new Android application with an activity."
- thumb="template_new_project.png">>
+ description="Creates a new Android application with an activity.">
+
+ <thumbs>
+ <thumb>template_new_project.png</thumb>
+ </thumbs>
<category value="Applications" />
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/HyperlinksTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/HyperlinksTest.java
index 0d42357..a54376d 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/HyperlinksTest.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/HyperlinksTest.java
@@ -19,8 +19,6 @@ import static com.android.sdklib.SdkConstants.FD_SOURCES;
import com.android.ide.common.resources.ResourceFile;
import com.android.ide.eclipse.adt.AdtUtils;
-import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
-import com.android.ide.eclipse.adt.internal.editors.Hyperlinks;
import com.android.ide.eclipse.adt.internal.editors.Hyperlinks.ResourceLink;
import com.android.ide.eclipse.adt.internal.editors.Hyperlinks.XmlResolver;
import com.android.ide.eclipse.adt.internal.editors.layout.refactoring.AdtProjectTest;
@@ -180,6 +178,12 @@ public class HyperlinksTest extends AdtProjectTest {
"class=\"com.and^roid.eclipse.tests.TestFragment\"");
}
+ public void testNavigate15() throws Exception {
+ // Check navigating to a theme resource
+ checkXmlNavigation("navigation1.xml", "res/layout/navigation1.xml",
+ "?android:attr/alert^DialogStyle");
+ }
+
// Left to test:
// onClick handling
// class attributes
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigation1-expected-navigate15.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigation1-expected-navigate15.txt
new file mode 100644
index 0000000..e36c5f3
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigation1-expected-navigate15.txt
@@ -0,0 +1,7 @@
+Go To Declaration in navigation1.xml for ?android:attr/alert^DialogStyle:
+Open Declaration in values/attrs.xml : [?android:attr/alertDialogStyle]
+ data/res/values/attrs.xml
+
+
+After open, the selected text is:
+ <attr name="alertDialogStyle" format="reference" />^
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigation1.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigation1.xml
index 9c175fc..e7ac4bc 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigation1.xml
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigation1.xml
@@ -13,4 +13,5 @@
<EditText
android:text="@android:string/ok"
</EditText>
+ <EditText android:text="?android:attr/alertDialogStyle" />
</LinearLayout>
diff --git a/files/ant/build.xml b/files/ant/build.xml
index 1f85f5e..cd1bc7d 100644
--- a/files/ant/build.xml
+++ b/files/ant/build.xml
@@ -648,7 +648,8 @@
nonConstantId="${android.library}"
libraryResFolderPathRefid="project.library.res.folder.path"
libraryPackagesRefid="project.library.packages"
- ignoreAssets="${aapt.ignore.assets}">
+ ignoreAssets="${aapt.ignore.assets}"
+ proguardFile="${out.absolute.dir}/proguard.txt">
<res path="${out.res.absolute.dir}" />
<res path="${resource.absolute.dir}" />
</aapt>
@@ -821,6 +822,7 @@
destfile="${preobfuscate.jar.file}" />
<proguard>
-include "${proguard.configcmd}"
+ -include "${out.absolute.dir}/proguard.txt"
-injars ${project.all.classes.value}
-outjars "${obfuscated.jar.file}"
-libraryjars ${project.target.classpath.value}
diff --git a/files/proguard-android.txt b/files/proguard-android.txt
index 6613823..3cc5c8a 100644
--- a/files/proguard-android.txt
+++ b/files/proguard-android.txt
@@ -24,15 +24,7 @@
# file from your project's proguard.config path property.
-keepattributes *Annotation*
--keep public class * extends android.app.Activity
--keep public class * extends android.app.Application
--keep public class * extends android.app.Service
--keep public class * extends android.content.BroadcastReceiver
--keep public class * extends android.content.ContentProvider
--keep public class * extends android.app.backup.BackupAgent
--keep public class * extends android.preference.Preference
--keep public class * extends android.support.v4.app.Fragment
--keep public class * extends android.app.Fragment
+-keep public class com.google.vending.licensing.ILicensingService
-keep public class com.android.vending.licensing.ILicensingService
# For native methods, see http://proguard.sourceforge.net/manual/examples.html#native
@@ -40,21 +32,14 @@
native <methods>;
}
--keep public class * extends android.view.View {
- public <init>(android.content.Context);
- public <init>(android.content.Context, android.util.AttributeSet);
- public <init>(android.content.Context, android.util.AttributeSet, int);
- public void set*(...);
-}
-
--keepclasseswithmembers class * {
- public <init>(android.content.Context, android.util.AttributeSet);
-}
-
--keepclasseswithmembers class * {
- public <init>(android.content.Context, android.util.AttributeSet, int);
+# keep setters in Views so that animations can still work.
+# see http://proguard.sourceforge.net/manual/examples.html#beans
+-keepclassmembers public class * extends android.view.View {
+ void set*(***);
+ *** get*();
}
+# We want to keep methods in Activity that could be used in the XML attribute onClick
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
}
diff --git a/templates/build.template b/templates/build.template
index 1ab7ea2..aea57a2 100644
--- a/templates/build.template
+++ b/templates/build.template
@@ -28,6 +28,15 @@
-->
<property file="ant.properties" />
+ <!-- if sdk.dir was not set from one of the property file, then
+ get it from the ANDROID_HOME env var.
+ This must be done before we load project.properties since
+ the proguard config can use sdk.dir -->
+ <property environment="env" />
+ <condition property="sdk.dir" value="${env.ANDROID_HOME}">
+ <isset property="env.ANDROID_HOME" />
+ </condition>
+
<!-- The project.properties file is created and updated by the 'android'
tool, as well as ADT.
@@ -39,13 +48,6 @@
application and should be checked into Version Control Systems. -->
<loadproperties srcFile="project.properties" />
- <!-- if sdk.dir was not set from one of the property file, then
- get it from the ANDROID_HOME env var. -->
- <property environment="env" />
- <condition property="sdk.dir" value="${env.ANDROID_HOME}">
- <isset property="env.ANDROID_HOME" />
- </condition>
-
<!-- quick check on sdk.dir -->
<fail
message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
diff --git a/testapps/customPropAnimTest/.classpath b/testapps/customPropAnimTest/.classpath
new file mode 100644
index 0000000..a4763d1
--- /dev/null
+++ b/testapps/customPropAnimTest/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="src" path="gen"/>
+ <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
+ <classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
+ <classpathentry kind="output" path="bin/classes"/>
+</classpath>
diff --git a/testapps/customPropAnimTest/.project b/testapps/customPropAnimTest/.project
new file mode 100644
index 0000000..0d12fe9
--- /dev/null
+++ b/testapps/customPropAnimTest/.project
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>customPropAnimTest</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>com.android.ide.eclipse.adt.ApkBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>com.android.ide.eclipse.adt.AndroidNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/testapps/customPropAnimTest/AndroidManifest.xml b/testapps/customPropAnimTest/AndroidManifest.xml
new file mode 100644
index 0000000..72c58d0
--- /dev/null
+++ b/testapps/customPropAnimTest/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.custompropertyanimation"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-sdk android:minSdkVersion="15" />
+
+ <application
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name" >
+ <activity
+ android:name=".CustomPropertyAnimationActivity"
+ android:label="@string/app_name" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest> \ No newline at end of file
diff --git a/testapps/customPropAnimTest/build.xml b/testapps/customPropAnimTest/build.xml
new file mode 100644
index 0000000..d6ee0bc
--- /dev/null
+++ b/testapps/customPropAnimTest/build.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="CustomPropertyAnimationActivity" default="help">
+
+ <!-- The local.properties file is created and updated by the 'android' tool.
+ It contains the path to the SDK. It should *NOT* be checked into
+ Version Control Systems. -->
+ <property file="local.properties" />
+
+ <!-- The ant.properties file can be created by you. It is only edited by the
+ 'android' tool to add properties to it.
+ This is the place to change some Ant specific build properties.
+ Here are some properties you may want to change/update:
+
+ source.dir
+ The name of the source directory. Default is 'src'.
+ out.dir
+ The name of the output directory. Default is 'bin'.
+
+ For other overridable properties, look at the beginning of the rules
+ files in the SDK, at tools/ant/build.xml
+
+ Properties related to the SDK location or the project target should
+ be updated using the 'android' tool with the 'update' action.
+
+ This file is an integral part of the build system for your
+ application and should be checked into Version Control Systems.
+
+ -->
+ <property file="ant.properties" />
+
+ <!-- The project.properties file is created and updated by the 'android'
+ tool, as well as ADT.
+
+ This contains project specific properties such as project target, and library
+ dependencies. Lower level build properties are stored in ant.properties
+ (or in .classpath for Eclipse projects).
+
+ This file is an integral part of the build system for your
+ application and should be checked into Version Control Systems. -->
+ <loadproperties srcFile="project.properties" />
+
+ <!-- if sdk.dir was not set from one of the property file, then
+ get it from the ANDROID_HOME env var. -->
+ <property environment="env" />
+ <condition property="sdk.dir" value="${env.ANDROID_HOME}">
+ <isset property="env.ANDROID_HOME" />
+ </condition>
+
+ <!-- quick check on sdk.dir -->
+ <fail
+ message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
+ unless="sdk.dir"
+ />
+
+ <!--
+ Import per project custom build rules if present at the root of the project.
+ This is the place to put custom intermediary targets such as:
+ -pre-build
+ -pre-compile
+ -post-compile (This is typically used for code obfuscation.
+ Compiled code location: ${out.classes.absolute.dir}
+ If this is not done in place, override ${out.dex.input.absolute.dir})
+ -post-package
+ -post-build
+ -pre-clean
+ -->
+ <import file="custom_rules.xml" optional="true" />
+
+ <!-- Import the actual build file.
+
+ To customize existing targets, there are two options:
+ - Customize only one target:
+ - copy/paste the target into this file, *before* the
+ <import> task.
+ - customize it to your needs.
+ - Customize the whole content of build.xml
+ - copy/paste the content of the rules files (minus the top node)
+ into this file, replacing the <import> task.
+ - customize to your needs.
+
+ ***********************
+ ****** IMPORTANT ******
+ ***********************
+ In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
+ in order to avoid having your file be overridden by tools such as "android update project"
+ -->
+ <!-- version-tag: 1 -->
+ <import file="${sdk.dir}/tools/ant/build.xml" />
+
+</project>
diff --git a/testapps/customPropAnimTest/proguard-project.txt b/testapps/customPropAnimTest/proguard-project.txt
new file mode 100644
index 0000000..f2fe155
--- /dev/null
+++ b/testapps/customPropAnimTest/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/testapps/customPropAnimTest/project.properties b/testapps/customPropAnimTest/project.properties
new file mode 100644
index 0000000..1a88dc6
--- /dev/null
+++ b/testapps/customPropAnimTest/project.properties
@@ -0,0 +1,15 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-15
+
diff --git a/testapps/customPropAnimTest/res/drawable-hdpi/ic_launcher.png b/testapps/customPropAnimTest/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/testapps/customPropAnimTest/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/testapps/customPropAnimTest/res/drawable-ldpi/ic_launcher.png b/testapps/customPropAnimTest/res/drawable-ldpi/ic_launcher.png
new file mode 100644
index 0000000..9923872
--- /dev/null
+++ b/testapps/customPropAnimTest/res/drawable-ldpi/ic_launcher.png
Binary files differ
diff --git a/testapps/customPropAnimTest/res/drawable-mdpi/ic_launcher.png b/testapps/customPropAnimTest/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/testapps/customPropAnimTest/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/testapps/customPropAnimTest/res/drawable-xhdpi/ic_launcher.png b/testapps/customPropAnimTest/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/testapps/customPropAnimTest/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/testapps/customPropAnimTest/res/layout/main.xml b/testapps/customPropAnimTest/res/layout/main.xml
new file mode 100644
index 0000000..40f9f1a
--- /dev/null
+++ b/testapps/customPropAnimTest/res/layout/main.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical"
+ android:id="@+id/container">
+
+</LinearLayout> \ No newline at end of file
diff --git a/testapps/customPropAnimTest/res/values/strings.xml b/testapps/customPropAnimTest/res/values/strings.xml
new file mode 100644
index 0000000..9c3c31b
--- /dev/null
+++ b/testapps/customPropAnimTest/res/values/strings.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <string name="hello">Hello World, CustomPropertyAnimationActivity!</string>
+ <string name="app_name">CustomPropertyAnimation</string>
+
+</resources> \ No newline at end of file
diff --git a/testapps/customPropAnimTest/src/com/android/custompropertyanimation/CustomPropertyAnimationActivity.java b/testapps/customPropAnimTest/src/com/android/custompropertyanimation/CustomPropertyAnimationActivity.java
new file mode 100644
index 0000000..b1b91b9
--- /dev/null
+++ b/testapps/customPropAnimTest/src/com/android/custompropertyanimation/CustomPropertyAnimationActivity.java
@@ -0,0 +1,22 @@
+package com.android.custompropertyanimation;
+
+import android.animation.ObjectAnimator;
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.LinearLayout;
+
+public class CustomPropertyAnimationActivity extends Activity {
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+ LinearLayout container = (LinearLayout) findViewById(R.id.container);
+
+ MyView view = new MyView(this);
+ container.addView(view);
+
+ ObjectAnimator anim = ObjectAnimator.ofFloat(view, "foo", 1);
+ anim.start();
+ }
+} \ No newline at end of file
diff --git a/testapps/customPropAnimTest/src/com/android/custompropertyanimation/MyView.java b/testapps/customPropAnimTest/src/com/android/custompropertyanimation/MyView.java
new file mode 100644
index 0000000..e557fda
--- /dev/null
+++ b/testapps/customPropAnimTest/src/com/android/custompropertyanimation/MyView.java
@@ -0,0 +1,24 @@
+package com.android.custompropertyanimation;
+
+import android.content.Context;
+import android.view.View;
+
+public class MyView extends View {
+
+ float mFoo = 0;
+
+ public MyView(Context context) {
+ super(context);
+ }
+
+ public void setFoo(float foo) {
+ System.out.println("foo = " + foo);
+ mFoo = foo;
+ }
+
+ public float getFoo() {
+ System.out.println("getFoo() returning " + mFoo);
+ return mFoo;
+ }
+
+}
diff --git a/testapps/customViewTest/libWithCustomView/build.xml b/testapps/customViewTest/libWithCustomView/build.xml
index 772f422..7d9e032 100644
--- a/testapps/customViewTest/libWithCustomView/build.xml
+++ b/testapps/customViewTest/libWithCustomView/build.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<project name="basicLibWithSupport" default="help">
+<project name="libWithCustomView" default="help">
<!-- The local.properties file is created and updated by the 'android' tool.
It contains the path to the SDK. It should *NOT* be checked into
@@ -39,27 +39,32 @@
application and should be checked into Version Control Systems. -->
<loadproperties srcFile="project.properties" />
+ <!-- if sdk.dir was not set from one of the property file, then
+ get it from the ANDROID_HOME env var. -->
+ <property environment="env" />
+ <condition property="sdk.dir" value="${env.ANDROID_HOME}">
+ <isset property="env.ANDROID_HOME" />
+ </condition>
+
<!-- quick check on sdk.dir -->
<fail
- message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through an env var"
+ message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
unless="sdk.dir"
/>
-
-<!-- extension targets. Uncomment the ones where you want to do custom work
- in between standard targets -->
-<!--
- <target name="-pre-build">
- </target>
- <target name="-pre-compile">
- </target>
-
- /* This is typically used for code obfuscation.
- Compiled code location: ${out.classes.absolute.dir}
- If this is not done in place, override ${out.dex.input.absolute.dir} */
- <target name="-post-compile">
- </target>
--->
+ <!--
+ Import per project custom build rules if present at the root of the project.
+ This is the place to put custom intermediary targets such as:
+ -pre-build
+ -pre-compile
+ -post-compile (This is typically used for code obfuscation.
+ Compiled code location: ${out.classes.absolute.dir}
+ If this is not done in place, override ${out.dex.input.absolute.dir})
+ -post-package
+ -post-build
+ -pre-clean
+ -->
+ <import file="custom_rules.xml" optional="true" />
<!-- Import the actual build file.
diff --git a/testapps/customViewTest/mainProject/build.xml b/testapps/customViewTest/mainProject/build.xml
index f3a3d91..2f98bb5 100644
--- a/testapps/customViewTest/mainProject/build.xml
+++ b/testapps/customViewTest/mainProject/build.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<project name="basicProjectWithSupport" default="help">
+<project name="customViewTest-mainProject" default="help">
<!-- The local.properties file is created and updated by the 'android' tool.
It contains the path to the SDK. It should *NOT* be checked into
@@ -39,27 +39,32 @@
application and should be checked into Version Control Systems. -->
<loadproperties srcFile="project.properties" />
+ <!-- if sdk.dir was not set from one of the property file, then
+ get it from the ANDROID_HOME env var. -->
+ <property environment="env" />
+ <condition property="sdk.dir" value="${env.ANDROID_HOME}">
+ <isset property="env.ANDROID_HOME" />
+ </condition>
+
<!-- quick check on sdk.dir -->
<fail
- message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through an env var"
+ message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
unless="sdk.dir"
/>
-
-<!-- extension targets. Uncomment the ones where you want to do custom work
- in between standard targets -->
-<!--
- <target name="-pre-build">
- </target>
- <target name="-pre-compile">
- </target>
-
- /* This is typically used for code obfuscation.
- Compiled code location: ${out.classes.absolute.dir}
- If this is not done in place, override ${out.dex.input.absolute.dir} */
- <target name="-post-compile">
- </target>
--->
+ <!--
+ Import per project custom build rules if present at the root of the project.
+ This is the place to put custom intermediary targets such as:
+ -pre-build
+ -pre-compile
+ -post-compile (This is typically used for code obfuscation.
+ Compiled code location: ${out.classes.absolute.dir}
+ If this is not done in place, override ${out.dex.input.absolute.dir})
+ -post-package
+ -post-build
+ -pre-clean
+ -->
+ <import file="custom_rules.xml" optional="true" />
<!-- Import the actual build file.
diff --git a/testapps/customViewTest/mainProject/proguard.cfg b/testapps/customViewTest/mainProject/proguard.cfg
deleted file mode 100644
index b1cdf17..0000000
--- a/testapps/customViewTest/mainProject/proguard.cfg
+++ /dev/null
@@ -1,40 +0,0 @@
--optimizationpasses 5
--dontusemixedcaseclassnames
--dontskipnonpubliclibraryclasses
--dontpreverify
--verbose
--optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
-
--keep public class * extends android.app.Activity
--keep public class * extends android.app.Application
--keep public class * extends android.app.Service
--keep public class * extends android.content.BroadcastReceiver
--keep public class * extends android.content.ContentProvider
--keep public class * extends android.app.backup.BackupAgentHelper
--keep public class * extends android.preference.Preference
--keep public class com.android.vending.licensing.ILicensingService
-
--keepclasseswithmembernames class * {
- native <methods>;
-}
-
--keepclasseswithmembers class * {
- public <init>(android.content.Context, android.util.AttributeSet);
-}
-
--keepclasseswithmembers class * {
- public <init>(android.content.Context, android.util.AttributeSet, int);
-}
-
--keepclassmembers class * extends android.app.Activity {
- public void *(android.view.View);
-}
-
--keepclassmembers enum * {
- public static **[] values();
- public static ** valueOf(java.lang.String);
-}
-
--keep class * implements android.os.Parcelable {
- public static final android.os.Parcelable$Creator *;
-}
diff --git a/testapps/customViewTest/mainProject/project.properties b/testapps/customViewTest/mainProject/project.properties
index b80c0cf..21d68c5 100644
--- a/testapps/customViewTest/mainProject/project.properties
+++ b/testapps/customViewTest/mainProject/project.properties
@@ -10,3 +10,6 @@
# Project target.
target=android-15
android.library.reference.1=../libWithCustomView
+
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
diff --git a/testapps/customViewTest/mainProject/res/values/strings.xml b/testapps/customViewTest/mainProject/res/values/strings.xml
index 3fa203a..e86ca41 100644
--- a/testapps/customViewTest/mainProject/res/values/strings.xml
+++ b/testapps/customViewTest/mainProject/res/values/strings.xml
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <string name="app_name">MyActivity</string>
+ <string name="app_name">customViewTest</string>
</resources>
diff --git a/testapps/libsAndJarTest/app/build.xml b/testapps/libsAndJarTest/app/build.xml
index 1160e8a..c084512 100644
--- a/testapps/libsAndJarTest/app/build.xml
+++ b/testapps/libsAndJarTest/app/build.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<project name="app" default="help">
+<project name="libsAndJarTest-app" default="help">
<!-- The local.properties file is created and updated by the 'android' tool.
It contains the path to the SDK. It should *NOT* be checked into
@@ -28,6 +28,13 @@
-->
<property file="ant.properties" />
+ <!-- if sdk.dir was not set from one of the property file, then
+ get it from the ANDROID_HOME env var. -->
+ <property environment="env" />
+ <condition property="sdk.dir" value="${env.ANDROID_HOME}">
+ <isset property="env.ANDROID_HOME" />
+ </condition>
+
<!-- The project.properties file is created and updated by the 'android'
tool, as well as ADT.
@@ -41,7 +48,7 @@
<!-- quick check on sdk.dir -->
<fail
- message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through an env var"
+ message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
unless="sdk.dir"
/>
diff --git a/testapps/libsAndJarTest/app/project.properties b/testapps/libsAndJarTest/app/project.properties
index 4fe0502..9df4221 100644
--- a/testapps/libsAndJarTest/app/project.properties
+++ b/testapps/libsAndJarTest/app/project.properties
@@ -7,9 +7,9 @@
# "ant.properties", and override values to adapt the script to your
# project structure.
#
-# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
-proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
-
# Project target.
target=android-15
android.library.reference.1=../lib1
+
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
diff --git a/testapps/libsAndJarTest/lib1/build.xml b/testapps/libsAndJarTest/lib1/build.xml
index 2a15ae6..ed25521 100644
--- a/testapps/libsAndJarTest/lib1/build.xml
+++ b/testapps/libsAndJarTest/lib1/build.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<project name="lib1" default="help">
+<project name="libsAndJarTest-lib1" default="help">
<!-- The local.properties file is created and updated by the 'android' tool.
It contains the path to the SDK. It should *NOT* be checked into
@@ -39,9 +39,16 @@
application and should be checked into Version Control Systems. -->
<loadproperties srcFile="project.properties" />
+ <!-- if sdk.dir was not set from one of the property file, then
+ get it from the ANDROID_HOME env var. -->
+ <property environment="env" />
+ <condition property="sdk.dir" value="${env.ANDROID_HOME}">
+ <isset property="env.ANDROID_HOME" />
+ </condition>
+
<!-- quick check on sdk.dir -->
<fail
- message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through an env var"
+ message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
unless="sdk.dir"
/>
diff --git a/testapps/libsAndJarTest/lib2/build.xml b/testapps/libsAndJarTest/lib2/build.xml
index 3d36fda..4f351c8 100644
--- a/testapps/libsAndJarTest/lib2/build.xml
+++ b/testapps/libsAndJarTest/lib2/build.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<project name="lib2" default="help">
+<project name="libsAndJarTest-lib2" default="help">
<!-- The local.properties file is created and updated by the 'android' tool.
It contains the path to the SDK. It should *NOT* be checked into
@@ -39,9 +39,16 @@
application and should be checked into Version Control Systems. -->
<loadproperties srcFile="project.properties" />
+ <!-- if sdk.dir was not set from one of the property file, then
+ get it from the ANDROID_HOME env var. -->
+ <property environment="env" />
+ <condition property="sdk.dir" value="${env.ANDROID_HOME}">
+ <isset property="env.ANDROID_HOME" />
+ </condition>
+
<!-- quick check on sdk.dir -->
<fail
- message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through an env var"
+ message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
unless="sdk.dir"
/>