diff options
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 Binary files differnew file mode 100644 index 0000000..96a442e --- /dev/null +++ b/testapps/customPropAnimTest/res/drawable-hdpi/ic_launcher.png diff --git a/testapps/customPropAnimTest/res/drawable-ldpi/ic_launcher.png b/testapps/customPropAnimTest/res/drawable-ldpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..9923872 --- /dev/null +++ b/testapps/customPropAnimTest/res/drawable-ldpi/ic_launcher.png diff --git a/testapps/customPropAnimTest/res/drawable-mdpi/ic_launcher.png b/testapps/customPropAnimTest/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..359047d --- /dev/null +++ b/testapps/customPropAnimTest/res/drawable-mdpi/ic_launcher.png diff --git a/testapps/customPropAnimTest/res/drawable-xhdpi/ic_launcher.png b/testapps/customPropAnimTest/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..71c6d76 --- /dev/null +++ b/testapps/customPropAnimTest/res/drawable-xhdpi/ic_launcher.png 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" /> |