am 9948d0c3: am a5670cf4: am cfc743a1: Adding LoaderCursor sample based on the LoaderCursor API demo.
* commit '9948d0c3fc0bad53a00f66d8220d363979f6c1e5':
Adding LoaderCursor sample based on the LoaderCursor API demo.
diff --git a/.gitignore b/.gitignore
index 2dd1573..552351b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,10 +19,8 @@
# Build stuff (auto-generated by android update project ...)
-build.xml
ant.properties
local.properties
-project.properties
# Eclipse project files
.classpath
@@ -35,5 +33,7 @@
*.ipr
*.iws
-#gitignore file
-.gitignore
+##Gradle-based build
+.gradle
+build/
+
diff --git a/common/build/build.gradle b/common/build/build.gradle
new file mode 100644
index 0000000..45ff0a1
--- /dev/null
+++ b/common/build/build.gradle
@@ -0,0 +1,29 @@
+/*
+* Copyright 2013 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.
+*/
+
+// The SampleGenPlugin source is in the buildSrc directory.
+import com.example.android.samples.build.SampleGenPlugin
+apply plugin: SampleGenPlugin
+
+// Add a preflight task that depends on the "refresh" task that gets
+// added by the SampleGenPlugin.
+task preflight {
+ project.afterEvaluate({preflight.dependsOn(project.refresh)})
+}
+
+task wrapper(type: Wrapper) {
+ gradleVersion = '1.6'
+}
\ No newline at end of file
diff --git a/common/build/build.iml b/common/build/build.iml
new file mode 100644
index 0000000..2edbb2b
--- /dev/null
+++ b/common/build/build.iml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module external.linked.project.path="$USER_HOME$/src/android/developers-dev/developers/samples/android/common/build" external.system.id="GRADLE" type="JAVA_MODULE" version="4">
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
+ <exclude-output />
+ <content url="file://$MODULE_DIR$">
+ <excludeFolder url="file://$MODULE_DIR$/.gradle" />
+ <excludeFolder url="file://$MODULE_DIR$/build" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ </component>
+</module>
+
diff --git a/common/build/buildSrc/build.gradle b/common/build/buildSrc/build.gradle
new file mode 100644
index 0000000..7ac6c8f
--- /dev/null
+++ b/common/build/buildSrc/build.gradle
@@ -0,0 +1,11 @@
+apply plugin: 'groovy'
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ compile 'org.freemarker:freemarker:2.3.20'
+ compile gradleApi()
+ compile localGroovy()
+}
diff --git a/common/build/buildSrc/src/main/groovy/com/example/android/samples/build/ApplyTemplates.groovy b/common/build/buildSrc/src/main/groovy/com/example/android/samples/build/ApplyTemplates.groovy
new file mode 100644
index 0000000..8864ce2
--- /dev/null
+++ b/common/build/buildSrc/src/main/groovy/com/example/android/samples/build/ApplyTemplates.groovy
@@ -0,0 +1,162 @@
+package com.example.android.samples.build
+
+import freemarker.cache.FileTemplateLoader
+import freemarker.cache.MultiTemplateLoader
+import freemarker.cache.TemplateLoader
+import freemarker.template.Configuration
+import freemarker.template.DefaultObjectWrapper
+import freemarker.template.Template
+import org.gradle.api.GradleException
+import org.gradle.api.file.FileVisitDetails
+import org.gradle.api.tasks.InputDirectory
+import org.gradle.api.tasks.OutputDirectory
+import org.gradle.api.tasks.SourceTask
+import org.gradle.api.tasks.TaskAction
+
+
+class ApplyTemplates extends SourceTask {
+ /**
+ * Freemarker context object
+ */
+ def Configuration cfg = new freemarker.template.Configuration()
+
+ /**
+ * The root directory for output files. All output file paths
+ * are assumed to be relative to this root.
+ */
+ @OutputDirectory
+ public outputDir = project.projectDir
+
+ /**
+ * Include directory. The templates in this directory will not be
+ * processed directly, but will be accessible to other templates
+ * via the <#include> directive.
+ */
+ def include = project.file("$project.projectDir/templates/include")
+
+ /**
+ * List of file extensions that indicate a file to be processed, rather
+ * than simply copied.
+ */
+ def extensionsToProcess = ['ftl']
+
+ /**
+ * List of file extensions that should be completely ignored by this
+ * task. File extensions that appear in neither this list nor the list
+ * specified by {@link #extensionsToProcess} are copied into the destination
+ * without processing.
+ */
+ def extensionsToIgnore = ['ftli']
+
+ /**
+ * A String -> String closure that transforms a (relative) input path into a
+ * (relative) output path. This closure is responsible for any alterations to
+ * the output path, including pathname substitution and extension removal.
+ */
+ Closure<String> filenameTransform
+
+ /**
+ * The hash which will be passed to the freemarker template engine. This hash
+ * is used by the freemarker script as input data.
+ * The hash should contain a key named "meta". The template processor will add
+ * processing data to this key.
+ */
+ def parameters
+
+ /**
+ * The main action for this task. Visits each file in the source directories and
+ * either processes, copies, or ignores it. The action taken for each file depends
+ * on the contents of {@link #extensionsToProcess} and {@link #extensionsToIgnore}.
+ */
+ @TaskAction
+ def applyTemplate() {
+ // Create a list of Freemarker template loaders based on the
+ // source tree(s) of this task. The loader list establishes a virtual
+ // file system for freemarker templates; the template language can
+ // load files, and each load request will have its path resolved
+ // against this set of loaders.
+ println "Gathering template load locations:"
+ def List loaders = []
+ source.asFileTrees.each {
+ src ->
+ println " ${src.dir}"
+ loaders.add(0, new FileTemplateLoader(project.file(src.dir)))
+ }
+
+ // Add the include path(s) to the list of loaders.
+ println "Gathering template include locations:"
+ include = project.fileTree(include)
+ include.asFileTrees.each {
+ inc ->
+ println " ${inc.dir}"
+ loaders.add(0, new FileTemplateLoader(project.file(inc.dir)))
+ }
+ // Add the loaders to the freemarker config
+ cfg.setTemplateLoader(new MultiTemplateLoader(loaders.toArray(new TemplateLoader[1])))
+
+ // Set the wrapper that will be used to convert the template parameters hash into
+ // the internal freemarker data model. The default wrapper is capable of handling a
+ // mix of POJOs/POGOs and XML nodes, so we'll use that.
+ cfg.setObjectWrapper(new DefaultObjectWrapper())
+
+ // This is very much like setting the target SDK level in Android.
+ cfg.setIncompatibleEnhancements("2.3.20")
+
+ // Add an implicit <#include 'common.ftl' to the top of every file.
+ // TODO: should probably be a parameter instead of hardcoded like this.
+ cfg.addAutoInclude('common.ftl')
+
+ // Visit every file in the source tree(s)
+ def processTree = source.getAsFileTree()
+ processTree.visit {
+ FileVisitDetails input ->
+ def inputFile = input.getRelativePath().toString()
+ def outputFile = input.getRelativePath().getFile(project.file(outputDir))
+ // Get the input and output files, and make sure the output path exists
+ def renamedOutput = filenameTransform(outputFile.toString())
+ outputFile = project.file(renamedOutput)
+
+ if (input.directory){
+ // create the output directory. This probably will have already been
+ // created as part of processing the files *in* the directory, but
+ // do it here anyway to support empty directories.
+ outputFile.mkdirs()
+ } else {
+ // We may or may not see the directory before we see the files
+ // in that directory, so create it here
+ outputFile.parentFile.mkdirs()
+
+ // Check the input file extension against the process/ignore list
+ def extension = "NONE"
+ def extensionPattern = ~/.*\.(\w*)$/
+ def extensionMatch = extensionPattern.matcher(inputFile)
+ if (extensionMatch.matches()) {
+ extension = extensionMatch[0][1]
+ }
+ // If the extension is in the process list, put the input through freemarker
+ if (extensionsToProcess.contains(extension)){
+ print '[freemarker] PROCESS: '
+ println "$inputFile -> $outputFile"
+
+ try {
+ def Template tpl = this.cfg.getTemplate(inputFile)
+ def FileWriter out = new FileWriter(outputFile)
+
+ // Add the output file path to parameters.meta so that the freemarker
+ // script can access it.
+ parameters.meta.put("outputFile", "${outputFile}")
+ tpl.process(parameters, out)
+ } catch (e) {
+ println e.message
+ throw new GradleException("Error processing ${inputFile}: ${e.message}")
+ }
+ } else if (!extensionsToIgnore.contains(extension)) {
+ // if it's not processed and not ignored, then it must be copied.
+ print '[freemarker] COPY: '
+ println "$inputFile -> $outputFile"
+ input.copyTo(outputFile);
+ }
+ }
+ }
+ }
+}
diff --git a/common/build/buildSrc/src/main/groovy/com/example/android/samples/build/SampleGenPlugin.groovy b/common/build/buildSrc/src/main/groovy/com/example/android/samples/build/SampleGenPlugin.groovy
new file mode 100644
index 0000000..64aea54
--- /dev/null
+++ b/common/build/buildSrc/src/main/groovy/com/example/android/samples/build/SampleGenPlugin.groovy
@@ -0,0 +1,69 @@
+package com.example.android.samples.build
+
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.api.tasks.GradleBuild
+/**
+ * Created by ilewis on 7/3/13.
+ */
+class SampleGenPlugin implements Plugin {
+
+ /**
+ * Creates a new sample generator task based on the supplied sources.
+ *
+ * @param name Name of the new task
+ * @param sources Source tree that this task should process
+ */
+ void createTask(Project project, String name, SampleGenProperties props, def sources, def destination) {
+ project.task ([type:ApplyTemplates], name, {
+ sources.each { tree ->
+ source += tree
+ }
+ outputDir = destination
+ include = props.templatesInclude()
+ filenameTransform = {s -> props.getOutputForInput(s)}
+ parameters = props.templateParams()
+ })
+ }
+
+ @Override
+ void apply(project) {
+ project.extensions.create("samplegen", SampleGenProperties)
+ project.samplegen.project = project
+ project.afterEvaluate({
+ SampleGenProperties samplegen = project.samplegen
+ project.task('create') {
+ if (project.gradle.startParameter.taskNames.contains('create')) {
+ samplegen.getCreationProperties()
+ }
+
+ }
+
+ project.task('refresh') {
+ samplegen.getRefreshProperties()
+ }
+
+ createTask(project, 'processTemplates', samplegen, samplegen.templates(), samplegen.targetProjectDir)
+ createTask(project, 'processCommon', samplegen, samplegen.common(), samplegen.targetCommonSourceDir())
+
+
+ project.task([type: GradleBuild], 'bootstrap', {
+ buildFile = "${samplegen.targetProjectDir}/build.gradle"
+ dir = samplegen.targetProjectDir
+ tasks = ["refresh"]
+ })
+ project.bootstrap.dependsOn(project.processTemplates)
+ project.bootstrap.dependsOn(project.processCommon)
+ project.create.dependsOn(project.bootstrap)
+
+ project.refresh.dependsOn(project.processTemplates)
+ project.refresh.dependsOn(project.processCommon)
+
+ // People get nervous when they see a task with no actions, so...
+ project.create << {println "Project creation finished."}
+ project.refresh << {println "Project refresh finished."}
+ })
+ }
+
+
+}
\ No newline at end of file
diff --git a/common/build/buildSrc/src/main/groovy/com/example/android/samples/build/SampleGenProperties.groovy b/common/build/buildSrc/src/main/groovy/com/example/android/samples/build/SampleGenProperties.groovy
new file mode 100644
index 0000000..86660cb
--- /dev/null
+++ b/common/build/buildSrc/src/main/groovy/com/example/android/samples/build/SampleGenProperties.groovy
@@ -0,0 +1,256 @@
+package com.example.android.samples.build
+
+import freemarker.ext.dom.NodeModel
+import groovy.transform.Canonical
+import org.gradle.api.GradleException
+import org.gradle.api.Project
+import org.gradle.api.file.FileTree
+
+/**
+ * Gradle extension that holds properties for sample generation.
+ *
+ * The sample generator needs a number of properties whose values can be
+ * inferred by convention from a smaller number of initial properties.
+ * This class defines fields for the initial properties, and getter
+ * methods for the inferred properties. It also defines a small number
+ * of convenience methods for setting up template-generation tasks.
+ */
+@Canonical
+class SampleGenProperties {
+ /**
+ * The Gradle project that this extension is being applied to.
+ */
+ Project project
+
+ /**
+ * Directory where the top-level sample project lives
+ */
+ def targetProjectDir
+
+ /**
+ * Relative path to samples/common directory
+ */
+ def pathToSamplesCommon
+
+ /**
+ * Java package name for the root package of this sample.
+ */
+ String targetSamplePackage
+
+
+ String targetCommonSourceDir() {
+ return "${targetProjectDir}/${targetSampleModule()}/src/common/java/com/example/android/common"
+ }
+
+ /**
+ * The name of this sample (and also of the corresponding .iml file)
+ */
+ String targetSampleName() {
+ return project.file(targetProjectDir).getName()
+ }
+
+ /**
+ * The name of the main module in the sample project
+ */
+ String targetSampleModule() {
+ return "${targetSampleName()}Sample"
+ }
+
+ /**
+ * The path to the template parameters file
+ */
+ String templateXml() {
+ return "${targetProjectDir}/template-params.xml"
+ }
+
+ /**
+ * Returns the sample's fully qualified Java package as an OS dependent
+ * path fragment
+ */
+ String targetSamplePackageAsPath() {
+ return targetSamplePackage.replaceAll(/\./, File.separator)
+ }
+
+ /**
+ * Returns the path to the common/build/templates directory
+ */
+ String templatesRoot() {
+ return "${pathToSamplesCommon}/build/templates"
+ }
+
+
+ /**
+ * Returns the path to common/src/java
+ */
+ String commonSourceRoot() {
+ return "${pathToSamplesCommon}/src/java/com/example/android/common"
+ }
+
+ /**
+ * Returns the path to the template include directory
+ */
+ String templatesInclude() {
+ return "${templatesRoot()}/include"
+ }
+
+ /**
+ * Returns the output file that will be generated for a particular
+ * input, by replacing generic pathnames with project-specific pathnames
+ * and dropping the .ftl extension from freemarker files.
+ *
+ * @param relativeInputPath Input file as a relative path from the template directory
+ * @return Relative output file path
+ */
+ String getOutputForInput(String relativeInputPath) {
+ String outputPath = relativeInputPath
+ outputPath = outputPath.replaceAll('_PROJECT_', targetSampleName())
+ outputPath = outputPath.replaceAll('_MODULE_', targetSampleModule())
+ outputPath = outputPath.replaceAll('_PACKAGE_', targetSamplePackageAsPath())
+
+ // This is kind of a hack; IntelliJ picks up any and all subdirectories named .idea, so
+ // named them ._IDE_ instead. TODO: remove when generating .idea projects is no longer necessary.
+ outputPath = outputPath.replaceAll('_IDE_', "idea")
+ outputPath = outputPath.replaceAll(/\.ftl$/, '')
+
+ // Any file beginning with a dot won't get picked up, so rename them as necessary here.
+ outputPath = outputPath.replaceAll('gitignore', '.gitignore')
+ return outputPath
+ }
+
+ /**
+ * Returns the tree(s) where the templates to be processed live. The template
+ * input paths that are passed to
+ * {@link SampleGenProperties#getOutputForInput(java.lang.String) getOutputForInput}
+ * are relative to the dir element in each tree.
+ */
+ FileTree[] templates() {
+ def result = []
+ def xmlFile = project.file(templateXml())
+ if (xmlFile.exists()) {
+ def xml = new XmlSlurper().parse(xmlFile)
+ xml.template.each { template ->
+ result.add(project.fileTree(dir: "${templatesRoot()}/${template.@src}"))
+ }
+ } else {
+ result.add(project.fileTree(dir: "${templatesRoot()}/create"))
+ }
+ return result;
+ }
+
+ /**
+ * Path(s) of the common directories to copy over to the sample project.
+ */
+ FileTree[] common() {
+ def result = []
+ def xmlFile = project.file(templateXml())
+ if (xmlFile.exists()) {
+ def xml = new XmlSlurper().parse(xmlFile)
+ xml.common.each { common ->
+ println "Adding common/${common.@src} from ${commonSourceRoot()}"
+ result.add(project.fileTree (
+ dir: "${commonSourceRoot()}",
+ include: "${common.@src}/**/*"
+ ))
+ }
+ }
+ return result
+ }
+
+ /**
+ * Returns the hash to supply to the freemarker template processor.
+ * This is loaded from the file specified by {@link SampleGenProperties#templateXml()}
+ * if such a file exists, or synthesized with some default parameters if it does not.
+ * In addition, some data about the current project is added to the "meta" key of the
+ * hash.
+ *
+ * @return The hash to supply to freemarker
+ */
+ Map templateParams() {
+ Map result = new HashMap();
+
+ def xmlFile = project.file(templateXml())
+ if (xmlFile.exists()) {
+ // Parse the xml into Freemarker's DOM structure
+ def params = freemarker.ext.dom.NodeModel.parse(xmlFile)
+
+ // Move to the <sample> node and stuff that in our map
+ def sampleNode = (NodeModel)params.exec(['/sample'])
+ result.put("sample", sampleNode)
+ } else {
+ // Fake data for use on creation
+ result.put("sample", [
+ name:targetSampleName(),
+ package:targetSamplePackage,
+ minSdk:4
+ ])
+ }
+
+ // Extra data that some templates find useful
+ result.put("meta", [
+ root: targetProjectDir,
+ module: targetSampleModule(),
+ common: pathToSamplesCommon,
+ ])
+ return result
+ }
+
+
+
+ /**
+ * Generate default values for properties that can be inferred from an existing
+ * generated project, unless those properties have already been
+ * explicitly specified.
+ */
+ void getRefreshProperties() {
+ if (!this.targetProjectDir) {
+ this.targetProjectDir = project.projectDir
+ }
+ def xmlFile = project.file(templateXml())
+ if (xmlFile.exists()) {
+ println "Template XML: $xmlFile"
+ def xml = new XmlSlurper().parse(xmlFile)
+ this.targetSamplePackage = xml.package.toString()
+ println "Target Package: $targetSamplePackage"
+ }
+ }
+
+ /**
+ * Generate default values for creation properties, unless those properties
+ * have already been explicitly specified. This method will attempt to get
+ * these properties interactively from the user if necessary.
+ */
+ void getCreationProperties() {
+ def calledFrom = project.hasProperty('calledFrom') ? new File(project.calledFrom)
+ : project.projectDir
+ calledFrom = calledFrom.getCanonicalPath()
+ println('\n\n\nReady to create project...')
+
+ if (!this.pathToSamplesCommonSet) {
+ if (project.hasProperty('pathToSamplesCommon')) {
+ this.pathToSamplesCommon = project.pathToSamplesCommon
+ } else {
+ throw new GradleException ('create task requires project property pathToSamplesCommon')
+ }
+ }
+
+ if (!this.targetProjectDir) {
+ if (project.hasProperty('out')) {
+ this.targetProjectDir = project.out
+ } else {
+ this.targetProjectDir = System.console().readLine("\noutput directory [$calledFrom]:")
+ if (this.targetProjectDir.length() <= 0) {
+ this.targetProjectDir = calledFrom
+ }
+ }
+ }
+
+ if (!this.targetSamplePackage) {
+ def defaultPackage = "com.example.android." + this.targetSampleName().toLowerCase()
+ this.targetSamplePackage = System.console().readLine("\nsample package name[$defaultPackage]:")
+ if (this.targetSamplePackage.length() <= 0) {
+ this.targetSamplePackage = defaultPackage
+ }
+ }
+ }
+
+}
diff --git a/common/build/buildSrc/src/main/main.iml b/common/build/buildSrc/src/main/main.iml
new file mode 100644
index 0000000..4faa95b
--- /dev/null
+++ b/common/build/buildSrc/src/main/main.iml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
+ <exclude-output />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/groovy" isTestSource="false" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ </component>
+</module>
+
diff --git a/common/build/gradle/wrapper/gradle-wrapper.jar b/common/build/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..a7634b0
--- /dev/null
+++ b/common/build/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/common/build/gradle/wrapper/gradle-wrapper.properties b/common/build/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..adbf9fe
--- /dev/null
+++ b/common/build/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Jul 03 16:40:03 PDT 2013
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=http\://services.gradle.org/distributions/gradle-1.6-bin.zip
diff --git a/common/build/gradlew b/common/build/gradlew
new file mode 100755
index 0000000..91a7e26
--- /dev/null
+++ b/common/build/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/common/build/gradlew.bat b/common/build/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/common/build/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/common/build/lib/assetstudio.jar b/common/build/lib/assetstudio.jar
new file mode 100644
index 0000000..0893a22
--- /dev/null
+++ b/common/build/lib/assetstudio.jar
Binary files differ
diff --git a/common/build/lib/buildSrc.jar b/common/build/lib/buildSrc.jar
new file mode 100644
index 0000000..9c56bc1
--- /dev/null
+++ b/common/build/lib/buildSrc.jar
Binary files differ
diff --git a/common/build/sample-create b/common/build/sample-create
new file mode 100755
index 0000000..df642d3
--- /dev/null
+++ b/common/build/sample-create
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+CALLED_FROM=$( pwd -P )
+SCRIPTNAME=$(basename "$0")
+SCRIPTPATH=$( cd "$(dirname "$0")" ; pwd -P )
+GRADLE=$SCRIPTPATH/gradlew
+
+SAMPLE_PATH=""
+
+if [ -n "$1" ]; then
+ SAMPLE_PATH=$CALLED_FROM/$1
+fi
+
+SAMPLES_COMMON=$( cd "$SCRIPTPATH/.." ; pwd -P )
+
+SAMPLES_COMMON=$(perl -e "use File::Spec; print File::Spec->abs2rel(@ARGV)" $SAMPLES_COMMON $SAMPLE_PATH)
+
+$GRADLE -b $SCRIPTPATH/build.gradle --info create -Pout=$SAMPLE_PATH -PcalledFrom=$CALLED_FROM -PpathToSamplesCommon=$SAMPLES_COMMON
diff --git a/common/build/settings.gradle b/common/build/settings.gradle
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/common/build/settings.gradle
@@ -0,0 +1 @@
+
diff --git a/common/build/templates/ActivityCards/ActivityCardsCommon.ftli b/common/build/templates/ActivityCards/ActivityCardsCommon.ftli
new file mode 100644
index 0000000..7ed2d5d
--- /dev/null
+++ b/common/build/templates/ActivityCards/ActivityCardsCommon.ftli
@@ -0,0 +1 @@
+<#macro make_activity_res activity type>${(activity.class!"activity_class")?lower_case}_${type}</#macro>
\ No newline at end of file
diff --git a/common/build/templates/ActivityCards/_MODULE_/src/template/java/_PACKAGE_/MainActivity.java.ftl b/common/build/templates/ActivityCards/_MODULE_/src/template/java/_PACKAGE_/MainActivity.java.ftl
new file mode 100755
index 0000000..25bc836
--- /dev/null
+++ b/common/build/templates/ActivityCards/_MODULE_/src/template/java/_PACKAGE_/MainActivity.java.ftl
@@ -0,0 +1,123 @@
+<#ftl>
+<#include "/ActivityCardsCommon.ftli">
+<#--
+ Copyright 2013 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 ${sample.package};
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.GridView;
+import android.widget.TextView;
+
+/**
+ * A simple launcher activity offering access to the individual samples in this project.
+ */
+public class MainActivity extends Activity implements AdapterView.OnItemClickListener {
+ private Sample[] mSamples;
+ private GridView mGridView;
+
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ // Prepare list of samples in this dashboard.
+ <#if (sample.activity?size > 0)>
+ mSamples = new Sample[]{
+ <#list sample.activity as activity>
+ new Sample(R.string.<@make_activity_res activity "title"/>, R.string.<@make_activity_res activity "description"/>,
+ ${activity.class}.class),
+ </#list>
+ };
+ <#else>
+ /* TODO: Define at least one <activity> tag in template-params.xml like so:
+ <activity>
+ <class>[Java class that implements the activity]</class>
+ <title>[Title bar text]</title>
+ <description>[One or two sentence description of what this activity does]</description>
+ </activity>
+ Once activity tags are defined, this text will disappear and be replaced by code
+ that adds your activities to the grid of cards.
+ */
+ </#if>
+
+ // Prepare the GridView
+ mGridView = (GridView) findViewById(android.R.id.list);
+ mGridView.setAdapter(new SampleAdapter());
+ mGridView.setOnItemClickListener(this);
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> container, View view, int position, long id) {
+ startActivity(mSamples[position].intent);
+ }
+
+ private class SampleAdapter extends BaseAdapter {
+ @Override
+ public int getCount() {
+ return mSamples.length;
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return mSamples[position];
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return mSamples[position].hashCode();
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup container) {
+ if (convertView == null) {
+ convertView = getLayoutInflater().inflate(R.layout.sample_dashboard_item,
+ container, false);
+ }
+
+ ((TextView) convertView.findViewById(android.R.id.text1)).setText(
+ mSamples[position].titleResId);
+ ((TextView) convertView.findViewById(android.R.id.text2)).setText(
+ mSamples[position].descriptionResId);
+ return convertView;
+ }
+ }
+
+ private class Sample {
+ int titleResId;
+ int descriptionResId;
+ Intent intent;
+
+ private Sample(int titleResId, int descriptionResId, Intent intent) {
+ this.intent = intent;
+ this.titleResId = titleResId;
+ this.descriptionResId = descriptionResId;
+ }
+
+ private Sample(int titleResId, int descriptionResId,
+ Class<? extends Activity> activityClass) {
+ this(titleResId, descriptionResId,
+ new Intent(MainActivity.this, activityClass));
+ }
+ }
+}
diff --git a/common/build/templates/ActivityCards/_MODULE_/src/template/res/drawable-hdpi/ic_launcher.png b/common/build/templates/ActivityCards/_MODULE_/src/template/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..b1efaf4
--- /dev/null
+++ b/common/build/templates/ActivityCards/_MODULE_/src/template/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/common/build/templates/ActivityCards/_MODULE_/src/template/res/drawable-mdpi/ic_launcher.png b/common/build/templates/ActivityCards/_MODULE_/src/template/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..f5f9244
--- /dev/null
+++ b/common/build/templates/ActivityCards/_MODULE_/src/template/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/common/build/templates/ActivityCards/_MODULE_/src/template/res/drawable-xhdpi/ic_launcher.png b/common/build/templates/ActivityCards/_MODULE_/src/template/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..5d07b3f
--- /dev/null
+++ b/common/build/templates/ActivityCards/_MODULE_/src/template/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/common/build/templates/ActivityCards/_MODULE_/src/template/res/drawable-xhdpi/sample_dashboard_item_background.9.png b/common/build/templates/ActivityCards/_MODULE_/src/template/res/drawable-xhdpi/sample_dashboard_item_background.9.png
new file mode 100644
index 0000000..1358628
--- /dev/null
+++ b/common/build/templates/ActivityCards/_MODULE_/src/template/res/drawable-xhdpi/sample_dashboard_item_background.9.png
Binary files differ
diff --git a/common/build/templates/ActivityCards/_MODULE_/src/template/res/drawable-xxhdpi/ic_launcher.png b/common/build/templates/ActivityCards/_MODULE_/src/template/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..6ef21e1
--- /dev/null
+++ b/common/build/templates/ActivityCards/_MODULE_/src/template/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/common/build/templates/ActivityCards/_MODULE_/src/template/res/layout/activity_main.xml b/common/build/templates/ActivityCards/_MODULE_/src/template/res/layout/activity_main.xml
new file mode 100755
index 0000000..88cdb80
--- /dev/null
+++ b/common/build/templates/ActivityCards/_MODULE_/src/template/res/layout/activity_main.xml
@@ -0,0 +1,41 @@
+<!--
+ Copyright 2013 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.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <TextView style="@style/Widget.SampleMessage"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="@dimen/horizontal_page_margin"
+ android:layout_marginRight="@dimen/horizontal_page_margin"
+ android:layout_marginTop="@dimen/vertical_page_margin"
+ android:layout_marginBottom="@dimen/vertical_page_margin"
+ android:text="@string/intro_message" />
+
+ <GridView android:id="@android:id/list"
+ style="@style/Widget.SampleDashboard.Grid"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:paddingLeft="@dimen/horizontal_page_margin"
+ android:paddingRight="@dimen/horizontal_page_margin"
+ android:paddingBottom="@dimen/vertical_page_margin"
+ android:scrollbarStyle="outsideOverlay" />
+
+</LinearLayout>
diff --git a/common/build/templates/ActivityCards/_MODULE_/src/template/res/layout/sample_dashboard_item.xml b/common/build/templates/ActivityCards/_MODULE_/src/template/res/layout/sample_dashboard_item.xml
new file mode 100644
index 0000000..38987ee
--- /dev/null
+++ b/common/build/templates/ActivityCards/_MODULE_/src/template/res/layout/sample_dashboard_item.xml
@@ -0,0 +1,32 @@
+<!--
+ Copyright 2013 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.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@style/Widget.SampleDashboard.Item"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <TextView android:id="@android:id/text1"
+ style="@style/Widget.SampleDashboard.Item.Title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <TextView android:id="@android:id/text2"
+ style="@style/Widget.SampleDashboard.Item.Description"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+</LinearLayout>
diff --git a/common/build/templates/ActivityCards/_MODULE_/src/template/res/values-sw600dp/dimens.xml b/common/build/templates/ActivityCards/_MODULE_/src/template/res/values-sw600dp/dimens.xml
new file mode 100644
index 0000000..22074a2
--- /dev/null
+++ b/common/build/templates/ActivityCards/_MODULE_/src/template/res/values-sw600dp/dimens.xml
@@ -0,0 +1,24 @@
+<!--
+ Copyright 2013 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.
+ -->
+
+<resources>
+
+ <!-- Semantic definitions -->
+
+ <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen>
+ <dimen name="vertical_page_margin">@dimen/margin_medium</dimen>
+
+</resources>
diff --git a/common/build/templates/ActivityCards/_MODULE_/src/template/res/values/activitycards-strings.xml.ftl b/common/build/templates/ActivityCards/_MODULE_/src/template/res/values/activitycards-strings.xml.ftl
new file mode 100644
index 0000000..e772a05
--- /dev/null
+++ b/common/build/templates/ActivityCards/_MODULE_/src/template/res/values/activitycards-strings.xml.ftl
@@ -0,0 +1,8 @@
+<#include "/ActivityCardsCommon.ftli">
+
+<resources>
+<#list sample.activity as activity>
+ <string name="<@make_activity_res activity "title"/>">${activity.title!"activity.title"}</string>
+ <string name="<@make_activity_res activity "description"/>">${activity.description!"activity.description"}</string>
+</#list>
+</resources>
\ No newline at end of file
diff --git a/common/build/templates/ActivityCards/_MODULE_/src/template/res/values/styles.xml b/common/build/templates/ActivityCards/_MODULE_/src/template/res/values/styles.xml
new file mode 100644
index 0000000..cafe531
--- /dev/null
+++ b/common/build/templates/ActivityCards/_MODULE_/src/template/res/values/styles.xml
@@ -0,0 +1,71 @@
+<!--
+ Copyright 2013 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.
+ -->
+
+<resources>
+
+ <!-- Activity themes -->
+
+ <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+ <style name="Theme.Sample" parent="Theme.Base" />
+
+ <style name="AppTheme" parent="Theme.Sample" />
+ <!-- Widget styling -->
+
+ <style name="Widget" />
+
+ <style name="Widget.SampleContentContainer">
+ <item name="android:paddingTop">@dimen/vertical_page_margin</item>
+ <item name="android:paddingBottom">@dimen/vertical_page_margin</item>
+ <item name="android:paddingLeft">@dimen/horizontal_page_margin</item>
+ <item name="android:paddingRight">@dimen/horizontal_page_margin</item>
+ </style>
+
+ <style name="Widget.SampleMessage">
+ <item name="android:textAppearance">?android:textAppearanceMedium</item>
+ <item name="android:lineSpacingMultiplier">1.1</item>
+ </style>
+
+ <style name="Widget.SampleDashboard.Grid" parent="Widget">
+ <item name="android:stretchMode">columnWidth</item>
+ <item name="android:columnWidth">200dp</item>
+ <item name="android:numColumns">auto_fit</item>
+ <item name="android:drawSelectorOnTop">true</item>
+ <item name="android:horizontalSpacing">@dimen/margin_medium</item>
+ <item name="android:verticalSpacing">@dimen/margin_medium</item>
+ </style>
+
+ <style name="Widget.SampleDashboard.Item" parent="Widget">
+ <item name="android:background">@drawable/sample_dashboard_item_background</item>
+ <item name="android:paddingTop">@dimen/margin_small</item>
+ <item name="android:paddingLeft">@dimen/margin_medium</item>
+ <item name="android:paddingRight">@dimen/margin_medium</item>
+ <item name="android:paddingBottom">@dimen/margin_medium</item>
+ </style>
+
+ <style name="Widget.SampleDashboard.Item.Title" parent="Widget">
+ <item name="android:layout_marginBottom">@dimen/margin_tiny</item>
+ <item name="android:textAppearance">?android:textAppearanceLarge</item>
+ <item name="android:textColor">#09c</item>
+ <item name="android:textStyle">bold</item>
+ <item name="android:textSize">24sp</item>
+ </style>
+
+ <style name="Widget.SampleDashboard.Item.Description" parent="Widget">
+ <item name="android:textAppearance">?android:textAppearanceSmall</item>
+ <item name="android:fontFamily">sans-serif-light</item>
+ </style>
+</resources>
diff --git a/common/build/templates/base/_MODULE_/build.gradle.ftl b/common/build/templates/base/_MODULE_/build.gradle.ftl
new file mode 100644
index 0000000..5ba7743
--- /dev/null
+++ b/common/build/templates/base/_MODULE_/build.gradle.ftl
@@ -0,0 +1,82 @@
+<#--
+ Copyright 2013 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.
+-->
+buildscript {
+ repositories {
+ mavenCentral()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:0.5.+'
+ }
+}
+
+apply plugin: 'android'
+
+
+dependencies {
+ // Add the support lib that is appropriate for SDK ${sample.minSdk}
+<#if sample.minSdk?number < 7>
+ compile "com.android.support:support-v4:13.0.+"
+<#elseif sample.minSdk?number < 13>
+ compile "com.android.support:support-v4:13.0.+"
+ compile "com.android.support:gridlayout-v7:13.0.+"
+<#else>
+ compile "com.android.support:support-v13:13.0.+"
+</#if>
+}
+
+// The sample build uses multiple directories to
+// keep boilerplate and common code separate from
+// the main sample code.
+List<String> dirs = [
+ 'main', // main sample code; look here for the interesting stuff.
+ 'common', // components that are reused by multiple samples
+ 'template'] // boilerplate code that is generated by the sample template process
+
+android {
+ <#-- Note that target SDK is hardcoded in this template. We expect all samples
+ to always use the most current SDK as their target. -->
+ compileSdkVersion 17
+ buildToolsVersion "17.0.0"
+
+ defaultConfig {
+ minSdkVersion ${sample.minSdk}
+ targetSdkVersion 17
+ }
+<#noparse>
+ sourceSets {
+ main {
+ dirs.each { dir ->
+ java.srcDirs "src/${dir}/java"
+ res.srcDirs "src/${dir}/res"
+ }
+ }
+ }
+</#noparse>
+}
+
+task preflight (dependsOn: parent.preflight) {}
+
+// Inject a preflight task into each variant so we have a place to hook tasks
+// that need to run before any of the android build tasks.
+<#noparse>
+android.applicationVariants.each { variant ->
+ tasks.getByPath("prepare${variant.name.capitalize()}Dependencies").dependsOn preflight
+}
+</#noparse>
+
+
+
diff --git a/common/build/templates/base/_MODULE_/src/template/java/_PACKAGE_/MainActivity.java.ftl b/common/build/templates/base/_MODULE_/src/template/java/_PACKAGE_/MainActivity.java.ftl
new file mode 100755
index 0000000..2ae93d2
--- /dev/null
+++ b/common/build/templates/base/_MODULE_/src/template/java/_PACKAGE_/MainActivity.java.ftl
@@ -0,0 +1,34 @@
+<#ftl>
+<#--
+ Copyright 2013 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 ${sample.package};
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+
+/**
+ * A simple launcher activity describing the alternative action bar presentations and allowing the
+ * user to navigate to a demo of each.
+ */
+public class MainActivity extends Activity {
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ }
+}
diff --git a/common/build/templates/base/_MODULE_/src/template/res/drawable-hdpi/ic_launcher.png b/common/build/templates/base/_MODULE_/src/template/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..b1efaf4
--- /dev/null
+++ b/common/build/templates/base/_MODULE_/src/template/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/common/build/templates/base/_MODULE_/src/template/res/drawable-hdpi/tile.9.png b/common/build/templates/base/_MODULE_/src/template/res/drawable-hdpi/tile.9.png
new file mode 100644
index 0000000..1358628
--- /dev/null
+++ b/common/build/templates/base/_MODULE_/src/template/res/drawable-hdpi/tile.9.png
Binary files differ
diff --git a/common/build/templates/base/_MODULE_/src/template/res/drawable-mdpi/ic_launcher.png b/common/build/templates/base/_MODULE_/src/template/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..f5f9244
--- /dev/null
+++ b/common/build/templates/base/_MODULE_/src/template/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/common/build/templates/base/_MODULE_/src/template/res/drawable-xhdpi/ic_launcher.png b/common/build/templates/base/_MODULE_/src/template/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..5d07b3f
--- /dev/null
+++ b/common/build/templates/base/_MODULE_/src/template/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/common/build/templates/base/_MODULE_/src/template/res/drawable-xxhdpi/ic_launcher.png b/common/build/templates/base/_MODULE_/src/template/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..6ef21e1
--- /dev/null
+++ b/common/build/templates/base/_MODULE_/src/template/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/common/build/templates/base/_MODULE_/src/template/res/layout/activity_main.xml b/common/build/templates/base/_MODULE_/src/template/res/layout/activity_main.xml
new file mode 100755
index 0000000..be1aa49
--- /dev/null
+++ b/common/build/templates/base/_MODULE_/src/template/res/layout/activity_main.xml
@@ -0,0 +1,36 @@
+<!--
+ Copyright 2013 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.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <LinearLayout style="@style/Widget.SampleMessageTile"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <TextView style="@style/Widget.SampleMessage"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="@dimen/horizontal_page_margin"
+ android:layout_marginRight="@dimen/horizontal_page_margin"
+ android:layout_marginTop="@dimen/vertical_page_margin"
+ android:layout_marginBottom="@dimen/vertical_page_margin"
+ android:text="@string/intro_message" />
+ </LinearLayout>
+</LinearLayout>
diff --git a/common/build/templates/base/_MODULE_/src/template/res/values-sw600dp/dimens.xml b/common/build/templates/base/_MODULE_/src/template/res/values-sw600dp/dimens.xml
new file mode 100644
index 0000000..22074a2
--- /dev/null
+++ b/common/build/templates/base/_MODULE_/src/template/res/values-sw600dp/dimens.xml
@@ -0,0 +1,24 @@
+<!--
+ Copyright 2013 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.
+ -->
+
+<resources>
+
+ <!-- Semantic definitions -->
+
+ <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen>
+ <dimen name="vertical_page_margin">@dimen/margin_medium</dimen>
+
+</resources>
diff --git a/common/build/templates/base/_MODULE_/src/template/res/values-sw600dp/styles.xml b/common/build/templates/base/_MODULE_/src/template/res/values-sw600dp/styles.xml
new file mode 100644
index 0000000..03d1974
--- /dev/null
+++ b/common/build/templates/base/_MODULE_/src/template/res/values-sw600dp/styles.xml
@@ -0,0 +1,25 @@
+<!--
+ Copyright 2013 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.
+ -->
+
+<resources>
+
+ <style name="Widget.SampleMessage">
+ <item name="android:textAppearance">?android:textAppearanceLarge</item>
+ <item name="android:lineSpacingMultiplier">1.2</item>
+ <item name="android:shadowDy">-6.5</item>
+ </style>
+
+</resources>
diff --git a/common/build/templates/base/_MODULE_/src/template/res/values/base-strings.xml.ftl b/common/build/templates/base/_MODULE_/src/template/res/values/base-strings.xml.ftl
new file mode 100755
index 0000000..9a15a37
--- /dev/null
+++ b/common/build/templates/base/_MODULE_/src/template/res/values/base-strings.xml.ftl
@@ -0,0 +1,9 @@
+<resources>
+ <string name="app_name">${sample.name}</string>
+
+ <string name="intro_message">
+ <![CDATA[
+ ${sample.intro}
+ ]]>
+ </string>
+</resources>
diff --git a/common/build/templates/base/_MODULE_/src/template/res/values/dimens.xml b/common/build/templates/base/_MODULE_/src/template/res/values/dimens.xml
new file mode 100644
index 0000000..39e710b
--- /dev/null
+++ b/common/build/templates/base/_MODULE_/src/template/res/values/dimens.xml
@@ -0,0 +1,32 @@
+<!--
+ Copyright 2013 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.
+ -->
+
+<resources>
+
+ <!-- Define standard dimensions to comply with Holo-style grids and rhythm. -->
+
+ <dimen name="margin_tiny">4dp</dimen>
+ <dimen name="margin_small">8dp</dimen>
+ <dimen name="margin_medium">16dp</dimen>
+ <dimen name="margin_large">32dp</dimen>
+ <dimen name="margin_huge">64dp</dimen>
+
+ <!-- Semantic definitions -->
+
+ <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen>
+ <dimen name="vertical_page_margin">@dimen/margin_medium</dimen>
+
+</resources>
diff --git a/common/build/templates/base/_MODULE_/src/template/res/values/styles.xml b/common/build/templates/base/_MODULE_/src/template/res/values/styles.xml
new file mode 100644
index 0000000..404623e
--- /dev/null
+++ b/common/build/templates/base/_MODULE_/src/template/res/values/styles.xml
@@ -0,0 +1,42 @@
+<!--
+ Copyright 2013 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.
+ -->
+
+<resources>
+
+ <!-- Activity themes -->
+
+ <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+ <style name="Theme.Sample" parent="Theme.Base" />
+
+ <style name="AppTheme" parent="Theme.Sample" />
+ <!-- Widget styling -->
+
+ <style name="Widget" />
+
+ <style name="Widget.SampleMessage">
+ <item name="android:textAppearance">?android:textAppearanceMedium</item>
+ <item name="android:lineSpacingMultiplier">1.1</item>
+ </style>
+
+ <style name="Widget.SampleMessageTile">
+ <item name="android:background">@drawable/tile</item>
+ <item name="android:shadowColor">#7F000000</item>
+ <item name="android:shadowDy">-3.5</item>
+ <item name="android:shadowRadius">2</item>
+ </style>
+
+</resources>
diff --git a/common/build/templates/create/._IDE_/.name.ftl b/common/build/templates/create/._IDE_/.name.ftl
new file mode 100644
index 0000000..52f9788
--- /dev/null
+++ b/common/build/templates/create/._IDE_/.name.ftl
@@ -0,0 +1,2 @@
+<#ftl>
+${sample.name}
diff --git a/common/build/templates/create/._IDE_/gradle.xml b/common/build/templates/create/._IDE_/gradle.xml
new file mode 100644
index 0000000..a9986e4
--- /dev/null
+++ b/common/build/templates/create/._IDE_/gradle.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="GradleSettings">
+ <option name="linkedExternalProjectsSettings">
+ <GradleProjectSettings>
+ <option name="externalProjectPath" value="$PROJECT_DIR$/build.gradle" />
+ <option name="useAutoImport" value="true" />
+ </GradleProjectSettings>
+ </option>
+ </component>
+</project>
+
diff --git a/common/build/templates/create/._IDE_/modules.xml.ftl b/common/build/templates/create/._IDE_/modules.xml.ftl
new file mode 100644
index 0000000..4108150
--- /dev/null
+++ b/common/build/templates/create/._IDE_/modules.xml.ftl
@@ -0,0 +1,10 @@
+<#ftl>
+<project version="4">
+ <component name="ProjectModuleManager">
+ <modules>
+ <module fileurl="file://$PROJECT_DIR$/${sample.name}.iml" filepath="$PROJECT_DIR$/${sample.name}.iml" />
+ <module fileurl="file://$PROJECT_DIR$/${meta.module}/${meta.module}.iml" filepath="$PROJECT_DIR$/${meta.module}/${meta.module}.iml" />
+ </modules>
+ </component>
+</project>
+
diff --git a/common/build/templates/create/_MODULE_/_MODULE_.iml.ftl b/common/build/templates/create/_MODULE_/_MODULE_.iml.ftl
new file mode 100644
index 0000000..fd6972c
--- /dev/null
+++ b/common/build/templates/create/_MODULE_/_MODULE_.iml.ftl
@@ -0,0 +1,84 @@
+<#--
+ Copyright 2013 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.
+-->
+<?xml version="1.0" encoding="UTF-8"?>
+<module external.system.id="GRADLE" type="JAVA_MODULE" version="4">
+ <component name="FacetManager">
+ <facet type="android" name="Android">
+ <configuration>
+ <option name="ALLOW_USER_CONFIGURATION" value="false" />
+ <option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
+ <option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
+ <option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
+ </configuration>
+ </facet>
+ <facet type="android-gradle" name="Android-Gradle">
+ <configuration>
+ <option name="GRADLE_PROJECT_PATH" value=":${sample.name}" />
+ </configuration>
+ </facet>
+ </component>
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
+ <exclude-output />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/build/source/r/debug" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/build/source/aidl/debug" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/build/source/rs/debug" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/build/source/buildConfig/debug" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/build/res/rs/debug" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/build/source/r/test" isTestSource="true" />
+ <sourceFolder url="file://$MODULE_DIR$/build/source/aidl/test" isTestSource="true" />
+ <sourceFolder url="file://$MODULE_DIR$/build/source/rs/test" isTestSource="true" />
+ <sourceFolder url="file://$MODULE_DIR$/build/source/buildConfig/test" isTestSource="true" />
+ <sourceFolder url="file://$MODULE_DIR$/build/res/rs/test" isTestSource="true" />
+ <sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/debug/assets" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/debug/res" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/debug/resources" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/assets" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/res" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/resources" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/instrumentTest/aidl" isTestSource="true" />
+ <sourceFolder url="file://$MODULE_DIR$/src/instrumentTest/assets" isTestSource="true" />
+ <sourceFolder url="file://$MODULE_DIR$/src/instrumentTest/java" isTestSource="true" />
+ <sourceFolder url="file://$MODULE_DIR$/src/instrumentTest/jni" isTestSource="true" />
+ <sourceFolder url="file://$MODULE_DIR$/src/instrumentTest/rs" isTestSource="true" />
+ <sourceFolder url="file://$MODULE_DIR$/src/instrumentTest/res" isTestSource="true" />
+ <sourceFolder url="file://$MODULE_DIR$/src/instrumentTest/resources" isTestSource="true" />
+ <excludeFolder url="file://$MODULE_DIR$/build/apk" />
+ <excludeFolder url="file://$MODULE_DIR$/build/assets" />
+ <excludeFolder url="file://$MODULE_DIR$/build/bundles" />
+ <excludeFolder url="file://$MODULE_DIR$/build/classes" />
+ <excludeFolder url="file://$MODULE_DIR$/build/dependency-cache" />
+ <excludeFolder url="file://$MODULE_DIR$/build/exploded-bundles" />
+ <excludeFolder url="file://$MODULE_DIR$/build/incremental" />
+ <excludeFolder url="file://$MODULE_DIR$/build/libs" />
+ <excludeFolder url="file://$MODULE_DIR$/build/manifests" />
+ <excludeFolder url="file://$MODULE_DIR$/build/symbols" />
+ <excludeFolder url="file://$MODULE_DIR$/build/tmp" />
+ </content>
+ <orderEntry type="jdk" jdkName="Android 4.2 Platform" jdkType="Android SDK" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="library" name="android-support-v4" level="project" />
+ </component>
+</module>
+
diff --git a/common/build/templates/create/_MODULE_/build.gradle.ftl b/common/build/templates/create/_MODULE_/build.gradle.ftl
new file mode 100644
index 0000000..c662fec
--- /dev/null
+++ b/common/build/templates/create/_MODULE_/build.gradle.ftl
@@ -0,0 +1,42 @@
+
+<#-- This build script is a bootstrapper for the "real" android build script that
+is contained in templates/base. It includes only what's necessary for Android Studio
+to recognize this as an Android project and start the template engine. -->
+
+buildscript {
+ repositories {
+ mavenCentral()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:0.4.2'
+ }
+}
+
+apply plugin: 'android'
+
+
+android {
+ <#-- Note that target SDK is hardcoded in this template. We expect all samples
+ to always use the most current SDK as their target. -->
+ compileSdkVersion 17
+ buildToolsVersion "17.0.0"
+
+ defaultConfig {
+ minSdkVersion ${sample.minSdk}
+ targetSdkVersion 17
+ }
+}
+
+task preflight (dependsOn: parent.preflight) {}
+
+// Inject a preflight task into each variant so we have a place to hook tasks
+// that need to run before any of the android build tasks.
+<#noparse>
+android.applicationVariants.each { variant ->
+ tasks.getByPath("prepare${variant.name.capitalize()}Dependencies").dependsOn preflight
+}
+</#noparse>
+
+
+
diff --git a/common/build/templates/create/_MODULE_/gitignore b/common/build/templates/create/_MODULE_/gitignore
new file mode 100644
index 0000000..f1e8ad1
--- /dev/null
+++ b/common/build/templates/create/_MODULE_/gitignore
@@ -0,0 +1,2 @@
+src/template/
+src/common/
\ No newline at end of file
diff --git a/common/build/templates/create/_MODULE_/proguard-project.txt b/common/build/templates/create/_MODULE_/proguard-project.txt
new file mode 100644
index 0000000..0d8f171
--- /dev/null
+++ b/common/build/templates/create/_MODULE_/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/common/build/templates/create/_MODULE_/project.properties b/common/build/templates/create/_MODULE_/project.properties
new file mode 100644
index 0000000..0c9830a
--- /dev/null
+++ b/common/build/templates/create/_MODULE_/project.properties
@@ -0,0 +1,14 @@
+# 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=Google Inc.:Google APIs:17
diff --git a/common/build/templates/create/_MODULE_/src/main/AndroidManifest.xml.ftl b/common/build/templates/create/_MODULE_/src/main/AndroidManifest.xml.ftl
new file mode 100644
index 0000000..fd49b3e
--- /dev/null
+++ b/common/build/templates/create/_MODULE_/src/main/AndroidManifest.xml.ftl
@@ -0,0 +1,24 @@
+<#ftl>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="${sample.package}"
+ android:versionCode="1"
+ android:versionName="1.0">
+
+ <uses-sdk android:minSdkVersion="${sample.minSdk}" android:targetSdkVersion="17" />
+
+ <application android:allowBackup="true"
+ android:label="@string/app_name"
+ android:icon="@drawable/ic_launcher"
+ android:theme="@style/AppTheme">
+
+ <activity android:name=".MainActivity"
+ 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>
diff --git a/common/build/templates/create/_PROJECT_.iml.ftl b/common/build/templates/create/_PROJECT_.iml.ftl
new file mode 100644
index 0000000..81076f2
--- /dev/null
+++ b/common/build/templates/create/_PROJECT_.iml.ftl
@@ -0,0 +1,14 @@
+<module external.system.id="GRADLE" type="JAVA_MODULE" version="4">
+<component name="NewModuleRootManager" inherit-compiler-output="true">
+<exclude-output />
+<content url="file://$MODULE_DIR$">
+ <excludeFolder url="file://$MODULE_DIR$/.gradle" />
+ <excludeFolder url="file://$MODULE_DIR$/gradle" />
+ <excludeFolder url="file://$MODULE_DIR$/.idea" />
+ <excludeFolder url="file://$MODULE_DIR$/buildSrc" />
+ <excludeFolder url="file://$MODULE_DIR$/build" />
+</content>
+<orderEntry type="inheritedJdk" />
+<orderEntry type="sourceFolder" forTests="false" />
+</component>
+</module>
diff --git a/common/build/templates/create/build.gradle.ftl b/common/build/templates/create/build.gradle.ftl
new file mode 100644
index 0000000..8a2e4da
--- /dev/null
+++ b/common/build/templates/create/build.gradle.ftl
@@ -0,0 +1,7 @@
+<#ftl>
+// BEGIN_EXCLUDE
+apply from: "../../common/build/build.gradle"
+samplegen {
+pathToSamplesCommon "../../common"
+}
+// END_EXCLUDE
\ No newline at end of file
diff --git a/common/build/templates/create/buildSrc/build.gradle.ftl b/common/build/templates/create/buildSrc/build.gradle.ftl
new file mode 100644
index 0000000..dcdd01a
--- /dev/null
+++ b/common/build/templates/create/buildSrc/build.gradle.ftl
@@ -0,0 +1,16 @@
+<#ftl>
+repositories {
+ mavenCentral()
+}
+dependencies {
+ compile 'org.freemarker:freemarker:2.3.20'
+}
+
+sourceSets {
+ main {
+ groovy {
+ srcDir new File(rootDir, "../${meta.common}/build/buildSrc/src/main/groovy")
+ }
+ }
+}
+
diff --git a/common/build/templates/create/gradle/wrapper/gradle-wrapper.jar b/common/build/templates/create/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..8c0fb64
--- /dev/null
+++ b/common/build/templates/create/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/common/build/templates/create/gradle/wrapper/gradle-wrapper.properties b/common/build/templates/create/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..5c22dec
--- /dev/null
+++ b/common/build/templates/create/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Apr 10 15:27:10 PDT 2013
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=http\://services.gradle.org/distributions/gradle-1.6-bin.zip
diff --git a/common/build/templates/create/gradlew b/common/build/templates/create/gradlew
new file mode 100755
index 0000000..91a7e26
--- /dev/null
+++ b/common/build/templates/create/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/common/build/templates/create/gradlew.bat b/common/build/templates/create/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/common/build/templates/create/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/common/build/templates/create/settings.gradle.ftl b/common/build/templates/create/settings.gradle.ftl
new file mode 100644
index 0000000..17556da
--- /dev/null
+++ b/common/build/templates/create/settings.gradle.ftl
@@ -0,0 +1,2 @@
+<#ftl>
+include '${meta.module}'
diff --git a/common/build/templates/create/template-params.xml.ftl b/common/build/templates/create/template-params.xml.ftl
new file mode 100644
index 0000000..2f1c989
--- /dev/null
+++ b/common/build/templates/create/template-params.xml.ftl
@@ -0,0 +1,21 @@
+<#ftl>
+<sample>
+ <name>${sample.name}</name>
+ <package>${sample.package}</package>
+
+
+ <!--TODO: change minSdk if needed-->
+ <minSdk>${sample.minSdk}</minSdk>
+
+
+ <intro>
+ <![CDATA[
+ Introductory text that explains what the sample is intended to demonstrate. Edit
+ in template-params.xml.
+ ]]>
+ </intro>
+
+ <template src="base"/>
+ <common src="logger"/>
+
+</sample>
diff --git a/common/build/templates/include/c-style-copyright.ftl b/common/build/templates/include/c-style-copyright.ftl
new file mode 100644
index 0000000..af10946
--- /dev/null
+++ b/common/build/templates/include/c-style-copyright.ftl
@@ -0,0 +1,15 @@
+/*
+* Copyright 2013 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.
+*/
diff --git a/common/build/templates/include/common.ftl b/common/build/templates/include/common.ftl
new file mode 100644
index 0000000..f116b0c
--- /dev/null
+++ b/common/build/templates/include/common.ftl
@@ -0,0 +1,6 @@
+<#-- Add the appropriate copyright header -->
+<#if meta.outputFile?ends_with("java")>
+ <#include "c-style-copyright.ftl">
+<#elseif meta.outputFile?ends_with("xml")>
+ <#include "xml-style-copyright.ftl">
+</#if>
diff --git a/common/build/templates/include/ftl-style-copyright.ftl b/common/build/templates/include/ftl-style-copyright.ftl
new file mode 100644
index 0000000..9b8acec
--- /dev/null
+++ b/common/build/templates/include/ftl-style-copyright.ftl
@@ -0,0 +1,15 @@
+<#--
+ Copyright 2013 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.
+-->
diff --git a/common/build/templates/include/ignoredir.fmpp b/common/build/templates/include/ignoredir.fmpp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/common/build/templates/include/ignoredir.fmpp
diff --git a/common/build/templates/include/xml-style-copyright.ftl b/common/build/templates/include/xml-style-copyright.ftl
new file mode 100644
index 0000000..f961eb7
--- /dev/null
+++ b/common/build/templates/include/xml-style-copyright.ftl
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2013 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.
+-->
diff --git a/common/src/com/example/android/common/actionbarcompat/MultiSelectionUtil.java b/common/src/com/example/android/common/actionbarcompat/MultiSelectionUtil.java
new file mode 100644
index 0000000..482f6ed
--- /dev/null
+++ b/common/src/com/example/android/common/actionbarcompat/MultiSelectionUtil.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2013 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.example.android.common.actionbarcompat;
+
+import android.os.Bundle;
+import android.support.v7.app.ActionBarActivity;
+import android.support.v7.view.ActionMode;
+import android.util.Pair;
+import android.util.SparseBooleanArray;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.AbsListView;
+import android.widget.Adapter;
+import android.widget.AdapterView;
+import android.widget.ListView;
+
+import java.util.HashSet;
+
+/**
+ * Utilities for handling multiple selection in list views. Contains functionality similar to {@link
+ * AbsListView#CHOICE_MODE_MULTIPLE_MODAL} which works with {@link ActionBarActivity} and
+ * backward-compatible action bars.
+ */
+public class MultiSelectionUtil {
+
+ /**
+ * Attach a Controller to the given <code>listView</code>, <code>activity</code>
+ * and <code>listener</code>.
+ *
+ * @param listView ListView which displays {@link android.widget.Checkable} items.
+ * @param activity Activity which contains the ListView.
+ * @param listener Listener that will manage the selection mode.
+ * @return the attached Controller instance.
+ */
+ public static Controller attachMultiSelectionController(final ListView listView,
+ final ActionBarActivity activity, final MultiChoiceModeListener listener) {
+ return new Controller(listView, activity, listener);
+ }
+
+ /**
+ * Class which provides functionality similar to {@link AbsListView#CHOICE_MODE_MULTIPLE_MODAL}
+ * for the {@link ListView} provided to it. A
+ * {@link android.widget.AdapterView.OnItemLongClickListener} is set on the ListView so that
+ * when an item is long-clicked an ActionBarCompat Action Mode is started. Once started, a
+ * {@link android.widget.AdapterView.OnItemClickListener} is set so that an item click toggles
+ * that item's checked state.
+ */
+ public static class Controller {
+
+ private final ListView mListView;
+ private final ActionBarActivity mActivity;
+ private final MultiChoiceModeListener mListener;
+ private final Callbacks mCallbacks;
+
+ // Current Action Mode (if there is one)
+ private ActionMode mActionMode;
+
+ // Keeps record of any items that should be checked on the next action mode creation
+ private HashSet<Pair<Integer, Long>> mItemsToCheck;
+
+ // Reference to the replace OnItemClickListener (so it can be restored later)
+ private AdapterView.OnItemClickListener mOldItemClickListener;
+
+ private final Runnable mSetChoiceModeNoneRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mListView.setChoiceMode(AbsListView.CHOICE_MODE_NONE);
+ }
+ };
+
+ private Controller(ListView listView, ActionBarActivity activity,
+ MultiChoiceModeListener listener) {
+ mListView = listView;
+ mActivity = activity;
+ mListener = listener;
+ mCallbacks = new Callbacks();
+
+ // We set ourselves as the OnItemLongClickListener so we know when to start
+ // an Action Mode
+ listView.setOnItemLongClickListener(mCallbacks);
+ }
+
+ /**
+ * Finish the current Action Mode (if there is one).
+ */
+ public void finish() {
+ if (mActionMode != null) {
+ mActionMode.finish();
+ }
+ }
+
+ /**
+ * This method should be called from your {@link ActionBarActivity} or
+ * {@link android.support.v4.app.Fragment Fragment} to allow the controller to restore any
+ * instance state.
+ *
+ * @param savedInstanceState - The state passed to your Activity or Fragment.
+ */
+ public void restoreInstanceState(Bundle savedInstanceState) {
+ if (savedInstanceState != null) {
+ long[] checkedIds = savedInstanceState.getLongArray(getStateKey());
+ if (checkedIds != null && checkedIds.length > 0) {
+ HashSet<Long> idsToCheckOnRestore = new HashSet<Long>();
+ for (long id : checkedIds) {
+ idsToCheckOnRestore.add(id);
+ }
+ tryRestoreInstanceState(idsToCheckOnRestore);
+ }
+ }
+ }
+
+ /**
+ * This method should be called from
+ * {@link ActionBarActivity#onSaveInstanceState(android.os.Bundle)} or
+ * {@link android.support.v4.app.Fragment#onSaveInstanceState(android.os.Bundle)
+ * Fragment.onSaveInstanceState(Bundle)} to allow the controller to save its instance
+ * state.
+ *
+ * @param outState - The state passed to your Activity or Fragment.
+ */
+ public void saveInstanceState(Bundle outState) {
+ if (mActionMode != null && mListView.getAdapter().hasStableIds()) {
+ outState.putLongArray(getStateKey(), mListView.getCheckedItemIds());
+ }
+ }
+
+ // Internal utility methods
+
+ private String getStateKey() {
+ return MultiSelectionUtil.class.getSimpleName() + "_" + mListView.getId();
+ }
+
+ private void tryRestoreInstanceState(HashSet<Long> idsToCheckOnRestore) {
+ if (idsToCheckOnRestore == null || mListView.getAdapter() == null) {
+ return;
+ }
+
+ boolean idsFound = false;
+ Adapter adapter = mListView.getAdapter();
+ for (int pos = adapter.getCount() - 1; pos >= 0; pos--) {
+ if (idsToCheckOnRestore.contains(adapter.getItemId(pos))) {
+ idsFound = true;
+ if (mItemsToCheck == null) {
+ mItemsToCheck = new HashSet<Pair<Integer, Long>>();
+ }
+ mItemsToCheck.add(new Pair<Integer, Long>(pos, adapter.getItemId(pos)));
+ }
+ }
+
+ if (idsFound) {
+ // We found some IDs that were checked. Let's now restore the multi-selection
+ // state.
+ mActionMode = mActivity.startSupportActionMode(mCallbacks);
+ }
+ }
+
+ /**
+ * This class encapsulates all of the callbacks necessary for the controller class.
+ */
+ final class Callbacks implements ActionMode.Callback, AdapterView.OnItemClickListener,
+ AdapterView.OnItemLongClickListener {
+
+ @Override
+ public final boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
+ if (mListener.onCreateActionMode(actionMode, menu)) {
+ mActionMode = actionMode;
+ // Keep a reference to the existing OnItemClickListener so we can restore it
+ mOldItemClickListener = mListView.getOnItemClickListener();
+
+ // Set-up the ListView to emulate CHOICE_MODE_MULTIPLE_MODAL
+ mListView.setOnItemClickListener(this);
+ mListView.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE);
+ mListView.removeCallbacks(mSetChoiceModeNoneRunnable);
+
+ // If there are some items to check, do it now
+ if (mItemsToCheck != null) {
+ for (Pair<Integer, Long> posAndId : mItemsToCheck) {
+ mListView.setItemChecked(posAndId.first, true);
+ // Notify the listener that the item has been checked
+ mListener.onItemCheckedStateChanged(mActionMode, posAndId.first,
+ posAndId.second, true);
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
+ // Proxy listener
+ return mListener.onPrepareActionMode(actionMode, menu);
+ }
+
+ @Override
+ public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
+ // Proxy listener
+ return mListener.onActionItemClicked(actionMode, menuItem);
+ }
+
+ @Override
+ public void onDestroyActionMode(ActionMode actionMode) {
+ mListener.onDestroyActionMode(actionMode);
+
+ // Clear all the checked items
+ SparseBooleanArray checkedPositions = mListView.getCheckedItemPositions();
+ if (checkedPositions != null) {
+ for (int i = 0; i < checkedPositions.size(); i++) {
+ mListView.setItemChecked(checkedPositions.keyAt(i), false);
+ }
+ }
+
+ // Restore the original onItemClickListener
+ mListView.setOnItemClickListener(mOldItemClickListener);
+
+ // Clear the Action Mode
+ mActionMode = null;
+
+ // Reset the ListView's Choice Mode
+ mListView.post(mSetChoiceModeNoneRunnable);
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
+ // Check to see what the new checked state is, and then notify the listener
+ final boolean checked = mListView.isItemChecked(position);
+ mListener.onItemCheckedStateChanged(mActionMode, position, id, checked);
+
+ boolean hasCheckedItem = checked;
+
+ // Check to see if we have any checked items
+ if (!hasCheckedItem) {
+ SparseBooleanArray checkedItemPositions = mListView.getCheckedItemPositions();
+ if (checkedItemPositions != null) {
+ // Iterate through the SparseBooleanArray to see if there is a checked item
+ int i = 0;
+ while (!hasCheckedItem && i < checkedItemPositions.size()) {
+ hasCheckedItem = checkedItemPositions.valueAt(i++);
+ }
+ }
+ }
+
+ // If we don't have any checked items, finish the action mode
+ if (!hasCheckedItem) {
+ mActionMode.finish();
+ }
+ }
+
+ @Override
+ public boolean onItemLongClick(AdapterView<?> adapterView, View view, int position,
+ long id) {
+ // If we already have an action mode started return false
+ // (onItemClick will be called anyway)
+ if (mActionMode != null) {
+ return false;
+ }
+
+ mItemsToCheck = new HashSet<Pair<Integer, Long>>();
+ mItemsToCheck.add(new Pair<Integer, Long>(position, id));
+ mActionMode = mActivity.startSupportActionMode(this);
+ return true;
+ }
+ }
+ }
+
+ /**
+ * @see android.widget.AbsListView.MultiChoiceModeListener
+ */
+ public static interface MultiChoiceModeListener extends ActionMode.Callback {
+
+ /**
+ * @see android.widget.AbsListView.MultiChoiceModeListener#onItemCheckedStateChanged(
+ *android.view.ActionMode, int, long, boolean)
+ */
+ public void onItemCheckedStateChanged(ActionMode mode, int position, long id,
+ boolean checked);
+ }
+}
diff --git a/common/src/com/example/android/common/dummydata/Cheeses.java b/common/src/com/example/android/common/dummydata/Cheeses.java
new file mode 100644
index 0000000..a386e68
--- /dev/null
+++ b/common/src/com/example/android/common/dummydata/Cheeses.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2013 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.example.android.common.dummydata;
+
+import java.util.ArrayList;
+
+/**
+ * Dummy data.
+ */
+public class Cheeses {
+ static final String[] CHEESES = {
+ "Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi",
+ "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", "Airag", "Airedale",
+ "Aisy Cendre", "Allgauer Emmentaler", "Alverca", "Ambert", "American Cheese",
+ "Ami du Chambertin", "Anejo Enchilado", "Anneau du Vic-Bilh", "Anthoriro", "Appenzell",
+ "Aragon", "Ardi Gasna", "Ardrahan", "Armenian String", "Aromes au Gene de Marc",
+ "Asadero", "Asiago", "Aubisque Pyrenees", "Autun", "Avaxtskyr", "Baby Swiss",
+ "Babybel", "Baguette Laonnaise", "Bakers", "Baladi", "Balaton", "Bandal", "Banon",
+ "Barry's Bay Cheddar", "Basing", "Basket Cheese", "Bath Cheese", "Bavarian Bergkase",
+ "Baylough", "Beaufort", "Beauvoorde", "Beenleigh Blue", "Beer Cheese", "Bel Paese",
+ "Bergader", "Bergere Bleue", "Berkswell", "Beyaz Peynir", "Bierkase", "Bishop Kennedy",
+ "Blarney", "Bleu d'Auvergne", "Bleu de Gex", "Bleu de Laqueuille",
+ "Bleu de Septmoncel", "Bleu Des Causses", "Blue", "Blue Castello", "Blue Rathgore",
+ "Blue Vein (Australian)", "Blue Vein Cheeses", "Bocconcini", "Bocconcini (Australian)",
+ "Boeren Leidenkaas", "Bonchester", "Bosworth", "Bougon", "Boule Du Roves",
+ "Boulette d'Avesnes", "Boursault", "Boursin", "Bouyssou", "Bra", "Braudostur",
+ "Breakfast Cheese", "Brebis du Lavort", "Brebis du Lochois", "Brebis du Puyfaucon",
+ "Bresse Bleu", "Brick", "Brie", "Brie de Meaux", "Brie de Melun", "Brillat-Savarin",
+ "Brin", "Brin d' Amour", "Brin d'Amour", "Brinza (Burduf Brinza)",
+ "Briquette de Brebis", "Briquette du Forez", "Broccio", "Broccio Demi-Affine",
+ "Brousse du Rove", "Bruder Basil", "Brusselae Kaas (Fromage de Bruxelles)", "Bryndza",
+ "Buchette d'Anjou", "Buffalo", "Burgos", "Butte", "Butterkase", "Button (Innes)",
+ "Buxton Blue", "Cabecou", "Caboc", "Cabrales", "Cachaille", "Caciocavallo", "Caciotta",
+ "Caerphilly", "Cairnsmore", "Calenzana", "Cambazola", "Camembert de Normandie",
+ "Canadian Cheddar", "Canestrato", "Cantal", "Caprice des Dieux", "Capricorn Goat",
+ "Capriole Banon", "Carre de l'Est", "Casciotta di Urbino", "Cashel Blue", "Castellano",
+ "Castelleno", "Castelmagno", "Castelo Branco", "Castigliano", "Cathelain",
+ "Celtic Promise", "Cendre d'Olivet", "Cerney", "Chabichou", "Chabichou du Poitou",
+ "Chabis de Gatine", "Chaource", "Charolais", "Chaumes", "Cheddar",
+ "Cheddar Clothbound", "Cheshire", "Chevres", "Chevrotin des Aravis", "Chontaleno",
+ "Civray", "Coeur de Camembert au Calvados", "Coeur de Chevre", "Colby", "Cold Pack",
+ "Comte", "Coolea", "Cooleney", "Coquetdale", "Corleggy", "Cornish Pepper",
+ "Cotherstone", "Cotija", "Cottage Cheese", "Cottage Cheese (Australian)",
+ "Cougar Gold", "Coulommiers", "Coverdale", "Crayeux de Roncq", "Cream Cheese",
+ "Cream Havarti", "Crema Agria", "Crema Mexicana", "Creme Fraiche", "Crescenza",
+ "Croghan", "Crottin de Chavignol", "Crottin du Chavignol", "Crowdie", "Crowley",
+ "Cuajada", "Curd", "Cure Nantais", "Curworthy", "Cwmtawe Pecorino",
+ "Cypress Grove Chevre", "Danablu (Danish Blue)", "Danbo", "Danish Fontina",
+ "Daralagjazsky", "Dauphin", "Delice des Fiouves", "Denhany Dorset Drum", "Derby",
+ "Dessertnyj Belyj", "Devon Blue", "Devon Garland", "Dolcelatte", "Doolin",
+ "Doppelrhamstufel", "Dorset Blue Vinney", "Double Gloucester", "Double Worcester",
+ "Dreux a la Feuille", "Dry Jack", "Duddleswell", "Dunbarra", "Dunlop", "Dunsyre Blue",
+ "Duroblando", "Durrus", "Dutch Mimolette (Commissiekaas)", "Edam", "Edelpilz",
+ "Emental Grand Cru", "Emlett", "Emmental", "Epoisses de Bourgogne", "Esbareich",
+ "Esrom", "Etorki", "Evansdale Farmhouse Brie", "Evora De L'Alentejo", "Exmoor Blue",
+ "Explorateur", "Feta", "Feta (Australian)", "Figue", "Filetta", "Fin-de-Siecle",
+ "Finlandia Swiss", "Finn", "Fiore Sardo", "Fleur du Maquis", "Flor de Guia",
+ "Flower Marie", "Folded", "Folded cheese with mint", "Fondant de Brebis",
+ "Fontainebleau", "Fontal", "Fontina Val d'Aosta", "Formaggio di capra", "Fougerus",
+ "Four Herb Gouda", "Fourme d' Ambert", "Fourme de Haute Loire", "Fourme de Montbrison",
+ "Fresh Jack", "Fresh Mozzarella", "Fresh Ricotta", "Fresh Truffles", "Fribourgeois",
+ "Friesekaas", "Friesian", "Friesla", "Frinault", "Fromage a Raclette", "Fromage Corse",
+ "Fromage de Montagne de Savoie", "Fromage Frais", "Fruit Cream Cheese",
+ "Frying Cheese", "Fynbo", "Gabriel", "Galette du Paludier", "Galette Lyonnaise",
+ "Galloway Goat's Milk Gems", "Gammelost", "Gaperon a l'Ail", "Garrotxa", "Gastanberra",
+ "Geitost", "Gippsland Blue", "Gjetost", "Gloucester", "Golden Cross", "Gorgonzola",
+ "Gornyaltajski", "Gospel Green", "Gouda", "Goutu", "Gowrie", "Grabetto", "Graddost",
+ "Grafton Village Cheddar", "Grana", "Grana Padano", "Grand Vatel",
+ "Grataron d' Areches", "Gratte-Paille", "Graviera", "Greuilh", "Greve",
+ "Gris de Lille", "Gruyere", "Gubbeen", "Guerbigny", "Halloumi",
+ "Halloumy (Australian)", "Haloumi-Style Cheese", "Harbourne Blue", "Havarti",
+ "Heidi Gruyere", "Hereford Hop", "Herrgardsost", "Herriot Farmhouse", "Herve",
+ "Hipi Iti", "Hubbardston Blue Cow", "Hushallsost", "Iberico", "Idaho Goatster",
+ "Idiazabal", "Il Boschetto al Tartufo", "Ile d'Yeu", "Isle of Mull", "Jarlsberg",
+ "Jermi Tortes", "Jibneh Arabieh", "Jindi Brie", "Jubilee Blue", "Juustoleipa",
+ "Kadchgall", "Kaseri", "Kashta", "Kefalotyri", "Kenafa", "Kernhem", "Kervella Affine",
+ "Kikorangi", "King Island Cape Wickham Brie", "King River Gold", "Klosterkaese",
+ "Knockalara", "Kugelkase", "L'Aveyronnais", "L'Ecir de l'Aubrac", "La Taupiniere",
+ "La Vache Qui Rit", "Laguiole", "Lairobell", "Lajta", "Lanark Blue", "Lancashire",
+ "Langres", "Lappi", "Laruns", "Lavistown", "Le Brin", "Le Fium Orbo", "Le Lacandou",
+ "Le Roule", "Leafield", "Lebbene", "Leerdammer", "Leicester", "Leyden", "Limburger",
+ "Lincolnshire Poacher", "Lingot Saint Bousquet d'Orb", "Liptauer", "Little Rydings",
+ "Livarot", "Llanboidy", "Llanglofan Farmhouse", "Loch Arthur Farmhouse",
+ "Loddiswell Avondale", "Longhorn", "Lou Palou", "Lou Pevre", "Lyonnais", "Maasdam",
+ "Macconais", "Mahoe Aged Gouda", "Mahon", "Malvern", "Mamirolle", "Manchego",
+ "Manouri", "Manur", "Marble Cheddar", "Marbled Cheeses", "Maredsous", "Margotin",
+ "Maribo", "Maroilles", "Mascares", "Mascarpone", "Mascarpone (Australian)",
+ "Mascarpone Torta", "Matocq", "Maytag Blue", "Meira", "Menallack Farmhouse",
+ "Menonita", "Meredith Blue", "Mesost", "Metton (Cancoillotte)", "Meyer Vintage Gouda",
+ "Mihalic Peynir", "Milleens", "Mimolette", "Mine-Gabhar", "Mini Baby Bells", "Mixte",
+ "Molbo", "Monastery Cheeses", "Mondseer", "Mont D'or Lyonnais", "Montasio",
+ "Monterey Jack", "Monterey Jack Dry", "Morbier", "Morbier Cru de Montagne",
+ "Mothais a la Feuille", "Mozzarella", "Mozzarella (Australian)",
+ "Mozzarella di Bufala", "Mozzarella Fresh, in water", "Mozzarella Rolls", "Munster",
+ "Murol", "Mycella", "Myzithra", "Naboulsi", "Nantais", "Neufchatel",
+ "Neufchatel (Australian)", "Niolo", "Nokkelost", "Northumberland", "Oaxaca",
+ "Olde York", "Olivet au Foin", "Olivet Bleu", "Olivet Cendre",
+ "Orkney Extra Mature Cheddar", "Orla", "Oschtjepka", "Ossau Fermier", "Ossau-Iraty",
+ "Oszczypek", "Oxford Blue", "P'tit Berrichon", "Palet de Babligny", "Paneer", "Panela",
+ "Pannerone", "Pant ys Gawn", "Parmesan (Parmigiano)", "Parmigiano Reggiano",
+ "Pas de l'Escalette", "Passendale", "Pasteurized Processed", "Pate de Fromage",
+ "Patefine Fort", "Pave d'Affinois", "Pave d'Auge", "Pave de Chirac", "Pave du Berry",
+ "Pecorino", "Pecorino in Walnut Leaves", "Pecorino Romano", "Peekskill Pyramid",
+ "Pelardon des Cevennes", "Pelardon des Corbieres", "Penamellera", "Penbryn",
+ "Pencarreg", "Perail de Brebis", "Petit Morin", "Petit Pardou", "Petit-Suisse",
+ "Picodon de Chevre", "Picos de Europa", "Piora", "Pithtviers au Foin",
+ "Plateau de Herve", "Plymouth Cheese", "Podhalanski", "Poivre d'Ane", "Polkolbin",
+ "Pont l'Eveque", "Port Nicholson", "Port-Salut", "Postel", "Pouligny-Saint-Pierre",
+ "Pourly", "Prastost", "Pressato", "Prince-Jean", "Processed Cheddar", "Provolone",
+ "Provolone (Australian)", "Pyengana Cheddar", "Pyramide", "Quark",
+ "Quark (Australian)", "Quartirolo Lombardo", "Quatre-Vents", "Quercy Petit",
+ "Queso Blanco", "Queso Blanco con Frutas --Pina y Mango", "Queso de Murcia",
+ "Queso del Montsec", "Queso del Tietar", "Queso Fresco", "Queso Fresco (Adobera)",
+ "Queso Iberico", "Queso Jalapeno", "Queso Majorero", "Queso Media Luna",
+ "Queso Para Frier", "Queso Quesadilla", "Rabacal", "Raclette", "Ragusano", "Raschera",
+ "Reblochon", "Red Leicester", "Regal de la Dombes", "Reggianito", "Remedou",
+ "Requeson", "Richelieu", "Ricotta", "Ricotta (Australian)", "Ricotta Salata", "Ridder",
+ "Rigotte", "Rocamadour", "Rollot", "Romano", "Romans Part Dieu", "Roncal", "Roquefort",
+ "Roule", "Rouleau De Beaulieu", "Royalp Tilsit", "Rubens", "Rustinu", "Saaland Pfarr",
+ "Saanenkaese", "Saga", "Sage Derby", "Sainte Maure", "Saint-Marcellin",
+ "Saint-Nectaire", "Saint-Paulin", "Salers", "Samso", "San Simon", "Sancerre",
+ "Sap Sago", "Sardo", "Sardo Egyptian", "Sbrinz", "Scamorza", "Schabzieger", "Schloss",
+ "Selles sur Cher", "Selva", "Serat", "Seriously Strong Cheddar", "Serra da Estrela",
+ "Sharpam", "Shelburne Cheddar", "Shropshire Blue", "Siraz", "Sirene", "Smoked Gouda",
+ "Somerset Brie", "Sonoma Jack", "Sottocenare al Tartufo", "Soumaintrain",
+ "Sourire Lozerien", "Spenwood", "Sraffordshire Organic", "St. Agur Blue Cheese",
+ "Stilton", "Stinking Bishop", "String", "Sussex Slipcote", "Sveciaost", "Swaledale",
+ "Sweet Style Swiss", "Swiss", "Syrian (Armenian String)", "Tala", "Taleggio", "Tamie",
+ "Tasmania Highland Chevre Log", "Taupiniere", "Teifi", "Telemea", "Testouri",
+ "Tete de Moine", "Tetilla", "Texas Goat Cheese", "Tibet", "Tillamook Cheddar",
+ "Tilsit", "Timboon Brie", "Toma", "Tomme Brulee", "Tomme d'Abondance",
+ "Tomme de Chevre", "Tomme de Romans", "Tomme de Savoie", "Tomme des Chouans", "Tommes",
+ "Torta del Casar", "Toscanello", "Touree de L'Aubier", "Tourmalet",
+ "Trappe (Veritable)", "Trois Cornes De Vendee", "Tronchon", "Trou du Cru", "Truffe",
+ "Tupi", "Turunmaa", "Tymsboro", "Tyn Grug", "Tyning", "Ubriaco", "Ulloa",
+ "Vacherin-Fribourgeois", "Valencay", "Vasterbottenost", "Venaco", "Vendomois",
+ "Vieux Corse", "Vignotte", "Vulscombe", "Waimata Farmhouse Blue",
+ "Washed Rind Cheese (Australian)", "Waterloo", "Weichkaese", "Wellington",
+ "Wensleydale", "White Stilton", "Whitestone Farmhouse", "Wigmore", "Woodside Cabecou",
+ "Xanadu", "Xynotyro", "Yarg Cornish", "Yarra Valley Pyramid", "Yorkshire Blue",
+ "Zamorano", "Zanetti Grana Padano", "Zanetti Parmigiano Reggiano"
+ };
+
+ public static ArrayList<String> asList() {
+ ArrayList<String> items = new ArrayList<String>();
+ for (int i = 0, z = CHEESES.length ; i < z ; i++) {
+ items.add(CHEESES[i]);
+ }
+ return items;
+ }
+}
\ No newline at end of file
diff --git a/common/src/java/com/example/android/common/logger/Log.java b/common/src/java/com/example/android/common/logger/Log.java
new file mode 100644
index 0000000..17503c5
--- /dev/null
+++ b/common/src/java/com/example/android/common/logger/Log.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2013 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.example.android.common.logger;
+
+/**
+ * Helper class for a list (or tree) of LoggerNodes.
+ *
+ * <p>When this is set as the head of the list,
+ * an instance of it can function as a drop-in replacement for {@link android.util.Log}.
+ * Most of the methods in this class server only to map a method call in Log to its equivalent
+ * in LogNode.</p>
+ */
+public class Log {
+ // Grabbing the native values from Android's native logging facilities,
+ // to make for easy migration and interop.
+ public static final int NONE = -1;
+ public static final int VERBOSE = android.util.Log.VERBOSE;
+ public static final int DEBUG = android.util.Log.DEBUG;
+ public static final int INFO = android.util.Log.INFO;
+ public static final int WARN = android.util.Log.WARN;
+ public static final int ERROR = android.util.Log.ERROR;
+ public static final int ASSERT = android.util.Log.ASSERT;
+
+ // Stores the beginning of the LogNode topology.
+ private static LogNode mLogNode;
+
+ /**
+ * Returns the next LogNode in the linked list.
+ */
+ public static LogNode getLogNode() {
+ return mLogNode;
+ }
+
+ /**
+ * Sets the LogNode data will be sent to.
+ */
+ public static void setLogNode(LogNode node) {
+ mLogNode = node;
+ }
+
+ /**
+ * Instructs the LogNode to print the log data provided. Other LogNodes can
+ * be chained to the end of the LogNode as desired.
+ *
+ * @param priority Log level of the data being logged. Verbose, Error, etc.
+ * @param tag Tag for for the log data. Can be used to organize log statements.
+ * @param msg The actual message to be logged.
+ * @param tr If an exception was thrown, this can be sent along for the logging facilities
+ * to extract and print useful information.
+ */
+ public static void println(int priority, String tag, String msg, Throwable tr) {
+ if (mLogNode != null) {
+ mLogNode.println(priority, tag, msg, tr);
+ }
+ }
+
+ /**
+ * Instructs the LogNode to print the log data provided. Other LogNodes can
+ * be chained to the end of the LogNode as desired.
+ *
+ * @param priority Log level of the data being logged. Verbose, Error, etc.
+ * @param tag Tag for for the log data. Can be used to organize log statements.
+ * @param msg The actual message to be logged. The actual message to be logged.
+ */
+ public static void println(int priority, String tag, String msg) {
+ println(priority, tag, msg, null);
+ }
+
+ /**
+ * Prints a message at VERBOSE priority.
+ *
+ * @param tag Tag for for the log data. Can be used to organize log statements.
+ * @param msg The actual message to be logged.
+ * @param tr If an exception was thrown, this can be sent along for the logging facilities
+ * to extract and print useful information.
+ */
+ public static void v(String tag, String msg, Throwable tr) {
+ println(VERBOSE, tag, msg, tr);
+ }
+
+ /**
+ * Prints a message at VERBOSE priority.
+ *
+ * @param tag Tag for for the log data. Can be used to organize log statements.
+ * @param msg The actual message to be logged.
+ */
+ public static void v(String tag, String msg) {
+ v(tag, msg, null);
+ }
+
+
+ /**
+ * Prints a message at DEBUG priority.
+ *
+ * @param tag Tag for for the log data. Can be used to organize log statements.
+ * @param msg The actual message to be logged.
+ * @param tr If an exception was thrown, this can be sent along for the logging facilities
+ * to extract and print useful information.
+ */
+ public static void d(String tag, String msg, Throwable tr) {
+ println(DEBUG, tag, msg, tr);
+ }
+
+ /**
+ * Prints a message at DEBUG priority.
+ *
+ * @param tag Tag for for the log data. Can be used to organize log statements.
+ * @param msg The actual message to be logged.
+ */
+ public static void d(String tag, String msg) {
+ d(tag, msg, null);
+ }
+
+ /**
+ * Prints a message at INFO priority.
+ *
+ * @param tag Tag for for the log data. Can be used to organize log statements.
+ * @param msg The actual message to be logged.
+ * @param tr If an exception was thrown, this can be sent along for the logging facilities
+ * to extract and print useful information.
+ */
+ public static void i(String tag, String msg, Throwable tr) {
+ println(INFO, tag, msg, tr);
+ }
+
+ /**
+ * Prints a message at INFO priority.
+ *
+ * @param tag Tag for for the log data. Can be used to organize log statements.
+ * @param msg The actual message to be logged.
+ */
+ public static void i(String tag, String msg) {
+ i(tag, msg, null);
+ }
+
+ /**
+ * Prints a message at WARN priority.
+ *
+ * @param tag Tag for for the log data. Can be used to organize log statements.
+ * @param msg The actual message to be logged.
+ * @param tr If an exception was thrown, this can be sent along for the logging facilities
+ * to extract and print useful information.
+ */
+ public static void w(String tag, String msg, Throwable tr) {
+ println(WARN, tag, msg, tr);
+ }
+
+ /**
+ * Prints a message at WARN priority.
+ *
+ * @param tag Tag for for the log data. Can be used to organize log statements.
+ * @param msg The actual message to be logged.
+ */
+ public static void w(String tag, String msg) {
+ w(tag, msg, null);
+ }
+
+ /**
+ * Prints a message at WARN priority.
+ *
+ * @param tag Tag for for the log data. Can be used to organize log statements.
+ * @param tr If an exception was thrown, this can be sent along for the logging facilities
+ * to extract and print useful information.
+ */
+ public static void w(String tag, Throwable tr) {
+ w(tag, null, tr);
+ }
+
+ /**
+ * Prints a message at ERROR priority.
+ *
+ * @param tag Tag for for the log data. Can be used to organize log statements.
+ * @param msg The actual message to be logged.
+ * @param tr If an exception was thrown, this can be sent along for the logging facilities
+ * to extract and print useful information.
+ */
+ public static void e(String tag, String msg, Throwable tr) {
+ println(ERROR, tag, msg, tr);
+ }
+
+ /**
+ * Prints a message at ERROR priority.
+ *
+ * @param tag Tag for for the log data. Can be used to organize log statements.
+ * @param msg The actual message to be logged.
+ */
+ public static void e(String tag, String msg) {
+ e(tag, msg, null);
+ }
+
+ /**
+ * Prints a message at ASSERT priority.
+ *
+ * @param tag Tag for for the log data. Can be used to organize log statements.
+ * @param msg The actual message to be logged.
+ * @param tr If an exception was thrown, this can be sent along for the logging facilities
+ * to extract and print useful information.
+ */
+ public static void wtf(String tag, String msg, Throwable tr) {
+ println(ASSERT, tag, msg, tr);
+ }
+
+ /**
+ * Prints a message at ASSERT priority.
+ *
+ * @param tag Tag for for the log data. Can be used to organize log statements.
+ * @param msg The actual message to be logged.
+ */
+ public static void wtf(String tag, String msg) {
+ wtf(tag, msg, null);
+ }
+
+ /**
+ * Prints a message at ASSERT priority.
+ *
+ * @param tag Tag for for the log data. Can be used to organize log statements.
+ * @param tr If an exception was thrown, this can be sent along for the logging facilities
+ * to extract and print useful information.
+ */
+ public static void wtf(String tag, Throwable tr) {
+ wtf(tag, null, tr);
+ }
+}
diff --git a/common/src/java/com/example/android/common/logger/LogNode.java b/common/src/java/com/example/android/common/logger/LogNode.java
new file mode 100644
index 0000000..bc37cab
--- /dev/null
+++ b/common/src/java/com/example/android/common/logger/LogNode.java
@@ -0,0 +1,39 @@
+/*
+ * 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.example.android.common.logger;
+
+/**
+ * Basic interface for a logging system that can output to one or more targets.
+ * Note that in addition to classes that will output these logs in some format,
+ * one can also implement this interface over a filter and insert that in the chain,
+ * such that no targets further down see certain data, or see manipulated forms of the data.
+ * You could, for instance, write a "ToHtmlLoggerNode" that just converted all the log data
+ * it received to HTML and sent it along to the next node in the chain, without printing it
+ * anywhere.
+ */
+public interface LogNode {
+
+ /**
+ * Instructs first LogNode in the list to print the log data provided.
+ * @param priority Log level of the data being logged. Verbose, Error, etc.
+ * @param tag Tag for for the log data. Can be used to organize log statements.
+ * @param msg The actual message to be logged. The actual message to be logged.
+ * @param tr If an exception was thrown, this can be sent along for the logging facilities
+ * to extract and print useful information.
+ */
+ public void println(int priority, String tag, String msg, Throwable tr);
+
+}
diff --git a/common/src/java/com/example/android/common/logger/LogView.java b/common/src/java/com/example/android/common/logger/LogView.java
new file mode 100644
index 0000000..234263d
--- /dev/null
+++ b/common/src/java/com/example/android/common/logger/LogView.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2013 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.example.android.common.logger;
+
+import android.content.Context;
+import android.util.*;
+import android.widget.TextView;
+
+/** Simple TextView which is used to output log data received through the LogNode interface.
+*/
+public class LogView extends TextView implements LogNode {
+
+ public LogView(Context context) {
+ super(context);
+ }
+
+ public LogView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public LogView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ /**
+ * Formats the log data and prints it out to the LogView.
+ * @param priority Log level of the data being logged. Verbose, Error, etc.
+ * @param tag Tag for for the log data. Can be used to organize log statements.
+ * @param msg The actual message to be logged. The actual message to be logged.
+ * @param tr If an exception was thrown, this can be sent along for the logging facilities
+ * to extract and print useful information.
+ */
+ @Override
+ public void println(int priority, String tag, String msg, Throwable tr) {
+ String priorityStr = null;
+
+ // For the purposes of this View, we want to print the priority as readable text.
+ switch(priority) {
+ case android.util.Log.VERBOSE:
+ priorityStr = "VERBOSE";
+ break;
+ case android.util.Log.DEBUG:
+ priorityStr = "DEBUG";
+ break;
+ case android.util.Log.INFO:
+ priorityStr = "INFO";
+ break;
+ case android.util.Log.WARN:
+ priorityStr = "WARN";
+ break;
+ case android.util.Log.ERROR:
+ priorityStr = "ERROR";
+ break;
+ case android.util.Log.ASSERT:
+ priorityStr = "ASSERT";
+ break;
+ default:
+ break;
+ }
+
+ // Handily, the Log class has a facility for converting a stack trace into a usable string.
+ String exceptionStr = null;
+ if (tr != null) {
+ exceptionStr = android.util.Log.getStackTraceString(tr);
+ }
+
+ // Take the priority, tag, message, and exception, and concatenate as necessary
+ // into one usable line of text.
+ StringBuilder outputBuilder = new StringBuilder();
+
+ String delimiter = "\t";
+ appendIfNotNull(outputBuilder, priorityStr, delimiter);
+ appendIfNotNull(outputBuilder, tag, delimiter);
+ appendIfNotNull(outputBuilder, msg, delimiter);
+ appendIfNotNull(outputBuilder, exceptionStr, delimiter);
+
+ // Actually display the text we just generated within the LogView.
+ appendToLog(outputBuilder.toString());
+
+ if (mNext != null) {
+ mNext.println(priority, tag, msg, tr);
+ }
+ }
+
+ public LogNode getNext() {
+ return mNext;
+ }
+
+ public void setNext(LogNode node) {
+ mNext = node;
+ }
+
+ /** Takes a string and adds to it, with a separator, if the bit to be added isn't null. Since
+ * the logger takes so many arguments that might be null, this method helps cut out some of the
+ * agonizing tedium of writing the same 3 lines over and over.
+ * @param source StringBuilder containing the text to append to.
+ * @param addStr The String to append
+ * @param delimiter The String to separate the source and appended strings. A tab or comma,
+ * for instance.
+ * @return The fully concatenated String as a StringBuilder
+ */
+ private StringBuilder appendIfNotNull(StringBuilder source, String addStr, String delimiter) {
+ if (addStr != null) {
+ if (addStr.length() == 0) {
+ delimiter = "";
+ }
+
+ return source.append(addStr).append(delimiter);
+ }
+ return source;
+ }
+
+ // The next LogNode in the chain.
+ LogNode mNext;
+
+ /** Outputs the string as a new line of log data in the LogView. */
+ public void appendToLog(String s) {
+ append("\n" + s);
+ }
+
+
+}
diff --git a/common/src/java/com/example/android/common/logger/LogWrapper.java b/common/src/java/com/example/android/common/logger/LogWrapper.java
new file mode 100644
index 0000000..16a9e7b
--- /dev/null
+++ b/common/src/java/com/example/android/common/logger/LogWrapper.java
@@ -0,0 +1,75 @@
+/*
+ * 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.example.android.common.logger;
+
+import android.util.Log;
+
+/**
+ * Helper class which wraps Android's native Log utility in the Logger interface. This way
+ * normal DDMS output can be one of the many targets receiving and outputting logs simultaneously.
+ */
+public class LogWrapper implements LogNode {
+
+ // For piping: The next node to receive Log data after this one has done its work.
+ private LogNode mNext;
+
+ /**
+ * Returns the next LogNode in the linked list.
+ */
+ public LogNode getNext() {
+ return mNext;
+ }
+
+ /**
+ * Sets the LogNode data will be sent to..
+ */
+ public void setNext(LogNode node) {
+ mNext = node;
+ }
+
+ /**
+ * Prints data out to the console using Android's native log mechanism.
+ * @param priority Log level of the data being logged. Verbose, Error, etc.
+ * @param tag Tag for for the log data. Can be used to organize log statements.
+ * @param msg The actual message to be logged. The actual message to be logged.
+ * @param tr If an exception was thrown, this can be sent along for the logging facilities
+ * to extract and print useful information.
+ */
+ @Override
+ public void println(int priority, String tag, String msg, Throwable tr) {
+ // There actually are log methods that don't take a msg parameter. For now,
+ // if that's the case, just convert null to the empty string and move on.
+ String useMsg = msg;
+ if (useMsg == null) {
+ useMsg = "";
+ }
+
+ // If an exeption was provided, convert that exception to a usable string and attach
+ // it to the end of the msg method.
+ if (tr != null) {
+ msg += "\n" + Log.getStackTraceString(tr);
+ }
+
+ // This is functionally identical to Log.x(tag, useMsg);
+ // For instance, if priority were Log.VERBOSE, this would be the same as Log.v(tag, useMsg)
+ Log.println(priority, tag, useMsg);
+
+ // If this isn't the last node in the chain, move things along.
+ if (mNext != null) {
+ mNext.println(priority, tag, msg, tr);
+ }
+ }
+}
diff --git a/common/src/java/com/example/android/common/logger/MessageOnlyLogFilter.java b/common/src/java/com/example/android/common/logger/MessageOnlyLogFilter.java
new file mode 100644
index 0000000..19967dc
--- /dev/null
+++ b/common/src/java/com/example/android/common/logger/MessageOnlyLogFilter.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2013 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.example.android.common.logger;
+
+/**
+ * Simple {@link LogNode} filter, removes everything except the message.
+ * Useful for situations like on-screen log output where you don't want a lot of metadata displayed,
+ * just easy-to-read message updates as they're happening.
+ */
+public class MessageOnlyLogFilter implements LogNode {
+
+ LogNode mNext;
+
+ /**
+ * Takes the "next" LogNode as a parameter, to simplify chaining.
+ *
+ * @param next The next LogNode in the pipeline.
+ */
+ public MessageOnlyLogFilter(LogNode next) {
+ mNext = next;
+ }
+
+ public MessageOnlyLogFilter() {
+ }
+
+ @Override
+ public void println(int priority, String tag, String msg, Throwable tr) {
+ if (mNext != null) {
+ getNext().println(Log.NONE, null, msg, null);
+ }
+ }
+
+ /**
+ * Returns the next LogNode in the chain.
+ */
+ public LogNode getNext() {
+ return mNext;
+ }
+
+ /**
+ * Sets the LogNode data will be sent to..
+ */
+ public void setNext(LogNode node) {
+ mNext = node;
+ }
+
+}
diff --git a/common/src/com/example/android/common/Pools.java b/common/src/java/com/example/android/common/util/Pools.java
similarity index 99%
rename from common/src/com/example/android/common/Pools.java
rename to common/src/java/com/example/android/common/util/Pools.java
index b31749a..1b7edb0 100644
--- a/common/src/com/example/android/common/Pools.java
+++ b/common/src/java/com/example/android/common/util/Pools.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.example.android.common;
+package com.example.android.common.util;
/**
* Helper class for creating pools of objects. Creating new objects is an
diff --git a/sampleSamples/BaseSample/BaseSampleSample/build.gradle b/sampleSamples/BaseSample/BaseSampleSample/build.gradle
new file mode 100644
index 0000000..7f993fa
--- /dev/null
+++ b/sampleSamples/BaseSample/BaseSampleSample/build.gradle
@@ -0,0 +1,34 @@
+
+buildscript {
+ repositories {
+ mavenCentral()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:0.4.2'
+ }
+}
+
+apply plugin: 'android'
+
+
+android {
+ compileSdkVersion 17
+ buildToolsVersion "17.0.0"
+
+ defaultConfig {
+ minSdkVersion 4
+ targetSdkVersion 17
+ }
+}
+
+task preflight (dependsOn: parent.preflight) {}
+
+// Inject a preflight task into each variant so we have a place to hook tasks
+// that need to run before any of the android build tasks.
+android.applicationVariants.each { variant ->
+ tasks.getByPath("prepare${variant.name.capitalize()}Dependencies").dependsOn preflight
+}
+
+
+
diff --git a/sampleSamples/BaseSample/BaseSampleSample/proguard-project.txt b/sampleSamples/BaseSample/BaseSampleSample/proguard-project.txt
new file mode 100644
index 0000000..0d8f171
--- /dev/null
+++ b/sampleSamples/BaseSample/BaseSampleSample/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/sampleSamples/BaseSample/BaseSampleSample/src/main/AndroidManifest.xml b/sampleSamples/BaseSample/BaseSampleSample/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..456eecf
--- /dev/null
+++ b/sampleSamples/BaseSample/BaseSampleSample/src/main/AndroidManifest.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2013 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.basesample"
+ android:versionCode="1"
+ android:versionName="1.0">
+
+ <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="17" />
+
+ <application android:allowBackup="true"
+ android:label="@string/app_name"
+ android:icon="@drawable/ic_launcher"
+ android:theme="@style/AppTheme">
+
+ <activity android:name=".MainActivity"
+ 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>
diff --git a/sampleSamples/BaseSample/build.gradle b/sampleSamples/BaseSample/build.gradle
new file mode 100644
index 0000000..bf09c05
--- /dev/null
+++ b/sampleSamples/BaseSample/build.gradle
@@ -0,0 +1,6 @@
+// BEGIN_EXCLUDE
+apply from: "../../common/build/build.gradle"
+samplegen {
+pathToSamplesCommon "../../common"
+}
+// END_EXCLUDE
\ No newline at end of file
diff --git a/sampleSamples/BaseSample/buildSrc/build.gradle b/sampleSamples/BaseSample/buildSrc/build.gradle
new file mode 100644
index 0000000..b2978f5
--- /dev/null
+++ b/sampleSamples/BaseSample/buildSrc/build.gradle
@@ -0,0 +1,15 @@
+repositories {
+ mavenCentral()
+}
+dependencies {
+ compile 'org.freemarker:freemarker:2.3.20'
+}
+
+sourceSets {
+ main {
+ groovy {
+ srcDir new File(rootDir, "../../../common/build/buildSrc/src/main/groovy")
+ }
+ }
+}
+
diff --git a/sampleSamples/BaseSample/template-params.xml b/sampleSamples/BaseSample/template-params.xml
new file mode 100644
index 0000000..6cd6ee9
--- /dev/null
+++ b/sampleSamples/BaseSample/template-params.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2013 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.
+-->
+<sample>
+ <name>BaseSample</name>
+ <package>com.example.android.basesample</package>
+
+
+ <!--TODO: change minSdk if needed-->
+ <minSdk>4</minSdk>
+
+
+ <template src="base"/>
+
+ <common src="logger"/>
+
+ <strings>
+ <intro>
+ <![CDATA[
+ Introductory text that explains what the sample is intended to demonstrate. Edit
+ in TemplateData.xml.
+ ]]>
+ </intro>
+ </strings>
+</sample>
diff --git a/ui/actionbar/DoneBar/DoneBar/src/main/java/com/example/android/donebar/DoneButtonActivity.java b/ui/actionbar/DoneBar/DoneBar/src/main/java/com/example/android/donebar/DoneButtonActivity.java
index 303d1ed..3b1e37d 100755
--- a/ui/actionbar/DoneBar/DoneBar/src/main/java/com/example/android/donebar/DoneButtonActivity.java
+++ b/ui/actionbar/DoneBar/DoneBar/src/main/java/com/example/android/donebar/DoneButtonActivity.java
@@ -67,6 +67,7 @@
return true;
}
+
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
diff --git a/ui/actionbar/DoneBar/proguard-project.txt b/ui/actionbar/DoneBar/proguard-project.txt
new file mode 100644
index 0000000..f2fe155
--- /dev/null
+++ b/ui/actionbar/DoneBar/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/ui/actionbarcompat/ListViewModalSelect/big_icon.png b/ui/actionbarcompat/ListViewModalSelect/big_icon.png
new file mode 100644
index 0000000..0f47486
--- /dev/null
+++ b/ui/actionbarcompat/ListViewModalSelect/big_icon.png
Binary files differ
diff --git a/ui/actionbarcompat/ListViewModalSelect/build.gradle b/ui/actionbarcompat/ListViewModalSelect/build.gradle
new file mode 100644
index 0000000..b99b102
--- /dev/null
+++ b/ui/actionbarcompat/ListViewModalSelect/build.gradle
@@ -0,0 +1,16 @@
+apply plugin: 'android'
+
+dependencies {
+ compile "com.android.support:support-v4:18.0.+"
+ compile "com.android.support:appcompat-v7:18.0.+"
+}
+
+android {
+ compileSdkVersion 17
+ buildToolsVersion "17.0.0"
+
+ defaultConfig {
+ minSdkVersion 7
+ targetSdkVersion 16
+ }
+}
diff --git a/ui/actionbarcompat/ListViewModalSelect/src/main/AndroidManifest.xml b/ui/actionbarcompat/ListViewModalSelect/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..1ca1c5d
--- /dev/null
+++ b/ui/actionbarcompat/ListViewModalSelect/src/main/AndroidManifest.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.actionbarcompat.listviewmodalselect"
+ android:versionCode="1"
+ android:versionName="1.0">
+
+ <!--
+ ActionBarCompat provides an Action Bar from API v7 onwards
+ -->
+ <uses-sdk
+ android:minSdkVersion="7"
+ android:targetSdkVersion="17" />
+
+ <application
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name"
+ android:theme="@style/Theme.AppCompat"
+ android:allowBackup="true">
+
+ <activity
+ android:name=".MainActivity">
+ <!-- Launcher Intent filter -->
+ <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/ui/actionbarcompat/ListViewModalSelect/src/main/res/drawable-hdpi/ic_launcher.png b/ui/actionbarcompat/ListViewModalSelect/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..a3d6711
--- /dev/null
+++ b/ui/actionbarcompat/ListViewModalSelect/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/ui/actionbarcompat/ListViewModalSelect/src/main/res/drawable-mdpi/ic_launcher.png b/ui/actionbarcompat/ListViewModalSelect/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..f488bbc
--- /dev/null
+++ b/ui/actionbarcompat/ListViewModalSelect/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/ui/actionbarcompat/ListViewModalSelect/src/main/res/drawable-xhdpi/ic_launcher.png b/ui/actionbarcompat/ListViewModalSelect/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..0651b33
--- /dev/null
+++ b/ui/actionbarcompat/ListViewModalSelect/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/ui/actionbarcompat/ListViewModalSelect/src/main/res/drawable-xxhdpi/ic_launcher.png b/ui/actionbarcompat/ListViewModalSelect/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..ac8a199
--- /dev/null
+++ b/ui/actionbarcompat/ListViewModalSelect/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/ui/actionbarcompat/ListViewModalSelect/src/main/res/drawable/background_checked.xml b/ui/actionbarcompat/ListViewModalSelect/src/main/res/drawable/background_checked.xml
new file mode 100644
index 0000000..9d8bef3
--- /dev/null
+++ b/ui/actionbarcompat/ListViewModalSelect/src/main/res/drawable/background_checked.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2013 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_checked="true" android:drawable="@color/translucent_blue" />
+ <item android:drawable="@android:color/transparent" />
+</selector>
\ No newline at end of file
diff --git a/ui/actionbarcompat/ListViewModalSelect/src/main/res/layout/activity_main.xml b/ui/actionbarcompat/ListViewModalSelect/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..456cd19
--- /dev/null
+++ b/ui/actionbarcompat/ListViewModalSelect/src/main/res/layout/activity_main.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2013 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <fragment
+ android:id="@+id/frag_app_list"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:name="com.example.android.actionbarcompat.listviewmodalselect.CheeseListFragment" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/intro_message"
+ android:padding="16dp"
+ android:textAppearance="?android:textAppearanceMedium"
+ android:lineSpacingMultiplier="1.1"
+ android:background="#fb3"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/ui/actionbarcompat/ListViewModalSelect/src/main/res/layout/simple_selectable_list_item.xml b/ui/actionbarcompat/ListViewModalSelect/src/main/res/layout/simple_selectable_list_item.xml
new file mode 100644
index 0000000..5f82f4b
--- /dev/null
+++ b/ui/actionbarcompat/ListViewModalSelect/src/main/res/layout/simple_selectable_list_item.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+
+<!--
+ The layout which is used in our ListView items. We use a CheckedTextView so we can
+ simulate the activated state (added in available from API v11) with the checked
+ state, which is available from API v1.
+-->
+<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/text1"
+ android:layout_width="match_parent"
+ android:layout_height="?android:attr/listPreferredItemHeight"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:gravity="center_vertical"
+ android:background="@drawable/background_checked"
+ android:paddingLeft="6dip"
+ android:paddingRight="9dip"/>
\ No newline at end of file
diff --git a/ui/actionbarcompat/ListViewModalSelect/src/main/res/menu/context_cheeses.xml b/ui/actionbarcompat/ListViewModalSelect/src/main/res/menu/context_cheeses.xml
new file mode 100644
index 0000000..e845a4d
--- /dev/null
+++ b/ui/actionbarcompat/ListViewModalSelect/src/main/res/menu/context_cheeses.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2013 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.
+-->
+
+<!--
+ As we're using ActionBarCompat, any action item attributes come from ActionBarCompat's XML
+ namespace instead of the android namespace. Here we've added a new support namespace added to
+ the menu element allowing us to use the 'showAsAction' attribute in a backwards compatible way.
+ Any other action item attributes used should be referenced from this namespace too
+ (actionProviderClass, actionViewClass, actionLayout).
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:support="http://schemas.android.com/apk/res-auto">
+
+ <item
+ android:id="@+id/menu_remove"
+ android:title="@string/menu_remove"
+ support:showAsAction="ifRoom"/>
+
+</menu>
\ No newline at end of file
diff --git a/ui/actionbarcompat/ListViewModalSelect/src/main/res/values/colors.xml b/ui/actionbarcompat/ListViewModalSelect/src/main/res/values/colors.xml
new file mode 100644
index 0000000..c4e8b3a
--- /dev/null
+++ b/ui/actionbarcompat/ListViewModalSelect/src/main/res/values/colors.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2013 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.
+-->
+<resources>
+
+ <!-- A translucent holo blue -->
+ <color name="translucent_blue">#9033B5E5</color>
+
+</resources>
\ No newline at end of file
diff --git a/ui/actionbarcompat/ListViewModalSelect/src/main/res/values/strings.xml b/ui/actionbarcompat/ListViewModalSelect/src/main/res/values/strings.xml
new file mode 100644
index 0000000..1987fab
--- /dev/null
+++ b/ui/actionbarcompat/ListViewModalSelect/src/main/res/values/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2013 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.
+-->
+<resources>
+
+ <string name="app_name">ABC ListView Modal</string>
+ <string name="intro_message">This sample demonstrates how to provide a
+ <em>MULTIPLE_MODAL</em> ListView choice mode, compatible from API v7.
+ </string>
+ <string name="menu_remove">Remove</string>
+
+</resources>
\ No newline at end of file
diff --git a/ui/actionbarcompat/ListViewModalSelect/src/main/src/com/example/android/actionbarcompat/listviewmodalselect/CheeseListFragment.java b/ui/actionbarcompat/ListViewModalSelect/src/main/src/com/example/android/actionbarcompat/listviewmodalselect/CheeseListFragment.java
new file mode 100644
index 0000000..2ea9638
--- /dev/null
+++ b/ui/actionbarcompat/ListViewModalSelect/src/main/src/com/example/android/actionbarcompat/listviewmodalselect/CheeseListFragment.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2013 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.example.android.actionbarcompat.listviewmodalselect;
+
+import com.example.android.common.actionbarcompat.MultiSelectionUtil;
+import com.example.android.common.dummydata.Cheeses;
+
+import android.os.Bundle;
+import android.support.v4.app.ListFragment;
+import android.support.v7.app.ActionBarActivity;
+import android.support.v7.view.ActionMode;
+import android.util.SparseBooleanArray;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.ArrayAdapter;
+import android.widget.BaseAdapter;
+import android.widget.ListView;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * This ListFragment displays a list of cheeses, allowing the user to select multiple items
+ * in a modal selection mode. In this example, the user can remove multiple items via the displayed
+ * action mode.
+ */
+public class CheeseListFragment extends ListFragment {
+
+ // ArrayList containing the cheese name Strings
+ private ArrayList<String> mItems;
+
+ // The Controller which provides CHOICE_MODE_MULTIPLE_MODAL-like functionality
+ private MultiSelectionUtil.Controller mMultiSelectController;
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ // The items which will be displayed
+ mItems = Cheeses.asList();
+
+ // Set the ListAdapter so that the ListView displays the items
+ setListAdapter(new ArrayAdapter<String>(getActivity(), R.layout.simple_selectable_list_item,
+ android.R.id.text1, mItems));
+
+ // BEGIN_INCLUDE(attach_controller)
+ // Attach a MultiSelectionUtil.Controller to the ListView, giving it an instance of
+ // ModalChoiceListener (see below)
+ mMultiSelectController = MultiSelectionUtil
+ .attachMultiSelectionController(getListView(), (ActionBarActivity) getActivity(),
+ new ModalChoiceListener());
+
+ // Allow the Controller to restore itself
+ mMultiSelectController.restoreInstanceState(savedInstanceState);
+ // END_INCLUDE(attach_controller)
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ // BEGIN_INCLUDE(save_instance)
+ // Allow the Controller to save it's instance state so that any checked items are
+ // stored
+ if (mMultiSelectController != null) {
+ mMultiSelectController.saveInstanceState(outState);
+ }
+ // END_INCLUDE(save_instance)
+ }
+
+ /**
+ * The listener which is provided to MultiSelectionUtil to handle any multi-selection actions.
+ * It is responsible for providing the menu to display on the action mode, and handling any
+ * action item clicks.
+ */
+ private class ModalChoiceListener implements MultiSelectionUtil.MultiChoiceModeListener {
+
+ @Override
+ public void onItemCheckedStateChanged(ActionMode mode, int position, long id,
+ boolean checked) {
+ }
+
+ @Override
+ public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
+ // Inflate the menu resource (res/menu/context_cheeses.xml) which will be displayed
+ // in the action mode
+ actionMode.getMenuInflater().inflate(R.menu.context_cheeses, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
+ return true;
+ }
+
+ @Override
+ public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
+ // We switch on the menu item's id, so we know which is clicked
+ switch (menuItem.getItemId()) {
+
+ // Our item with the menu_remove ID is used to remove the item(s) from the list.
+ // Here we retrieve which positions are currently checked, then iterate through the
+ // list and remove the checked items. Once finished we notify the adapter to update
+ // the ListView contents and finish the action mode.
+ case R.id.menu_remove:
+ // Retrieve which positions are currently checked
+ final ListView listView = getListView();
+ SparseBooleanArray checkedPositions = listView.getCheckedItemPositions();
+
+ // Check to see if there are any checked items
+ if (checkedPositions.size() == 0) {
+ return false;
+ }
+
+ // Iterate through the items and remove any which are checked
+ final Iterator<String> iterator = mItems.iterator();
+ int i = 0;
+ while (iterator.hasNext()) {
+ // Call next() so that the iterator index moves
+ iterator.next();
+
+ // Remove the item if it is checked (and increment position)
+ if (checkedPositions.get(i++)) {
+ iterator.remove();
+ }
+ }
+
+ // Clear the ListView's checked items
+ listView.clearChoices();
+
+ // Finally, notify the adapter so that it updates the ListView
+ ((BaseAdapter) getListAdapter()).notifyDataSetChanged();
+
+ // As we're removing all of checked items, we'll close the action mode
+ actionMode.finish();
+
+ // We return true to signify that we have handled the action item click
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public void onDestroyActionMode(ActionMode actionMode) {
+ }
+ }
+}
\ No newline at end of file
diff --git a/ui/actionbarcompat/ListViewModalSelect/src/main/src/com/example/android/actionbarcompat/listviewmodalselect/MainActivity.java b/ui/actionbarcompat/ListViewModalSelect/src/main/src/com/example/android/actionbarcompat/listviewmodalselect/MainActivity.java
new file mode 100644
index 0000000..72afc56
--- /dev/null
+++ b/ui/actionbarcompat/ListViewModalSelect/src/main/src/com/example/android/actionbarcompat/listviewmodalselect/MainActivity.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2013 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.example.android.actionbarcompat.listviewmodalselect;
+
+import android.os.Bundle;
+import android.support.v7.app.ActionBarActivity;
+
+/**
+ * This sample shows you how a provide a ListView which allows the user to select multiple items
+ * in a modal selection mode with ActionBarCompat, backwards compatible to API v7.
+ * <p>
+ * This Activity extends from {@link ActionBarActivity}, which provides all of the function
+ * necessary to display a compatible Action Bar on devices running Android v2.1+.
+ * <p>
+ * The interesting part of this sample is in {@link CheeseListFragment}.
+ */
+public class MainActivity extends ActionBarActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Set content view (which contains a CheeseListFragment)
+ setContentView(R.layout.activity_main);
+ }
+
+}
\ No newline at end of file
diff --git a/ui/actionbarcompat/SearchView/big_icon.png b/ui/actionbarcompat/SearchView/big_icon.png
new file mode 100644
index 0000000..e4c6ee2
--- /dev/null
+++ b/ui/actionbarcompat/SearchView/big_icon.png
Binary files differ
diff --git a/ui/actionbarcompat/SearchView/build.gradle b/ui/actionbarcompat/SearchView/build.gradle
new file mode 100644
index 0000000..b99b102
--- /dev/null
+++ b/ui/actionbarcompat/SearchView/build.gradle
@@ -0,0 +1,16 @@
+apply plugin: 'android'
+
+dependencies {
+ compile "com.android.support:support-v4:18.0.+"
+ compile "com.android.support:appcompat-v7:18.0.+"
+}
+
+android {
+ compileSdkVersion 17
+ buildToolsVersion "17.0.0"
+
+ defaultConfig {
+ minSdkVersion 7
+ targetSdkVersion 16
+ }
+}
diff --git a/ui/actionbarcompat/SearchView/src/main/AndroidManifest.xml b/ui/actionbarcompat/SearchView/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..b2554d6
--- /dev/null
+++ b/ui/actionbarcompat/SearchView/src/main/AndroidManifest.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2013 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.actionbarcompat.searchview"
+ android:versionCode="1"
+ android:versionName="1.0">
+
+ <!--
+ ActionBarCompat provides an Action Bar with compatible SearchView
+ from API v7 onwards
+ -->
+ <uses-sdk
+ android:minSdkVersion="7"
+ android:targetSdkVersion="17" />
+
+ <application
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme"
+ android:allowBackup="true">
+
+ <activity
+ android:name=".SearchActivity">
+ <!-- Launcher Intent filter -->
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ </application>
+
+</manifest>
diff --git a/ui/actionbarcompat/SearchView/src/main/res/drawable-hdpi/ic_action_search.png b/ui/actionbarcompat/SearchView/src/main/res/drawable-hdpi/ic_action_search.png
new file mode 100644
index 0000000..9839a13
--- /dev/null
+++ b/ui/actionbarcompat/SearchView/src/main/res/drawable-hdpi/ic_action_search.png
Binary files differ
diff --git a/ui/actionbarcompat/SearchView/src/main/res/drawable-hdpi/ic_launcher.png b/ui/actionbarcompat/SearchView/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..45afb7d
--- /dev/null
+++ b/ui/actionbarcompat/SearchView/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/ui/actionbarcompat/SearchView/src/main/res/drawable-mdpi/ic_action_search.png b/ui/actionbarcompat/SearchView/src/main/res/drawable-mdpi/ic_action_search.png
new file mode 100644
index 0000000..076085c
--- /dev/null
+++ b/ui/actionbarcompat/SearchView/src/main/res/drawable-mdpi/ic_action_search.png
Binary files differ
diff --git a/ui/actionbarcompat/SearchView/src/main/res/drawable-mdpi/ic_launcher.png b/ui/actionbarcompat/SearchView/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..57f2527
--- /dev/null
+++ b/ui/actionbarcompat/SearchView/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/ui/actionbarcompat/SearchView/src/main/res/drawable-xhdpi/ic_action_search.png b/ui/actionbarcompat/SearchView/src/main/res/drawable-xhdpi/ic_action_search.png
new file mode 100644
index 0000000..f9272df
--- /dev/null
+++ b/ui/actionbarcompat/SearchView/src/main/res/drawable-xhdpi/ic_action_search.png
Binary files differ
diff --git a/ui/actionbarcompat/SearchView/src/main/res/drawable-xhdpi/ic_launcher.png b/ui/actionbarcompat/SearchView/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..478f739
--- /dev/null
+++ b/ui/actionbarcompat/SearchView/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/ui/actionbarcompat/SearchView/src/main/res/drawable-xxhdpi/ic_launcher.png b/ui/actionbarcompat/SearchView/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..2e14315
--- /dev/null
+++ b/ui/actionbarcompat/SearchView/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/ui/actionbarcompat/SearchView/src/main/res/layout/activity_main.xml b/ui/actionbarcompat/SearchView/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..9158690
--- /dev/null
+++ b/ui/actionbarcompat/SearchView/src/main/res/layout/activity_main.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2013 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.
+-->
+<fragment xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/frag_app_list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:name="com.example.android.actionbarcompat.searchview.AppListFragment" />
diff --git a/ui/actionbarcompat/SearchView/src/main/res/layout/list_item.xml b/ui/actionbarcompat/SearchView/src/main/res/layout/list_item.xml
new file mode 100644
index 0000000..893796c
--- /dev/null
+++ b/ui/actionbarcompat/SearchView/src/main/res/layout/list_item.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2013 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="?android:listPreferredItemHeight"
+ android:padding="8dp"
+ android:gravity="center_vertical">
+
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_marginRight="8dp"
+ android:scaleType="fitCenter"
+ android:contentDescription="@string/content_item_icon"/>
+
+ <TextView
+ android:id="@android:id/text1"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:textAppearanceMedium"
+ android:maxLines="1"
+ android:ellipsize="end"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/ui/actionbarcompat/SearchView/src/main/res/menu/main.xml b/ui/actionbarcompat/SearchView/src/main/res/menu/main.xml
new file mode 100644
index 0000000..1709e8b
--- /dev/null
+++ b/ui/actionbarcompat/SearchView/src/main/res/menu/main.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2013 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.
+-->
+
+<!--
+ As we're using ActionBarCompat, any action item attributes come from ActionBarCompat's XML
+ namespace instead of the android namespace. Here we've added a new support namespace added to
+ the menu element allowing us to use the 'showAsAction' attribute in a backwards compatible way.
+ Any other action item attributes used should be referenced from this namespace too
+ (actionProviderClass, actionViewClass, actionLayout).
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:support="http://schemas.android.com/apk/res-auto">
+
+ <!--
+ Here we create an item, setting the actionViewClass attribute to point to ActionBarCompat's
+ SearchView. We add the collapseActionView flag to showAsAction so that the SearchView is
+ collapsed until the user clicks on the action item.
+ -->
+ <item
+ android:id="@+id/menu_search"
+ android:title="@string/menu_search"
+ android:icon="@drawable/ic_action_search"
+ support:actionViewClass="android.support.v7.widget.SearchView"
+ support:showAsAction="ifRoom|collapseActionView"/>
+
+</menu>
\ No newline at end of file
diff --git a/ui/actionbarcompat/SearchView/src/main/res/values/strings.xml b/ui/actionbarcompat/SearchView/src/main/res/values/strings.xml
new file mode 100644
index 0000000..4a5ab36
--- /dev/null
+++ b/ui/actionbarcompat/SearchView/src/main/res/values/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2013 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.
+-->
+<resources>
+
+ <string name="app_name">ABC Search Demo</string>
+ <string name="applist_empty_text">Search for an app above.</string>
+ <string name="search_hint">Search applications</string>
+ <string name="menu_search">Search</string>
+ <string name="content_item_icon">Item Icon</string>
+
+</resources>
\ No newline at end of file
diff --git a/ui/actionbarcompat/SearchView/src/main/res/values/styles.xml b/ui/actionbarcompat/SearchView/src/main/res/values/styles.xml
new file mode 100644
index 0000000..d9a47e3
--- /dev/null
+++ b/ui/actionbarcompat/SearchView/src/main/res/values/styles.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2013 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.
+-->
+<resources>
+
+ <!-- Application theme. -->
+ <style name="AppTheme" parent="Theme.AppCompat"></style>
+
+</resources>
\ No newline at end of file
diff --git a/ui/actionbarcompat/SearchView/src/main/src/com/example/android/actionbarcompat/searchview/AppListFragment.java b/ui/actionbarcompat/SearchView/src/main/src/com/example/android/actionbarcompat/searchview/AppListFragment.java
new file mode 100644
index 0000000..981eb37
--- /dev/null
+++ b/ui/actionbarcompat/SearchView/src/main/src/com/example/android/actionbarcompat/searchview/AppListFragment.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2013 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.example.android.actionbarcompat.searchview;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.support.v4.app.ListFragment;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+
+/**
+ * A ListFragment which displays a list of applications currently on the device, with
+ * filtering functionality.
+ */
+public class AppListFragment extends ListFragment {
+
+ // Stores the full list of applications installed on the device
+ private final List<ApplicationItem> mInstalledApps = new ArrayList<ApplicationItem>();
+
+ // Stores the result of the last query
+ private final List<ApplicationItem> mFilteredApps = new ArrayList<ApplicationItem>();
+
+ private PackageManager mPackageManager;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Retrieve all of the applications installed on this device
+ mPackageManager = getActivity().getPackageManager();
+
+ // Start an AsyncTask to load the application's installed on the device
+ new LoadApplicationsTask().execute();
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+
+ // Set the ListView's Adapter to display the filtered applications
+ setListAdapter(new AppAdapter());
+
+ // Set the text which displays when the ListView is empty
+ setEmptyText(getString(R.string.applist_empty_text));
+
+ // If the installed applications is not loaded yet, hide the list
+ if (mInstalledApps.isEmpty()) {
+ setListShown(false);
+ }
+ }
+
+ /**
+ * Set the query used to filter the installed application's names.
+ * @param query Query to filter against.
+ */
+ public void setFilterQuery(String query) {
+ // Fail-fast if the installed apps has not been loaded yet
+ if (mInstalledApps.isEmpty()) {
+ return;
+ }
+
+ // Clear current filtered apps and hide ListView
+ mFilteredApps.clear();
+
+ if (TextUtils.isEmpty(query)) {
+ // If the query is empty, show all install apps
+ mFilteredApps.addAll(mInstalledApps);
+ } else {
+ // Compile Regex Pattern which will match if the app name contains the query
+ final Pattern p = Pattern.compile(".*" + query + ".*", Pattern.CASE_INSENSITIVE);
+
+ // Iterate through the installed apps to see if their label matches the query
+ for (ApplicationItem application : mInstalledApps) {
+ // If the app label matches the query, add it to the filtered apps list
+ if (p.matcher(application.label).matches()) {
+ mFilteredApps.add(application);
+ }
+ }
+ }
+
+ // Notify the adapter so that it updates the ListView's contents
+ AppAdapter adapter = (AppAdapter) getListAdapter();
+ adapter.notifyDataSetChanged();
+ }
+
+ private class AppAdapter extends BaseAdapter {
+ private final LayoutInflater mInflater;
+
+ AppAdapter() {
+ mInflater = LayoutInflater.from(getActivity());
+ }
+
+ @Override
+ public int getCount() {
+ return mFilteredApps.size();
+ }
+
+ @Override
+ public ApplicationItem getItem(int position) {
+ return mFilteredApps.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (convertView == null) {
+ convertView = mInflater.inflate(R.layout.list_item, parent, false);
+ }
+
+ final ApplicationItem item = getItem(position);
+
+ // Set the application's label on the TextView
+ ((TextView) convertView.findViewById(android.R.id.text1))
+ .setText(item.label);
+
+ // ImageView to display the application's icon
+ ImageView imageView = (ImageView) convertView.findViewById(R.id.icon);
+
+ // Check the item's drawable reference to see if it is available already
+ Drawable icon = item.drawable != null ? item.drawable.get() : null;
+
+ if (icon != null) {
+ imageView.setImageDrawable(icon);
+ } else {
+ // Start a new AsyncTask which will retrieve the application icon and display it in
+ // the ImageView
+ new ApplicationIconTask(imageView).execute(item);
+ }
+
+ return convertView;
+ }
+ }
+
+ /**
+ * Our model object for each application item. Allows us to load the label async and store the
+ * result for use later (filtering and displaying in the adapter).
+ */
+ private static class ApplicationItem {
+ final ApplicationInfo applicationInfo;
+ final CharSequence label;
+ WeakReference<Drawable> drawable;
+
+ ApplicationItem(PackageManager packageManager, ApplicationInfo applicationInfo) {
+ this.applicationInfo = applicationInfo;
+
+ // Load and store the app's label using the PackageManager
+ this.label = applicationInfo.loadLabel(packageManager);
+ }
+ }
+
+ /**
+ * Simple AsyncTask which retrieves the installed applications from the {@link PackageManager}
+ * and stores them locally.
+ */
+ private class LoadApplicationsTask extends AsyncTask<Void, Void, List<ApplicationItem>> {
+
+ @Override
+ protected List<ApplicationItem> doInBackground(Void... voids) {
+ // Load all installed applications on the device
+ List<ApplicationInfo> apps = mPackageManager.getInstalledApplications(0);
+ // Create an ArrayList used to store the result
+ ArrayList<ApplicationItem> loadedApps = new ArrayList<ApplicationItem>();
+
+ // Iterate through the results and create an ApplicationItem instance for each one,
+ // adding them to the returned List.
+ for (ApplicationInfo info : apps) {
+ loadedApps.add(new ApplicationItem(mPackageManager, info));
+ }
+
+ return loadedApps;
+ }
+
+ @Override
+ protected void onPostExecute(List<ApplicationItem> loadedApps) {
+ super.onPostExecute(loadedApps);
+
+ // Add the results to the install apps list
+ mInstalledApps.addAll(loadedApps);
+
+ // Reset the query to update the ListView
+ setFilterQuery(null);
+
+ // Make sure the list is shown
+ setListShown(true);
+ }
+ }
+
+ /**
+ * Simple AsyncTask which loads the given application's logo from the {@link PackageManager} and
+ * displays it in the given {@link ImageView}
+ */
+ private class ApplicationIconTask extends AsyncTask<ApplicationItem, Void, Drawable> {
+ private final ImageView mImageView;
+
+ ApplicationIconTask(ImageView imageView) {
+ mImageView = imageView;
+ }
+
+ @Override
+ protected Drawable doInBackground(ApplicationItem... params) {
+ Drawable icon = null;
+
+ // Check to see that we've been provided one item
+ if (params.length == 1) {
+ ApplicationItem item = params[0];
+
+ // Load the icon from the PackageManager
+ icon = item.applicationInfo.loadIcon(mPackageManager);
+
+ // Store the icon in a WeakReference so we do not cause a OutOfMemoryError.
+ item.drawable = new WeakReference<Drawable>(icon);
+ }
+
+ return icon;
+ }
+
+ @Override
+ protected void onPostExecute(Drawable icon) {
+ super.onPostExecute(icon);
+ // Display the icon in the ImageView
+ mImageView.setImageDrawable(icon);
+ }
+ }
+
+}
diff --git a/ui/actionbarcompat/SearchView/src/main/src/com/example/android/actionbarcompat/searchview/SearchActivity.java b/ui/actionbarcompat/SearchView/src/main/src/com/example/android/actionbarcompat/searchview/SearchActivity.java
new file mode 100644
index 0000000..c0cbdda
--- /dev/null
+++ b/ui/actionbarcompat/SearchView/src/main/src/com/example/android/actionbarcompat/searchview/SearchActivity.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2013 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.example.android.actionbarcompat.searchview;
+
+import android.os.Bundle;
+import android.support.v4.view.MenuItemCompat;
+import android.support.v7.app.ActionBarActivity;
+import android.support.v7.widget.SearchView;
+import android.view.Menu;
+import android.view.MenuItem;
+
+/**
+ * This sample shows you how to use {@link SearchView} to provide a search function in a single
+ * Activity, when utilizing ActionBarCompat.
+ *
+ * This Activity extends from {@link ActionBarActivity}, which provides all of the function
+ * necessary to display a compatible Action Bar on devices running Android v2.1+.
+ */
+public class SearchActivity extends ActionBarActivity {
+
+ private AppListFragment mAppListFragment;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ // Retrieve the AppListFragment from the layout
+ mAppListFragment = (AppListFragment) getSupportFragmentManager()
+ .findFragmentById(R.id.frag_app_list);
+ }
+
+ // BEGIN_INCLUDE(create_menu)
+ /**
+ * Use this method to instantiate your menu, inflating our {@code main} menu resource.
+ */
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate our menu from the resources by using the menu inflater.
+ getMenuInflater().inflate(R.menu.main, menu);
+
+ return super.onCreateOptionsMenu(menu);
+ }
+ // END_INCLUDE(create_menu)
+
+
+ /**
+ * This is called just before the menu is about to be displayed. You should
+ * use this method to modify your menu or its items. In this example, we
+ * retrieve the SearchView.
+ */
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ // BEGIN_INCLUDE(retrieve_view)
+ // First we retrieve the searchable menu item
+ MenuItem searchItem = menu.findItem(R.id.menu_search);
+
+ // Now we get the SearchView from the item
+ final SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
+ // END_INCLUDE(retrieve_view)
+
+ // Set the hint text which displays when the SearchView is empty
+ searchView.setQueryHint(getString(R.string.search_hint));
+
+ // BEGIN_INCLUDE(action_expand_listener)
+ // We now set OnActionExpandListener on the menu item so we can reset SearchView's query
+ // when it is collapsed.
+ MenuItemCompat.setOnActionExpandListener(searchItem,
+ new MenuItemCompat.OnActionExpandListener() {
+ @Override
+ public boolean onMenuItemActionExpand(MenuItem menuItem) {
+ // Return true to allow the action view to expand
+ return true;
+ }
+
+ @Override
+ public boolean onMenuItemActionCollapse(MenuItem menuItem) {
+ // When the action view is collapsed, reset the query
+ searchView.setQuery(null, true);
+
+ // Return true to allow the action view to collapse
+ return true;
+ }
+ });
+ // END_INCLUDE(action_expand_listener)
+
+ // BEGIN_INCLUDE(query_text_listener)
+ // We now set a OnQueryTextListener so we are notified when the query has changed
+ searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+ @Override
+ public boolean onQueryTextSubmit(String s) {
+ // Propagate query to our AppListFragment
+ mAppListFragment.setFilterQuery(s);
+
+ // Return true to signify that we have handled the query submit
+ return true;
+ }
+
+ @Override
+ public boolean onQueryTextChange(String s) {
+ // Propagate query to our AppListFragment
+ mAppListFragment.setFilterQuery(s);
+
+ // Return true to signify that we have handled the query change
+ return true;
+ }
+ });
+ // END_INCLUDE(query_text_listener)
+
+ return super.onPrepareOptionsMenu(menu);
+ }
+}