├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src ├── androidTest ├── AndroidManifest.xml ├── java │ └── com │ │ └── onehilltech │ │ └── metadata │ │ ├── ManifestMetadataTest.java │ │ ├── MetadataValues.java │ │ ├── TestClass.java │ │ └── test │ │ └── TestActivity.java └── res │ └── values │ ├── strings.xml │ └── values.xml └── main ├── AndroidManifest.xml └── java └── com └── onehilltech └── metadata ├── ManifestMetadata.java ├── MetadataMethod.java ├── MetadataProperty.java └── ResourceType.java /.gitignore: -------------------------------------------------------------------------------- 1 | ### Gradle template 2 | .gradle 3 | build/ 4 | 5 | # Ignore Gradle GUI config 6 | gradle-app.setting 7 | 8 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 9 | !gradle-wrapper.jar 10 | ### JetBrains template 11 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio 12 | 13 | *.iml 14 | 15 | ## Directory-based project format: 16 | .idea/ 17 | # if you remove the above rule, at least ignore the following: 18 | 19 | # User-specific stuff: 20 | # .idea/workspace.xml 21 | # .idea/tasks.xml 22 | # .idea/dictionaries 23 | 24 | # Sensitive or high-churn files: 25 | # .idea/dataSources.ids 26 | # .idea/dataSources.xml 27 | # .idea/sqlDataSources.xml 28 | # .idea/dynamic.xml 29 | # .idea/uiDesigner.xml 30 | 31 | # Gradle: 32 | # .idea/gradle.xml 33 | # .idea/libraries 34 | 35 | # Mongo Explorer plugin: 36 | # .idea/mongoSettings.xml 37 | 38 | ## File-based project format: 39 | *.ipr 40 | *.iws 41 | 42 | ## Plugin-specific files: 43 | 44 | # IntelliJ 45 | /out/ 46 | 47 | # mpeltonen/sbt-idea plugin 48 | .idea_modules/ 49 | 50 | # JIRA plugin 51 | atlassian-ide-plugin.xml 52 | 53 | # Crashlytics plugin (for Android Studio and IntelliJ) 54 | com_crashlytics_export_strings.xml 55 | crashlytics.properties 56 | crashlytics-build.properties 57 | ### Android template 58 | # Built application files 59 | *.apk 60 | *.ap_ 61 | 62 | # Files for the Dalvik VM 63 | *.dex 64 | 65 | # Java class files 66 | *.class 67 | 68 | # Generated files 69 | bin/ 70 | gen/ 71 | 72 | # Gradle files 73 | .gradle/ 74 | build/ 75 | 76 | # Local configuration file (sdk path, etc) 77 | local.properties 78 | bintray.properties 79 | 80 | # Proguard folder generated by Eclipse 81 | proguard/ 82 | 83 | # Log Files 84 | *.log 85 | 86 | # Android Studio Navigation editor temp files 87 | .navigation/ 88 | ### Java template 89 | *.class 90 | 91 | # Mobile Tools for Java (J2ME) 92 | .mtj.tmp/ 93 | 94 | # Package Files # 95 | *.jar 96 | *.war 97 | *.ear 98 | 99 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 100 | hs_err_pid* 101 | 102 | # Eclipse Files 103 | .classpath 104 | .project 105 | .settings/ 106 | RemoteSystemsTempFiles/ 107 | .metadata/ 108 | 109 | # Maven Files 110 | target/ 111 | 112 | */gen/ 113 | */bin/ 114 | 115 | *.class 116 | ### OSX template 117 | .DS_Store 118 | .AppleDouble 119 | .LSOverride 120 | 121 | # Icon must end with two \r 122 | Icon 123 | 124 | # Thumbnails 125 | ._* 126 | 127 | # Files that might appear in the root of a volume 128 | .DocumentRevisions-V100 129 | .fseventsd 130 | .Spotlight-V100 131 | .TemporaryItems 132 | .Trashes 133 | .VolumeIcon.icns 134 | 135 | # Directories potentially created on remote AFP share 136 | .AppleDB 137 | .AppleDesktop 138 | Network Trash Folder 139 | Temporary Items 140 | .apdisk 141 | 142 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: android 2 | sudo: false 3 | 4 | env: 5 | global: 6 | - ANDROID_BUILD_TOOLS_VERSION=23.0.3 7 | - ADB_INSTALL_TIMEOUT=10 8 | 9 | matrix: 10 | - ANDROID_TARGET=android-23 ANDROID_ABI=armeabi-v7a 11 | 12 | android: 13 | components: 14 | # Use the latest revision of Android SDK Tools 15 | - platform-tools 16 | - tools 17 | 18 | # The BuildTools version used by your project 19 | - build-tools-$ANDROID_BUILD_TOOLS_VERSION 20 | 21 | # The SDK version used to compile your project 22 | - $ANDROID_TARGET 23 | 24 | # Additional components 25 | - extra-google-m2repository 26 | - extra-android-m2repository 27 | 28 | # Specify the system image to run emulator during tests 29 | - sys-img-$ANDROID_ABI-$ANDROID_TARGET 30 | 31 | before_script: 32 | # Create and start emulator 33 | - chmod +x gradlew 34 | - pip install --user codecov 35 | - echo no | android create avd --force -n test -t $ANDROID_TARGET --abi $ANDROID_ABI 36 | - emulator -avd test -no-skin -no-audio -no-window & 37 | - android-wait-for-emulator 38 | - adb shell input keyevent 82 & 39 | 40 | script: 41 | - ./gradlew build connectedCheck -PdisablePreDex 42 | 43 | after_success: 44 | - codecov -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 One Hill Technologies, LLC 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | android-metadata 2 | ================== 3 | 4 | [![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-android--metadata-green.svg?style=true)](https://android-arsenal.com/details/1/4045) 5 | [![Download](https://jitpack.io/v/onehilltech/android-metadata.svg)](https://jitpack.io/#onehilltech/android-metadata) 6 | [![Build Status](https://travis-ci.org/onehilltech/android-metadata.svg)](https://travis-ci.org/onehilltech/android-metadata) 7 | [![codecov.io](http://codecov.io/github/onehilltech/android-metadata/coverage.svg?branch=master)](http://codecov.io/github/onehilltech/android-metadata?branch=master) 8 | 9 | A utility library for Android designed to simplify reading meta-data 10 | values from AndroidManifest.xml. 11 | 12 | * **Quickly** access a meta-data values from anywhere with few lines of code. 13 | * Read individual meta-data values into **type-specific** variables. 14 | * **Instantiate** objects from meta-data values. 15 | * Read one or more meta-data values into **annotated** Java classes. 16 | * Use meta-data values to pass configuration parameters to **third-party libraries**. 17 | 18 | ## Installation 19 | 20 | #### Gradle 21 | 22 | ``` 23 | buildscript { 24 | repositories { 25 | maven { url "https://jitpack.io" } 26 | } 27 | } 28 | 29 | dependencies { 30 | compile com.github.onehilltech:android-metadata:x.y.z 31 | } 32 | ``` 33 | 34 | ## Getting Started 35 | 36 | Here is the quickest and easiest way to load the metadata from AndroidManifest.xml 37 | and get a value. The value, by default, is a String value type. 38 | 39 | ```java 40 | ManifestMetadata metadata = ManifestMetadata.get (context); 41 | 42 | // 43 | String value = metadata.getValue ("appid"); 44 | ``` 45 | 46 | If the value is not a String type, then you can provide a hint: 47 | 48 | ```java 49 | ManifestMetadata metadata = ManifestMetadata.get (context); 50 | 51 | // 52 | Integer connTimeout = metadata.getValue ("conn.timeout", Integer.class); 53 | ``` 54 | 55 | You can even directly load a resource from the metadata: 56 | 57 | ```java 58 | ManifestMetadata metadata = ManifestMetadata.get (context); 59 | 60 | // 61 | String appName = metadata.getValue ("appname", true, String.class); 62 | ``` 63 | 64 | In some cases, you may need to provide additional information about 65 | the resource type since different resources types can have the same 66 | Java type: 67 | 68 | ```java 69 | ManifestMetadata metadata = ManifestMetadata.get (context); 70 | 71 | // 72 | Integer bgColor = metadata.getValue ("bgcolor", true, Integer.class, ResourceType.Color); 73 | ``` 74 | 75 | ## Using Annotations to Load Metadata 76 | 77 | Here is the simplest example of using an annotation to define what 78 | meta-data value in AndroidManifest.xml it should be initialized with: 79 | 80 | ```java 81 | public class MyData { 82 | private String appid_; 83 | 84 | @MetadataProperty (name="my.message") 85 | public String message; 86 | 87 | @MetadataMethod (name="appid") 88 | public void setAppId (String appid) { 89 | this.appid_ = appid; 90 | } 91 | } 92 | ``` 93 | 94 | In the example above, the field **message** will be initialized with 95 | the value of meta-data tag named **my.message**. You initialize all 96 | values with the **@Metadata** annotation using a single line of code: 97 | 98 | ```java 99 | MyData myData = new MyData (); 100 | ManifestMetadata.get (context).initFromMetadata (myData); 101 | ``` 102 | 103 | This method will auto-detect the target type, and then assign the value. 104 | If the field is not assignable using the meta-data's value, then an 105 | exception will be thrown. 106 | 107 | ### Reading from a Resource 108 | 109 | In some cases, you will want to read the value from a resource (i.e., 110 | you use android:resource in the meta-data tag). You can use the **@Metadata** 111 | annotation to read resource values as well: 112 | 113 | ```java 114 | public class MyData { 115 | private String appid_; 116 | 117 | @MetadataProperty (name="my.message", fromResource=true) 118 | public String message; 119 | 120 | @MetadataMethod (name="appid", fromResource=true) 121 | public void setAppId (String appid) { 122 | this.appid_ = appid; 123 | } 124 | } 125 | ``` 126 | 127 | ### Giving Resource Type Hints 128 | 129 | There are some resources that have the same field type, such as integer 130 | and color. This makes it hard to auto-detect the resources type. We can 131 | therefore provide a hint as follows: 132 | 133 | ```java 134 | public class MyData { 135 | @MetadataProperty (name="my.message", fromResource=true, resourceType=ResourceType.Color) 136 | public int backgroundColor; 137 | } 138 | ``` 139 | 140 | In the example above, the value for **backgroundColor** will be loaded 141 | from resources and interpreted as a color. 142 | 143 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | jcenter() 5 | } 6 | 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:3.2.1' 9 | } 10 | } 11 | 12 | allprojects { 13 | repositories { 14 | jcenter() 15 | maven { url "https://maven.google.com" } 16 | } 17 | } 18 | 19 | apply plugin: 'com.android.library' 20 | 21 | group = "com.onehilltech.android" 22 | version = "1.6.0" 23 | 24 | android { 25 | compileSdkVersion 28 26 | buildToolsVersion "28.0.3" 27 | 28 | defaultConfig { 29 | minSdkVersion 14 30 | targetSdkVersion 26 31 | versionCode 9 32 | versionName "1.5.2" 33 | 34 | testApplicationId 'com.onehilltech.metadata.test' 35 | testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' 36 | } 37 | 38 | buildTypes { 39 | debug { 40 | testCoverageEnabled true 41 | } 42 | 43 | release { 44 | minifyEnabled false 45 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 46 | } 47 | } 48 | 49 | compileOptions { 50 | sourceCompatibility JavaVersion.VERSION_1_7 51 | } 52 | 53 | lintOptions { 54 | abortOnError false 55 | } 56 | } 57 | 58 | dependencies { 59 | androidTestImplementation 'androidx.test.ext:junit:1.0.0' 60 | androidTestImplementation 'androidx.test:runner:1.1.0' 61 | } 62 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | name=android-metadata 2 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onehilltech/android-metadata/d53a1acac4e33337764f9997f6d8aa4139ca4369/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Mar 17 00:05:30 EDT 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onehilltech/android-metadata/d53a1acac4e33337764f9997f6d8aa4139ca4369/settings.gradle -------------------------------------------------------------------------------- /src/androidTest/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 20 | 21 | 22 | 23 | 24 | 26 | 27 | 28 | 29 | 30 | 33 | 34 | 37 | 38 | 41 | 42 | 43 | 46 | 47 | 50 | 51 | 54 | 55 | 58 | 59 | 62 | 63 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /src/androidTest/java/com/onehilltech/metadata/ManifestMetadataTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2013, James H. Hill 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.onehilltech.metadata; 18 | 19 | import android.content.ComponentName; 20 | import android.content.Context; 21 | import android.content.pm.PackageManager.NameNotFoundException; 22 | import android.content.res.Resources; 23 | import android.os.Bundle; 24 | 25 | import com.onehilltech.metadata.test.TestActivity; 26 | 27 | import junit.framework.Assert; 28 | 29 | import org.junit.Test; 30 | import org.junit.runner.RunWith; 31 | 32 | import androidx.test.ext.junit.runners.AndroidJUnit4; 33 | import androidx.test.platform.app.InstrumentationRegistry; 34 | 35 | @RunWith (AndroidJUnit4.class) 36 | public class ManifestMetadataTest 37 | { 38 | public static final String METADATA_STRING = "metadata.string"; 39 | public static final String METADATA_INTEGER = "metadata.integer"; 40 | public static final String METADATA_CLASSNAME = "metadata.classname"; 41 | 42 | public static final String METADATA_RESOURCE_STRING = "metadata.resource.string"; 43 | public static final String METADATA_RESOURCE_INTEGER = "metadata.resource.integer"; 44 | 45 | public static final String METADATA_RESOURCE_BOOLEAN_TRUE = "metadata.resource.boolean.true"; 46 | public static final String METADATA_RESOURCE_BOOLEAN_FALSE = "metadata.resource.boolean.false"; 47 | public static final String METADATA_RESOURCE_DIMENSION = "metadata.resource.dimension"; 48 | public static final String METADATA_RESOURCE_COLOR = "metadata.resource.color"; 49 | 50 | @Test 51 | public void testGetMetadata () 52 | { 53 | try 54 | { 55 | Bundle metadata = ManifestMetadata.get (InstrumentationRegistry.getInstrumentation ().getContext ()).getMetadata (); 56 | 57 | Assert.assertEquals (true, metadata.containsKey (METADATA_STRING)); 58 | Assert.assertEquals (true, metadata.containsKey (METADATA_INTEGER)); 59 | Assert.assertEquals (true, metadata.containsKey (METADATA_CLASSNAME)); 60 | 61 | Assert.assertEquals (true, metadata.containsKey (METADATA_RESOURCE_STRING)); 62 | Assert.assertEquals (true, metadata.containsKey (METADATA_RESOURCE_INTEGER)); 63 | 64 | Assert.assertEquals (true, metadata.containsKey (METADATA_RESOURCE_BOOLEAN_TRUE)); 65 | Assert.assertEquals (true, metadata.containsKey (METADATA_RESOURCE_BOOLEAN_FALSE)); 66 | 67 | Assert.assertEquals (true, metadata.containsKey (METADATA_RESOURCE_DIMENSION)); 68 | 69 | Assert.assertEquals (true, metadata.containsKey (METADATA_RESOURCE_COLOR)); 70 | } 71 | catch (NameNotFoundException e) 72 | { 73 | Assert.fail (e.getMessage ()); 74 | } 75 | } 76 | 77 | @Test 78 | public void testGetMetadataValue () 79 | { 80 | try 81 | { 82 | ManifestMetadata metadata = ManifestMetadata.get (InstrumentationRegistry.getInstrumentation ().getContext ()); 83 | String value = metadata.getValue (METADATA_STRING); 84 | Assert.assertEquals ("Hello, World!", value); 85 | 86 | int intValue = metadata.getValue (METADATA_INTEGER, Integer.class); 87 | Assert.assertEquals (42, intValue); 88 | } 89 | catch (Exception e) 90 | { 91 | Assert.fail (e.getMessage ()); 92 | } 93 | } 94 | 95 | @Test 96 | public void testLoadFromManifest () 97 | { 98 | try 99 | { 100 | // Test @Metadata 101 | 102 | MetadataValues values = new MetadataValues (); 103 | ManifestMetadata.get (InstrumentationRegistry.getInstrumentation ().getContext ()).initFromMetadata (values); 104 | Resources r = InstrumentationRegistry.getInstrumentation ().getContext ().getResources (); 105 | 106 | Assert.assertEquals ("Hello, World!", values.theString); 107 | Assert.assertEquals (42, values.theInteger); 108 | Assert.assertEquals (TestClass.class, values.theClass); 109 | 110 | Assert.assertEquals ("Hello, World!", values.theStringResource); 111 | Assert.assertEquals (com.onehilltech.metadata.test.R.string.hello_world, values.theIntegerResource); 112 | 113 | Assert.assertTrue (values.theTrueValue); 114 | Assert.assertFalse (values.theFalseValue); 115 | 116 | Assert.assertEquals (r.getDimension (com.onehilltech.metadata.test.R.dimen.sample_dimen), values.theDimension); 117 | 118 | // Test @MetadataMethod 119 | Assert.assertEquals ("Hello, World!", values.getMetadataString ()); 120 | } 121 | catch (Exception e) 122 | { 123 | e.printStackTrace (); 124 | Assert.fail (e.getMessage ()); 125 | } 126 | } 127 | 128 | @Test 129 | public void testLoadFromManifestWithResourceType () 130 | { 131 | try 132 | { 133 | MetadataValues values = new MetadataValues (); 134 | ManifestMetadata.get (InstrumentationRegistry.getInstrumentation ().getContext ()).initFromMetadata (values); 135 | Resources r =InstrumentationRegistry.getInstrumentation ().getContext ().getResources (); 136 | 137 | // Testing the resourceType method. 138 | Assert.assertEquals (r.getColor (com.onehilltech.metadata.test.R.color.black), values.colorBlack); 139 | } 140 | catch (Exception e) 141 | { 142 | e.printStackTrace (); 143 | Assert.fail (e.getMessage ()); 144 | } 145 | } 146 | 147 | @Test(expected=NameNotFoundException.class) 148 | public void testNameNotFoundException () throws Exception 149 | { 150 | ManifestMetadata metadata = ManifestMetadata.get (InstrumentationRegistry.getInstrumentation ().getContext ()); 151 | metadata.getValue ("foo", String.class); 152 | } 153 | 154 | @Test 155 | public void testActivityMetadata () throws Exception 156 | { 157 | Context context = InstrumentationRegistry.getInstrumentation ().getContext (); 158 | ComponentName componentName = new ComponentName (context, TestActivity.class); 159 | ManifestMetadata metadata = ManifestMetadata.get (context, componentName); 160 | 161 | Assert.assertEquals (1, metadata.getMetadata ().size ()); 162 | Assert.assertEquals ("Hello, World", metadata.getValue ("metadata.activity.string")); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/androidTest/java/com/onehilltech/metadata/MetadataValues.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2013, James H. Hill 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.onehilltech.metadata; 18 | 19 | import android.content.res.XmlResourceParser; 20 | 21 | public class MetadataValues 22 | { 23 | //// Attribute Testers 24 | 25 | // android:value 26 | @MetadataProperty (name="metadata.string") 27 | public String theString; 28 | 29 | @MetadataProperty(name="metadata.integer") 30 | public int theInteger; 31 | 32 | @MetadataProperty(name="metadata.classname") 33 | public Class theClass; 34 | 35 | // android:resource 36 | @MetadataProperty(name="metadata.resource.string", fromResource=true) 37 | public String theStringResource; 38 | 39 | @MetadataProperty(name="metadata.resource.integer", fromResource=true) 40 | public int theIntegerResource; 41 | 42 | @MetadataProperty(name="metadata.resource.animation", fromResource=true) 43 | public XmlResourceParser theAnimation; 44 | 45 | @MetadataProperty(name="metadata.resource.boolean.true", fromResource=true) 46 | public boolean theTrueValue; 47 | 48 | @MetadataProperty(name="metadata.resource.boolean.false", fromResource=true) 49 | public Boolean theFalseValue; 50 | 51 | @MetadataProperty(name="metadata.resource.dimension", fromResource=true) 52 | public float theDimension; 53 | 54 | @MetadataProperty(name="metadata.resource.color", fromResource=true, resourceType= ResourceType.Color) 55 | public int colorBlack; 56 | 57 | //// Method Testers 58 | 59 | private String metadataString_; 60 | 61 | @MetadataMethod (name="metadata.string") 62 | public void setMetadataString (String str) 63 | { 64 | this.metadataString_ = str; 65 | } 66 | 67 | public String getMetadataString () 68 | { 69 | return this.metadataString_; 70 | } 71 | } -------------------------------------------------------------------------------- /src/androidTest/java/com/onehilltech/metadata/TestClass.java: -------------------------------------------------------------------------------- 1 | /** 2 | /** 3 | * Copyright (C) 2013, James H. Hill 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.onehilltech.metadata; 19 | 20 | public class TestClass 21 | { 22 | 23 | } -------------------------------------------------------------------------------- /src/androidTest/java/com/onehilltech/metadata/test/TestActivity.java: -------------------------------------------------------------------------------- 1 | package com.onehilltech.metadata.test; 2 | 3 | import android.app.Activity; 4 | 5 | public class TestActivity extends Activity 6 | { 7 | } 8 | -------------------------------------------------------------------------------- /src/androidTest/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hello, World! 4 | 5 | -------------------------------------------------------------------------------- /src/androidTest/res/values/values.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | false 5 | 6 | 5sp 7 | 8 | #000000 9 | -------------------------------------------------------------------------------- /src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 20 | 21 | -------------------------------------------------------------------------------- /src/main/java/com/onehilltech/metadata/ManifestMetadata.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2013, James H. Hill 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.onehilltech.metadata; 18 | 19 | import android.app.Activity; 20 | import android.content.ComponentName; 21 | import android.content.Context; 22 | import android.content.pm.ActivityInfo; 23 | import android.content.pm.ApplicationInfo; 24 | import android.content.pm.PackageManager; 25 | import android.content.pm.PackageManager.NameNotFoundException; 26 | import android.content.res.Resources; 27 | import android.content.res.XmlResourceParser; 28 | import android.os.Bundle; 29 | import android.util.Log; 30 | 31 | import java.lang.ref.WeakReference; 32 | import java.lang.reflect.Field; 33 | import java.lang.reflect.InvocationTargetException; 34 | import java.lang.reflect.Method; 35 | import java.util.HashMap; 36 | 37 | /** 38 | * Utility class for loading meta-data from AndroidManifest.xml. This 39 | * class defines methods that convert meta-data values into type-specific 40 | * values. It also provides methods for initializing annotated classes 41 | * with values defined in the meta-data. 42 | * 43 | * The class is a Singleton, and must be access using the get () method. 44 | * We use the Singleton pattern since there is only one manifest for a 45 | * mobile application, and we can better control access to it (and its 46 | * memory usage) via the singleton object. 47 | */ 48 | public class ManifestMetadata 49 | { 50 | /// The metadata bundle 51 | private final Bundle metadata_; 52 | 53 | /// The application context for the program. 54 | private final Context context_; 55 | 56 | /// Singleton reference. 57 | private static WeakReference app_; 58 | 59 | private static final HashMap > activities_ = new HashMap<> (); 60 | 61 | private static final String TAG = "ManifestMetadata"; 62 | 63 | /** 64 | * Get the singleton instance of the manifest. W 65 | * @param context Execution context 66 | * @return 67 | * @throws NameNotFoundException 68 | */ 69 | public static ManifestMetadata get (Context context) 70 | throws NameNotFoundException 71 | { 72 | if (app_ != null && app_.get () != null) 73 | return app_.get (); 74 | 75 | // Load the metadata for the application. 76 | PackageManager pm = context.getPackageManager (); 77 | ApplicationInfo ai = pm.getApplicationInfo (context.getPackageName (), PackageManager.GET_META_DATA); 78 | ManifestMetadata mm = new ManifestMetadata (context, ai.metaData); 79 | 80 | app_ = new WeakReference <> (mm); 81 | 82 | return app_.get (); 83 | } 84 | 85 | /** 86 | * Get the metadata for the activity. 87 | * 88 | * @param activity 89 | * @return 90 | */ 91 | @SuppressWarnings ("unused") 92 | public static ManifestMetadata get (Activity activity) 93 | throws PackageManager.NameNotFoundException 94 | { 95 | return get (activity, activity.getComponentName ()); 96 | } 97 | 98 | /** 99 | * Get the metadata for a component. 100 | * 101 | * @param componentName 102 | * @return 103 | */ 104 | public static ManifestMetadata get (Context context, ComponentName componentName) 105 | throws PackageManager.NameNotFoundException 106 | { 107 | // Check if we already have a reference to the metadata for this activity. If 108 | // so, then just return our reference to the client. We can do this since the 109 | // metadata will not change over the lifetime of the application. 110 | WeakReference metadata = activities_.get (componentName); 111 | 112 | if (metadata != null && metadata.get () != null) 113 | return metadata.get (); 114 | 115 | // Load the metadata for the application. 116 | PackageManager pm = context.getPackageManager (); 117 | ActivityInfo ai = pm.getActivityInfo (componentName, PackageManager.GET_META_DATA); 118 | ManifestMetadata mm = new ManifestMetadata (context, ai.metaData); 119 | 120 | // Cache the metadata for future reference. 121 | metadata = new WeakReference<> (mm); 122 | activities_.put (componentName, metadata); 123 | 124 | return mm; 125 | } 126 | 127 | /** 128 | * Default constructor. 129 | * 130 | * @param context 131 | * @throws NameNotFoundException 132 | */ 133 | private ManifestMetadata (Context context, Bundle bundle) 134 | { 135 | this.context_ = context; 136 | this.metadata_ = bundle; 137 | } 138 | 139 | /** 140 | * Get the metadata bundle. 141 | * 142 | * @return 143 | */ 144 | public Bundle getMetadata () 145 | { 146 | return this.metadata_; 147 | } 148 | 149 | public boolean exists () 150 | { 151 | return this.metadata_ != null; 152 | } 153 | 154 | /** 155 | * Get the value of a meta-data element in AndroidManifest.xml. The 156 | * value is returned as a String value type. 157 | * 158 | * @param name Name of the meta-data element 159 | * @return Value of the meta-data element 160 | */ 161 | public String getValue (String name) 162 | { 163 | return this.metadata_.getString (name); 164 | } 165 | 166 | /** 167 | * Get the value of a metadata element in AndroidManifest.xml. If the 168 | * element exist, then a generic Object is return. If the value does not 169 | * exist, then an exception is thrown. 170 | */ 171 | public T getValue (String name, Class typeHint) 172 | throws NameNotFoundException, IllegalArgumentException, 173 | ClassNotFoundException, IllegalAccessException, InvocationTargetException 174 | { 175 | return this.getValue (name, false, ResourceType.Auto, typeHint); 176 | } 177 | 178 | public T getValue (String name, boolean fromResource, Class typeHint) 179 | throws NameNotFoundException, IllegalArgumentException, 180 | ClassNotFoundException, IllegalAccessException, InvocationTargetException 181 | { 182 | return getValue (name, fromResource, ResourceType.Auto, typeHint); 183 | } 184 | 185 | @SuppressWarnings ("unchecked") 186 | public T getValue (String name, 187 | boolean fromResource, 188 | ResourceType resourceType, 189 | Class typeHint) 190 | throws NameNotFoundException, IllegalArgumentException, 191 | ClassNotFoundException, IllegalAccessException, InvocationTargetException 192 | { 193 | if (!this.metadata_.containsKey (name)) 194 | throw new NameNotFoundException (name + " not defined in AndroidManifest.xml"); 195 | 196 | return (T)this.getValueFromMetadata (name, 197 | fromResource, 198 | resourceType, 199 | typeHint); 200 | } 201 | 202 | /** 203 | * Load metadata from the manifest and initialize annotated values/methods 204 | * in the target object. 205 | * 206 | * @param target Instance of object with annotated class 207 | * @throws NameNotFoundException 208 | * @throws IllegalAccessException 209 | * @throws IllegalArgumentException 210 | * @throws ClassNotFoundException 211 | * @throws InvocationTargetException 212 | */ 213 | public void initFromMetadata (T target) 214 | throws NameNotFoundException, IllegalArgumentException, 215 | IllegalAccessException, ClassNotFoundException, InvocationTargetException 216 | { 217 | if (this.metadata_ == null) 218 | return; 219 | 220 | // Get the class, and locate fields with @Metadata. 221 | Field [] fields = target.getClass ().getFields (); 222 | 223 | if (fields.length != 0) 224 | { 225 | // Iterate over each field in the class definition. If we find a 226 | // field with the @Metadata annotation, then we need to load its 227 | // value from AndroidManifest.xml. 228 | for (Field field : fields) 229 | { 230 | if (!field.isAnnotationPresent (MetadataProperty.class)) 231 | continue; 232 | 233 | // Get the annotation value, and the target meta-data name. 234 | MetadataProperty annotation = field.getAnnotation (MetadataProperty.class); 235 | String targetName = annotation.name (); 236 | 237 | // If the meta-data name is an empty string, use the field name. 238 | if (targetName.equals ("")) 239 | targetName = field.getName (); 240 | 241 | // Make sure that metadata does contain the target name before, 242 | // or there is no need to continue at this point. 243 | if (!this.metadata_.containsKey (targetName)) 244 | continue; 245 | 246 | // Load the value from the bundle. 247 | Object theValue = 248 | this.getValueFromMetadata (targetName, 249 | annotation.fromResource (), 250 | annotation.resourceType (), 251 | field.getType ()); 252 | 253 | // Finally, we can set the value! 254 | field.set (target, theValue); 255 | } 256 | } 257 | 258 | // Locate all methods with @MetadataMethod 259 | Method [] methods = target.getClass ().getMethods (); 260 | 261 | if (methods.length != 0) 262 | { 263 | // Iterate over each field in the class definition. If we find a 264 | // field with the @Metadata annotation, then we need to load its 265 | // value from AndroidManifest.xml. 266 | for (Method method : methods) 267 | { 268 | if (!method.isAnnotationPresent (MetadataMethod.class)) 269 | continue; 270 | 271 | // Get the annotation value, and the target meta-data name. For methods, 272 | // the target name is required. 273 | MetadataMethod annotation = method.getAnnotation (MetadataMethod.class); 274 | String targetName = annotation.name (); 275 | 276 | // Make sure that meta-data does contain the target name before, 277 | // or there is no need to continue at this point. 278 | if (!this.metadata_.containsKey (targetName)) 279 | continue; 280 | 281 | // Load the value from the bundle. 282 | Object theValue = 283 | this.getValueFromMetadata (targetName, 284 | annotation.fromResource (), 285 | annotation.resourceType (), 286 | method.getParameterTypes ()[0]); 287 | 288 | // Finally, we can set the value! 289 | method.invoke (target, theValue); 290 | } 291 | } 292 | } 293 | 294 | /** 295 | * Load a value from meta-data. The returned value is an Object of the 296 | * correct type. It is the responsibility of the caller to convert the 297 | * returned object to the correct type. 298 | * 299 | * For classes, the return object is an instance of the class. The class 300 | * therefore must have a default constructor defined. If one is not defined, 301 | * then an exception will be thrown. 302 | * 303 | * @param targetName Name of value in meta-data bundle 304 | * @param typeInfo Type information about the target property 305 | * @return 306 | * @throws ClassNotFoundException 307 | * @throws IllegalArgumentException 308 | * @throws IllegalAccessException 309 | * @throws InvocationTargetException 310 | */ 311 | private Object getValueFromMetadata (String targetName, 312 | boolean fromResource, 313 | ResourceType rcType, 314 | Class typeInfo) 315 | throws ClassNotFoundException, IllegalArgumentException, 316 | IllegalAccessException, InvocationTargetException 317 | { 318 | // Load the value from the bundle. 319 | Object theValue = this.metadata_.get (targetName); 320 | 321 | // The current value that we are reading from the bundle is actually 322 | // a resource id. We therefore need to convert the value to an Integer 323 | // so we can load it from the the resources. 324 | if (fromResource) 325 | { 326 | Integer rcid = (Integer)theValue; 327 | 328 | // Either, we are going to auto-detect the resource type based on the 329 | // type of the field, or we are given a hint in the annotation. 330 | if (rcType.equals (ResourceType.Auto)) 331 | theValue = this.getValueFromResource (rcid, typeInfo); 332 | else 333 | theValue = this.getValueFromResource (rcid, rcType); 334 | } 335 | 336 | if (typeInfo.equals (Class.class)) 337 | { 338 | // The value is a Class object. Let's load the class object. 339 | ClassLoader classLoader = this.context_.getClassLoader (); 340 | theValue = classLoader.loadClass ((String)theValue); 341 | } 342 | 343 | return theValue; 344 | } 345 | 346 | /** 347 | * Get the value from a resource. The value type is determined by 348 | * the field object type. 349 | * 350 | * @param rcid 351 | * @return 352 | */ 353 | private Object getValueFromResource (int rcid, Class typeInfo) 354 | { 355 | Resources r = this.context_.getResources (); 356 | 357 | if (typeInfo.isAssignableFrom (String.class)) 358 | return r.getString (rcid); 359 | else if (typeInfo.isAssignableFrom (int.class) || typeInfo.isAssignableFrom (Integer.class)) 360 | return rcid; 361 | else if (typeInfo.isAssignableFrom (boolean.class) || typeInfo.isAssignableFrom (Boolean.class)) 362 | return r.getBoolean (rcid); 363 | else if (typeInfo.isAssignableFrom (float.class) || typeInfo.isAssignableFrom (Float.class)) 364 | return r.getDimension (rcid); 365 | else if (typeInfo.isAssignableFrom (int[].class)) 366 | return r.getIntArray (rcid); 367 | else if (typeInfo.isAssignableFrom (XmlResourceParser.class)) 368 | return r.getAnimation (rcid); 369 | else 370 | return null; 371 | } 372 | 373 | /** 374 | * Get the value from a resource. The resource type is determined by 375 | * the rcType parameter. 376 | * 377 | * @param rcid 378 | * @param rcType 379 | * @return 380 | * @throws IllegalArgumentException 381 | * @throws IllegalAccessException 382 | * @throws InvocationTargetException 383 | */ 384 | private Object getValueFromResource (int rcid, ResourceType rcType) 385 | throws IllegalArgumentException, IllegalAccessException, InvocationTargetException 386 | { 387 | Method method = resourceTable_.get (rcType); 388 | return method.invoke (this.context_.getResources (), rcid); 389 | } 390 | 391 | private static final HashMap resourceTable_ = new HashMap (); 392 | 393 | static 394 | { 395 | installResourceMethodAccessor (ResourceType.Animation, "getAnimation"); 396 | installResourceMethodAccessor (ResourceType.Boolean, "getBoolean"); 397 | installResourceMethodAccessor (ResourceType.Color, "getColor"); 398 | installResourceMethodAccessor (ResourceType.Dimension, "getDimension"); 399 | installResourceMethodAccessor (ResourceType.DimensionPixelOffset, "getDimensionPixelOffset"); 400 | installResourceMethodAccessor (ResourceType.DimensionPixelSize, "getDimensionPixelSize"); 401 | installResourceMethodAccessor (ResourceType.Drawable, "getDrawable"); 402 | installResourceMethodAccessor (ResourceType.Id, "getInteger"); 403 | installResourceMethodAccessor (ResourceType.Integer, "getInteger"); 404 | installResourceMethodAccessor (ResourceType.IntArray, "getIntArray"); 405 | installResourceMethodAccessor (ResourceType.String, "getString"); 406 | } 407 | 408 | /** 409 | * Install the resource access method for the specified resource type. 410 | * 411 | * @param rcType 412 | * @param methodName 413 | */ 414 | private static void installResourceMethodAccessor (ResourceType rcType, String methodName) 415 | { 416 | Class clazz = Resources.class; 417 | 418 | try 419 | { 420 | resourceTable_.put (rcType, clazz.getMethod (methodName, int.class)); 421 | } 422 | catch (SecurityException e) 423 | { 424 | Log.w (TAG, e.getMessage (), e); 425 | } 426 | catch (NoSuchMethodException e) 427 | { 428 | Log.w (TAG, e.getMessage (), e); 429 | } 430 | } 431 | } -------------------------------------------------------------------------------- /src/main/java/com/onehilltech/metadata/MetadataMethod.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2013, James H. Hill 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.onehilltech.metadata; 18 | 19 | import java.lang.annotation.Documented; 20 | import java.lang.annotation.ElementType; 21 | import java.lang.annotation.Retention; 22 | import java.lang.annotation.RetentionPolicy; 23 | import java.lang.annotation.Target; 24 | 25 | /** 26 | * Metadata annotation for setter methods of a class. The setter method 27 | * can only have 1 parameter---the value read from metadata. 28 | */ 29 | @Documented 30 | @Retention(RetentionPolicy.RUNTIME) 31 | @Target(ElementType.METHOD) 32 | public @interface MetadataMethod 33 | { 34 | /// Name of the metadata. 35 | String name (); 36 | 37 | /// The metadata value comes from a resource. 38 | boolean fromResource () default false; 39 | 40 | /// Get the hint for the resource type. 41 | ResourceType resourceType () default ResourceType.Auto; 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/onehilltech/metadata/MetadataProperty.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2013, James H. Hill 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.onehilltech.metadata; 18 | 19 | import java.lang.annotation.Documented; 20 | import java.lang.annotation.ElementType; 21 | import java.lang.annotation.Retention; 22 | import java.lang.annotation.RetentionPolicy; 23 | import java.lang.annotation.Target; 24 | 25 | /** 26 | * Metadata annotation for class attributes. 27 | */ 28 | @Documented 29 | @Retention(RetentionPolicy.RUNTIME) 30 | @Target(ElementType.FIELD) 31 | public @interface MetadataProperty 32 | { 33 | /// Name of the metadata. 34 | String name () default ""; 35 | 36 | /// The metadata value comes from a resource. 37 | boolean fromResource () default false; 38 | 39 | /// Get the hint for the resource type. 40 | ResourceType resourceType () default ResourceType.Auto; 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/onehilltech/metadata/ResourceType.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2013, James H. Hill 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.onehilltech.metadata; 18 | 19 | /** 20 | * Enumeration of the different Android resource types. These values 21 | * are for providing a hint to the @Metadata annotation. 22 | */ 23 | public enum ResourceType 24 | { 25 | Auto, 26 | Animation, 27 | Boolean, 28 | Color, 29 | Dimension, 30 | DimensionPixelOffset, 31 | DimensionPixelSize, 32 | Drawable, 33 | Id, 34 | Integer, 35 | IntArray, 36 | String 37 | } 38 | --------------------------------------------------------------------------------