diff --git a/.github/workflows/android-appcenter.yml b/.github/workflows/android-appcenter.yml index b4b63f8..3000e2b 100644 --- a/.github/workflows/android-appcenter.yml +++ b/.github/workflows/android-appcenter.yml @@ -6,20 +6,18 @@ on: branches: [ main ] paths: - '.github/workflows/android-appcenter.yml' - - 'demo/android/Activity/**' - - '!demo/android/Activity/README.md' + - 'binding/android/KoalaTestApp/**' - 'resources/audio_samples/**' pull_request: branches: [ main, 'v[0-9]+.[0-9]+'] paths: - '.github/workflows/android-appcenter.yml' - - 'demo/android/Activity/**' - - '!demo/android/Activity/README.md' + - 'binding/android/KoalaTestApp/**' - 'resources/audio_samples/**' defaults: run: - working-directory: demo/android/Activity + working-directory: binding/android/KoalaTestApp jobs: build: @@ -43,16 +41,22 @@ jobs: java-version: '11' distribution: 'temurin' - - name: Binding pre-build - run: ./gradlew assemble - working-directory: binding/android - - name: Copy test_resources run: ./copy_test_resources.sh - name: Inject AccessKey run: echo pvTestingAccessKey="${{secrets.PV_VALID_ACCESS_KEY}}" >> local.properties + - name: Inject Android keystore variables + run: | + echo storePassword="${{secrets.ANDROID_RELEASE_KEYSTORE_PASSWORD}}" >> local.properties + echo keyPassword="${{secrets.ANDROID_RELEASE_KEYSTORE_PASSWORD}}" >> local.properties + echo keyAlias=picovoice >> local.properties + echo storeFile=../picovoice.jks >> local.properties + + - name: Setup Android keystore file + run: echo "${{secrets.ANDROID_RELEASE_KEYSTORE_FILE_B64}}" | base64 -d > picovoice.jks + - name: Build app run: ./gradlew assembleDebug @@ -64,7 +68,63 @@ jobs: --token ${{secrets.APPCENTERAPITOKEN}} --app "Picovoice/Koala-Android" --devices "Picovoice/android-min-max" - --app-path koala-activity-demo-app/build/outputs/apk/debug/koala-activity-demo-app-debug.apk + --app-path koala-test-app/build/outputs/apk/debug/koala-test-app-debug.apk + --test-series "koala-android" + --locale "en_US" + --build-dir koala-test-app/build/outputs/apk/androidTest/debug + + build-integ: + name: Run Android Integration Tests on AppCenter + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Set up Node.js LTS + uses: actions/setup-node@v3 + with: + node-version: lts/* + + - name: Install AppCenter CLI + run: npm install -g appcenter-cli + + - name: set up JDK 11 + uses: actions/setup-java@v3 + with: + java-version: '11' + distribution: 'temurin' + + - name: Copy test_resources + run: ./copy_test_resources.sh + + - name: Inject AccessKey + run: echo pvTestingAccessKey="${{secrets.PV_VALID_ACCESS_KEY}}" >> local.properties + + - name: Inject Android keystore variables + run: | + echo storePassword="${{secrets.ANDROID_RELEASE_KEYSTORE_PASSWORD}}" >> local.properties + echo keyPassword="${{secrets.ANDROID_RELEASE_KEYSTORE_PASSWORD}}" >> local.properties + echo keyAlias=picovoice >> local.properties + echo storeFile=../picovoice.jks >> local.properties + + - name: Setup Android keystore file + run: echo "${{secrets.ANDROID_RELEASE_KEYSTORE_FILE_B64}}" | base64 -d > picovoice.jks + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Build app + run: ./gradlew assembleRelease + + - name: Build androidTest + run: ./gradlew assembleReleaseAndroidTest -DtestBuildType=integ + + - name: Run tests on AppCenter + run: appcenter test run espresso + --token ${{secrets.APPCENTERAPITOKEN}} + --app "Picovoice/Koala-Android" + --devices "Picovoice/android-min-max" + --app-path koala-test-app/build/outputs/apk/release/koala-test-app-release.apk --test-series "koala-android" --locale "en_US" - --build-dir koala-activity-demo-app/build/outputs/apk/androidTest/debug + --build-dir koala-test-app/build/outputs/apk/androidTest/release diff --git a/.github/workflows/android-perf.yml b/.github/workflows/android-perf.yml index e3a72a6..ba9bd6e 100644 --- a/.github/workflows/android-perf.yml +++ b/.github/workflows/android-perf.yml @@ -19,7 +19,7 @@ on: defaults: run: - working-directory: demo/android/Activity + working-directory: binding/android/KoalaTestApp jobs: build: @@ -58,6 +58,16 @@ jobs: - name: Inject AccessKey run: echo pvTestingAccessKey="${{secrets.PV_VALID_ACCESS_KEY}}" >> local.properties + - name: Inject Android keystore variables + run: | + echo storePassword="${{secrets.ANDROID_RELEASE_KEYSTORE_PASSWORD}}" >> local.properties + echo keyPassword="${{secrets.ANDROID_RELEASE_KEYSTORE_PASSWORD}}" >> local.properties + echo keyAlias=picovoice >> local.properties + echo storeFile=../picovoice.jks >> local.properties + + - name: Setup Android keystore file + run: echo "${{secrets.ANDROID_RELEASE_KEYSTORE_FILE_B64}}" | base64 -d > picovoice.jks + - name: Inject Number of Iterations run: echo numTestIterations="30" >> local.properties @@ -71,15 +81,15 @@ jobs: run: ./gradlew assembleDebug - name: Build androidTest - run: ./gradlew assembleAndroidTest + run: ./gradlew assembleAndroidTest -DtestBuildType=perf - name: Run tests on AppCenter run: appcenter test run espresso --token ${{secrets.APPCENTERAPITOKEN}} --app "Picovoice/Koala-Android" --devices "Picovoice/${{ matrix.device }}" - --app-path koala-activity-demo-app/build/outputs/apk/debug/koala-activity-demo-app-debug.apk + --app-path koala-test-app/build/outputs/apk/debug/koala-test-app-debug.apk --test-series "koala-android" --locale "en_US" - --build-dir koala-activity-demo-app/build/outputs/apk/androidTest/debug + --build-dir koala-test-app/build/outputs/apk/androidTest/debug diff --git a/binding/android/.gitignore b/binding/android/Koala/.gitignore similarity index 100% rename from binding/android/.gitignore rename to binding/android/Koala/.gitignore diff --git a/binding/android/build.gradle b/binding/android/Koala/build.gradle similarity index 100% rename from binding/android/build.gradle rename to binding/android/Koala/build.gradle diff --git a/binding/android/gradle.properties b/binding/android/Koala/gradle.properties similarity index 100% rename from binding/android/gradle.properties rename to binding/android/Koala/gradle.properties diff --git a/binding/android/gradle/wrapper/gradle-wrapper.jar b/binding/android/Koala/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from binding/android/gradle/wrapper/gradle-wrapper.jar rename to binding/android/Koala/gradle/wrapper/gradle-wrapper.jar diff --git a/binding/android/gradle/wrapper/gradle-wrapper.properties b/binding/android/Koala/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from binding/android/gradle/wrapper/gradle-wrapper.properties rename to binding/android/Koala/gradle/wrapper/gradle-wrapper.properties diff --git a/binding/android/gradlew b/binding/android/Koala/gradlew similarity index 100% rename from binding/android/gradlew rename to binding/android/Koala/gradlew diff --git a/binding/android/gradlew.bat b/binding/android/Koala/gradlew.bat similarity index 100% rename from binding/android/gradlew.bat rename to binding/android/Koala/gradlew.bat diff --git a/binding/android/koala/.gitignore b/binding/android/Koala/koala/.gitignore similarity index 100% rename from binding/android/koala/.gitignore rename to binding/android/Koala/koala/.gitignore diff --git a/binding/android/koala/build.gradle b/binding/android/Koala/koala/build.gradle similarity index 100% rename from binding/android/koala/build.gradle rename to binding/android/Koala/koala/build.gradle diff --git a/binding/android/koala/consumer-rules.pro b/binding/android/Koala/koala/consumer-rules.pro similarity index 100% rename from binding/android/koala/consumer-rules.pro rename to binding/android/Koala/koala/consumer-rules.pro diff --git a/binding/android/koala/proguard-rules.pro b/binding/android/Koala/koala/proguard-rules.pro similarity index 100% rename from binding/android/koala/proguard-rules.pro rename to binding/android/Koala/koala/proguard-rules.pro diff --git a/binding/android/koala/src/main/AndroidManifest.xml b/binding/android/Koala/koala/src/main/AndroidManifest.xml similarity index 100% rename from binding/android/koala/src/main/AndroidManifest.xml rename to binding/android/Koala/koala/src/main/AndroidManifest.xml diff --git a/binding/android/koala/src/main/java/ai/picovoice/koala/Koala.java b/binding/android/Koala/koala/src/main/java/ai/picovoice/koala/Koala.java similarity index 100% rename from binding/android/koala/src/main/java/ai/picovoice/koala/Koala.java rename to binding/android/Koala/koala/src/main/java/ai/picovoice/koala/Koala.java diff --git a/binding/android/koala/src/main/java/ai/picovoice/koala/KoalaNative.java b/binding/android/Koala/koala/src/main/java/ai/picovoice/koala/KoalaNative.java similarity index 100% rename from binding/android/koala/src/main/java/ai/picovoice/koala/KoalaNative.java rename to binding/android/Koala/koala/src/main/java/ai/picovoice/koala/KoalaNative.java diff --git a/binding/android/koala/src/main/java/ai/picovoice/koala/exception/KoalaActivationException.java b/binding/android/Koala/koala/src/main/java/ai/picovoice/koala/exception/KoalaActivationException.java similarity index 100% rename from binding/android/koala/src/main/java/ai/picovoice/koala/exception/KoalaActivationException.java rename to binding/android/Koala/koala/src/main/java/ai/picovoice/koala/exception/KoalaActivationException.java diff --git a/binding/android/koala/src/main/java/ai/picovoice/koala/exception/KoalaActivationLimitException.java b/binding/android/Koala/koala/src/main/java/ai/picovoice/koala/exception/KoalaActivationLimitException.java similarity index 100% rename from binding/android/koala/src/main/java/ai/picovoice/koala/exception/KoalaActivationLimitException.java rename to binding/android/Koala/koala/src/main/java/ai/picovoice/koala/exception/KoalaActivationLimitException.java diff --git a/binding/android/koala/src/main/java/ai/picovoice/koala/exception/KoalaActivationRefusedException.java b/binding/android/Koala/koala/src/main/java/ai/picovoice/koala/exception/KoalaActivationRefusedException.java similarity index 100% rename from binding/android/koala/src/main/java/ai/picovoice/koala/exception/KoalaActivationRefusedException.java rename to binding/android/Koala/koala/src/main/java/ai/picovoice/koala/exception/KoalaActivationRefusedException.java diff --git a/binding/android/koala/src/main/java/ai/picovoice/koala/exception/KoalaActivationThrottledException.java b/binding/android/Koala/koala/src/main/java/ai/picovoice/koala/exception/KoalaActivationThrottledException.java similarity index 100% rename from binding/android/koala/src/main/java/ai/picovoice/koala/exception/KoalaActivationThrottledException.java rename to binding/android/Koala/koala/src/main/java/ai/picovoice/koala/exception/KoalaActivationThrottledException.java diff --git a/binding/android/koala/src/main/java/ai/picovoice/koala/exception/KoalaException.java b/binding/android/Koala/koala/src/main/java/ai/picovoice/koala/exception/KoalaException.java similarity index 100% rename from binding/android/koala/src/main/java/ai/picovoice/koala/exception/KoalaException.java rename to binding/android/Koala/koala/src/main/java/ai/picovoice/koala/exception/KoalaException.java diff --git a/binding/android/koala/src/main/java/ai/picovoice/koala/exception/KoalaIOException.java b/binding/android/Koala/koala/src/main/java/ai/picovoice/koala/exception/KoalaIOException.java similarity index 100% rename from binding/android/koala/src/main/java/ai/picovoice/koala/exception/KoalaIOException.java rename to binding/android/Koala/koala/src/main/java/ai/picovoice/koala/exception/KoalaIOException.java diff --git a/binding/android/koala/src/main/java/ai/picovoice/koala/exception/KoalaInvalidArgumentException.java b/binding/android/Koala/koala/src/main/java/ai/picovoice/koala/exception/KoalaInvalidArgumentException.java similarity index 100% rename from binding/android/koala/src/main/java/ai/picovoice/koala/exception/KoalaInvalidArgumentException.java rename to binding/android/Koala/koala/src/main/java/ai/picovoice/koala/exception/KoalaInvalidArgumentException.java diff --git a/binding/android/koala/src/main/java/ai/picovoice/koala/exception/KoalaInvalidStateException.java b/binding/android/Koala/koala/src/main/java/ai/picovoice/koala/exception/KoalaInvalidStateException.java similarity index 100% rename from binding/android/koala/src/main/java/ai/picovoice/koala/exception/KoalaInvalidStateException.java rename to binding/android/Koala/koala/src/main/java/ai/picovoice/koala/exception/KoalaInvalidStateException.java diff --git a/binding/android/koala/src/main/java/ai/picovoice/koala/exception/KoalaKeyException.java b/binding/android/Koala/koala/src/main/java/ai/picovoice/koala/exception/KoalaKeyException.java similarity index 100% rename from binding/android/koala/src/main/java/ai/picovoice/koala/exception/KoalaKeyException.java rename to binding/android/Koala/koala/src/main/java/ai/picovoice/koala/exception/KoalaKeyException.java diff --git a/binding/android/koala/src/main/java/ai/picovoice/koala/exception/KoalaMemoryException.java b/binding/android/Koala/koala/src/main/java/ai/picovoice/koala/exception/KoalaMemoryException.java similarity index 100% rename from binding/android/koala/src/main/java/ai/picovoice/koala/exception/KoalaMemoryException.java rename to binding/android/Koala/koala/src/main/java/ai/picovoice/koala/exception/KoalaMemoryException.java diff --git a/binding/android/koala/src/main/java/ai/picovoice/koala/exception/KoalaRuntimeException.java b/binding/android/Koala/koala/src/main/java/ai/picovoice/koala/exception/KoalaRuntimeException.java similarity index 100% rename from binding/android/koala/src/main/java/ai/picovoice/koala/exception/KoalaRuntimeException.java rename to binding/android/Koala/koala/src/main/java/ai/picovoice/koala/exception/KoalaRuntimeException.java diff --git a/binding/android/koala/src/main/java/ai/picovoice/koala/exception/KoalaStopIterationException.java b/binding/android/Koala/koala/src/main/java/ai/picovoice/koala/exception/KoalaStopIterationException.java similarity index 100% rename from binding/android/koala/src/main/java/ai/picovoice/koala/exception/KoalaStopIterationException.java rename to binding/android/Koala/koala/src/main/java/ai/picovoice/koala/exception/KoalaStopIterationException.java diff --git a/binding/android/settings.gradle b/binding/android/Koala/settings.gradle similarity index 100% rename from binding/android/settings.gradle rename to binding/android/Koala/settings.gradle diff --git a/binding/android/KoalaTestApp/.gitignore b/binding/android/KoalaTestApp/.gitignore new file mode 100644 index 0000000..7d775c9 --- /dev/null +++ b/binding/android/KoalaTestApp/.gitignore @@ -0,0 +1,16 @@ +*.iml +.gradle +/local.properties +/.idea/ +.DS_Store +/build +/captures +.externalNativeBuild +release +test_resources + +*.pv +*.wav + +*.jks +!.dummy.jks \ No newline at end of file diff --git a/binding/android/KoalaTestApp/build.gradle b/binding/android/KoalaTestApp/build.gradle new file mode 100644 index 0000000..9e877c5 --- /dev/null +++ b/binding/android/KoalaTestApp/build.gradle @@ -0,0 +1,28 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +ext { + defaultTargetSdkVersion = 31 +} + +buildscript { + repositories { + google() + mavenCentral() + } + dependencies { + classpath 'com.android.tools.build:gradle:7.2.2' + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/binding/android/KoalaTestApp/copy_test_resources.sh b/binding/android/KoalaTestApp/copy_test_resources.sh new file mode 100755 index 0000000..744ef16 --- /dev/null +++ b/binding/android/KoalaTestApp/copy_test_resources.sh @@ -0,0 +1,9 @@ +if [ ! -d "./koala-test-app/src/androidTest/assets/test_resources/audio" ] +then + echo "Creating test audio samples directory..." + mkdir -p ./koala-test-app/src/androidTest/assets/test_resources/audio +fi + +echo "Copying test audio samples..." +cp ../../../resources/audio_samples/test.wav ./koala-test-app/src/androidTest/assets/test_resources/audio/test.wav +cp ../../../resources/audio_samples/noise.wav ./koala-test-app/src/androidTest/assets/test_resources/audio/noise.wav diff --git a/binding/android/KoalaTestApp/gradle.properties b/binding/android/KoalaTestApp/gradle.properties new file mode 100644 index 0000000..c09e1e3 --- /dev/null +++ b/binding/android/KoalaTestApp/gradle.properties @@ -0,0 +1,17 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app"s APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true diff --git a/binding/android/KoalaTestApp/gradle/wrapper/gradle-wrapper.jar b/binding/android/KoalaTestApp/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..f6b961f Binary files /dev/null and b/binding/android/KoalaTestApp/gradle/wrapper/gradle-wrapper.jar differ diff --git a/binding/android/KoalaTestApp/gradle/wrapper/gradle-wrapper.properties b/binding/android/KoalaTestApp/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..3fadc5c --- /dev/null +++ b/binding/android/KoalaTestApp/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Tue Jun 29 23:02:09 PDT 2021 +distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/binding/android/KoalaTestApp/gradlew b/binding/android/KoalaTestApp/gradlew new file mode 100755 index 0000000..cccdd3d --- /dev/null +++ b/binding/android/KoalaTestApp/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# 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\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# 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 +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +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" -a "$nonstop" = "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"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # 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 + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/binding/android/KoalaTestApp/gradlew.bat b/binding/android/KoalaTestApp/gradlew.bat new file mode 100644 index 0000000..e95643d --- /dev/null +++ b/binding/android/KoalaTestApp/gradlew.bat @@ -0,0 +1,84 @@ +@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 + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@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= + +@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 Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_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=%* + +: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/binding/android/KoalaTestApp/koala-test-app/.gitignore b/binding/android/KoalaTestApp/koala-test-app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/binding/android/KoalaTestApp/koala-test-app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/binding/android/KoalaTestApp/koala-test-app/build.gradle b/binding/android/KoalaTestApp/koala-test-app/build.gradle new file mode 100644 index 0000000..fbe2c9a --- /dev/null +++ b/binding/android/KoalaTestApp/koala-test-app/build.gradle @@ -0,0 +1,131 @@ +apply plugin: 'com.android.application' + +Properties properties = new Properties() +if (rootProject.file("local.properties").exists()) { + properties.load(rootProject.file("local.properties").newDataInputStream()) + if (project.hasProperty("pvTestingAccessKey")) { + properties.put("pvTestingAccessKey", project.getProperty("pvTestingAccessKey")) + } + if (project.hasProperty("numTestIterations")) { + properties.put("numTestIterations", project.getProperty("numTestIterations")) + } + if (project.hasProperty("performanceThresholdSec")) { + properties.put("performanceThresholdSec", project.getProperty("performanceThresholdSec")) + } + + if (project.hasProperty("storePassword")) { + properties.put("storePassword", project.getProperty("storePassword")) + } + if (project.hasProperty("storeFile")) { + properties.put("storeFile", project.getProperty("storeFile")) + } + if (project.hasProperty("keyAlias")) { + properties.put("keyAlias", project.getProperty("keyAlias")) + } + if (project.hasProperty("keyPassword")) { + properties.put("keyPassword", project.getProperty("keyPassword")) + } +} + +android { + compileSdkVersion defaultTargetSdkVersion + + defaultConfig { + applicationId "ai.picovoice.koala.testapp" + minSdkVersion 21 + targetSdkVersion defaultTargetSdkVersion + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + + resValue 'string', 'pvTestingAccessKey', properties.getProperty("pvTestingAccessKey", "") + resValue 'string', 'numTestIterations', properties.getProperty("numTestIterations", "") + resValue 'string', 'performanceThresholdSec', properties.getProperty("performanceThresholdSec", "") + } + + signingConfigs { + release { + storePassword properties.getProperty("storePassword") + storeFile file(properties.getProperty("storeFile", ".dummy.jks")) + keyAlias properties.getProperty("keyAlias") + keyPassword properties.getProperty("keyPassword") + } + } + + buildTypes { + debug { + signingConfig signingConfigs.release + } + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + signingConfig signingConfigs.release + } + } + + if (System.getProperty("testBuildType", "debug") == "integ") { + testBuildType("release") + } + + sourceSets { + androidTest { + java { + if (System.getProperty("testBuildType", "debug") == "perf") { + exclude "**/KoalaTest.java" + exclude "**/IntegrationTest.java" + } else if (System.getProperty("testBuildType", "debug") == "integ") { + exclude "**/KoalaTest.java" + exclude "**/PerformanceTest.java" + } else { + exclude "**/IntegrationTest.java" + exclude "**/PerformanceTest.java" + } + } + } + } + + task("copyParams", type: Copy) { + from("$projectDir/../../../../lib/common/") + include("koala_params.pv") + into("$projectDir/src/main/assets/models") + } + task("copyAudio", type: Copy) { + description = "Copy audio resources" + from("$projectDir/../../../../resources/audio_samples/") + include("noise.wav") + into("$projectDir/src/main/assets/audio_samples") + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + lint { + abortOnError false + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.3.1' + implementation 'com.google.android.material:material:1.4.0' + implementation 'androidx.constraintlayout:constraintlayout:2.1.1' + implementation 'androidx.navigation:navigation-fragment:2.3.5' + implementation 'androidx.navigation:navigation-ui:2.3.5' + implementation 'ai.picovoice:koala-android:1.0.0' + + // Espresso UI Testing + androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation('androidx.test.espresso:espresso-core:3.2.0', { + exclude group: 'com.android.support', module: 'support-annotations' + }) + androidTestImplementation('com.microsoft.appcenter:espresso-test-extension:1.4') + androidTestImplementation('androidx.test.espresso:espresso-intents:3.5.1') +} + +afterEvaluate { + tasks."mergeDebugAssets".dependsOn "copyParams" + tasks."mergeReleaseAssets".dependsOn "copyParams" + tasks."mergeDebugAssets".dependsOn "copyAudio" + tasks."mergeReleaseAssets".dependsOn "copyAudio" +} diff --git a/binding/android/KoalaTestApp/koala-test-app/proguard-rules.pro b/binding/android/KoalaTestApp/koala-test-app/proguard-rules.pro new file mode 100644 index 0000000..158caf3 --- /dev/null +++ b/binding/android/KoalaTestApp/koala-test-app/proguard-rules.pro @@ -0,0 +1,23 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# 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 *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile +-keep class com.google.** { *; } +-keep class com.microsoft.** { *; } \ No newline at end of file diff --git a/binding/android/KoalaTestApp/koala-test-app/src/androidTest/java/ai/picovoice/koala/testapp/IntegrationTest.java b/binding/android/KoalaTestApp/koala-test-app/src/androidTest/java/ai/picovoice/koala/testapp/IntegrationTest.java new file mode 100644 index 0000000..ba7e18f --- /dev/null +++ b/binding/android/KoalaTestApp/koala-test-app/src/androidTest/java/ai/picovoice/koala/testapp/IntegrationTest.java @@ -0,0 +1,108 @@ +package ai.picovoice.koala.testapp; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom; +import static androidx.test.espresso.matcher.ViewMatchers.withId; + +import android.view.View; +import android.widget.TextView; + +import androidx.test.espresso.PerformException; +import androidx.test.espresso.UiController; +import androidx.test.espresso.ViewAction; +import androidx.test.espresso.intent.Intents; +import androidx.test.espresso.util.HumanReadables; +import androidx.test.ext.junit.rules.ActivityScenarioRule; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import com.microsoft.appcenter.espresso.Factory; +import com.microsoft.appcenter.espresso.ReportHelper; + +import org.hamcrest.Matcher; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.TimeoutException; + +class WaitForTextAction implements ViewAction { + private final String text; + private final long timeout; + + public WaitForTextAction(String text, long timeout) { + this.text = text; + this.timeout = timeout; + } + + @Override + public String getDescription() { + return String.format( + "Wait for '%d' milliseconds for the view to have text '%s'", + this.timeout, + this.text + ); + } + + @Override + public Matcher getConstraints() { + return isAssignableFrom(TextView.class); + } + + @Override + public void perform(UiController uiController, View view) { + long endTime = System.currentTimeMillis() + this.timeout; + + while (System.currentTimeMillis() < endTime) { + TextView textView = (TextView) view; + if (textView.getText().equals(this.text)) { + return; + } + uiController.loopMainThreadForAtLeast(50); + } + + throw new PerformException.Builder() + .withActionDescription(this.getDescription()) + .withCause(new TimeoutException(String.format("Waited for '%d' milliseconds", this.timeout))) + .withViewDescription(HumanReadables.describe(view)) + .build(); + } +} + +@RunWith(AndroidJUnit4.class) +public class IntegrationTest { + + @Rule + public ReportHelper reportHelper = Factory.getReportHelper(); + + @Rule + public ActivityScenarioRule activityScenarioRule = + new ActivityScenarioRule<>(MainActivity.class); + + @Before + public void intentsInit() { + Intents.init(); + } + + @After + public void intentsTeardown() { + Intents.release(); + } + + @After + public void TearDown() { + reportHelper.label("Stopping App"); + } + + @Test + public void testKoala() { + onView(withId(R.id.testButton)).perform(click()); + onView(withId(R.id.testResult)).perform(waitForText("Passed", 60000)); + } + + private ViewAction waitForText(String text, long timeout) { + return new WaitForTextAction(text, timeout); + } +} diff --git a/demo/android/Activity/koala-activity-demo-app/src/androidTest/java/ai/picovoice/koalaactivitydemo/KoalaTest.java b/binding/android/KoalaTestApp/koala-test-app/src/androidTest/java/ai/picovoice/koala/testapp/KoalaTest.java similarity index 99% rename from demo/android/Activity/koala-activity-demo-app/src/androidTest/java/ai/picovoice/koalaactivitydemo/KoalaTest.java rename to binding/android/KoalaTestApp/koala-test-app/src/androidTest/java/ai/picovoice/koala/testapp/KoalaTest.java index 5ebced1..fa10921 100644 --- a/demo/android/Activity/koala-activity-demo-app/src/androidTest/java/ai/picovoice/koalaactivitydemo/KoalaTest.java +++ b/binding/android/KoalaTestApp/koala-test-app/src/androidTest/java/ai/picovoice/koala/testapp/KoalaTest.java @@ -8,7 +8,7 @@ limitations under the License. */ -package ai.picovoice.koalaactivitydemo; +package ai.picovoice.koala.testapp; import static androidx.test.core.app.ApplicationProvider.getApplicationContext; @@ -41,12 +41,12 @@ import java.util.ArrayList; import java.util.List; -import ai.picovoice.koala.Koala; -import ai.picovoice.koala.KoalaException; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import ai.picovoice.koala.Koala; +import ai.picovoice.koala.KoalaException; + @RunWith(AndroidJUnit4.class) public class KoalaTest { diff --git a/demo/android/Activity/koala-activity-demo-app/src/androidTest/java/ai/picovoice/koalaactivitydemo/PerformanceTest.java b/binding/android/KoalaTestApp/koala-test-app/src/androidTest/java/ai/picovoice/koala/testapp/PerformanceTest.java similarity index 99% rename from demo/android/Activity/koala-activity-demo-app/src/androidTest/java/ai/picovoice/koalaactivitydemo/PerformanceTest.java rename to binding/android/KoalaTestApp/koala-test-app/src/androidTest/java/ai/picovoice/koala/testapp/PerformanceTest.java index 44407a7..02767a2 100644 --- a/demo/android/Activity/koala-activity-demo-app/src/androidTest/java/ai/picovoice/koalaactivitydemo/PerformanceTest.java +++ b/binding/android/KoalaTestApp/koala-test-app/src/androidTest/java/ai/picovoice/koala/testapp/PerformanceTest.java @@ -8,7 +8,7 @@ limitations under the License. */ -package ai.picovoice.koalaactivitydemo; +package ai.picovoice.koala.testapp; import static androidx.test.core.app.ApplicationProvider.getApplicationContext; import static org.junit.Assert.assertTrue; diff --git a/binding/android/KoalaTestApp/koala-test-app/src/main/AndroidManifest.xml b/binding/android/KoalaTestApp/koala-test-app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..194bafb --- /dev/null +++ b/binding/android/KoalaTestApp/koala-test-app/src/main/AndroidManifest.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + diff --git a/binding/android/KoalaTestApp/koala-test-app/src/main/ic_launcher-playstore.png b/binding/android/KoalaTestApp/koala-test-app/src/main/ic_launcher-playstore.png new file mode 100644 index 0000000..0e7d9f6 Binary files /dev/null and b/binding/android/KoalaTestApp/koala-test-app/src/main/ic_launcher-playstore.png differ diff --git a/binding/android/KoalaTestApp/koala-test-app/src/main/java/ai/picovoice/koala/testapp/MainActivity.java b/binding/android/KoalaTestApp/koala-test-app/src/main/java/ai/picovoice/koala/testapp/MainActivity.java new file mode 100644 index 0000000..837d5f7 --- /dev/null +++ b/binding/android/KoalaTestApp/koala-test-app/src/main/java/ai/picovoice/koala/testapp/MainActivity.java @@ -0,0 +1,221 @@ +/* + Copyright 2023 Picovoice Inc. + + You may not use this file except in compliance with the license. A copy of the license is + located in the "LICENSE" file accompanying this source. + + 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 ai.picovoice.koala.testapp; + +import android.os.Bundle; +import android.view.View; +import android.widget.Button; +import android.widget.ListView; +import android.widget.SimpleAdapter; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.content.ContextCompat; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.ArrayList; +import java.util.HashMap; + +import ai.picovoice.koala.Koala; +import ai.picovoice.koala.KoalaException; + +public class MainActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + } + + @Override + protected void onStop() { + super.onStop(); + } + + public void startTest(View view) { + Button testButton = findViewById(R.id.testButton); + testButton.setBackground(ContextCompat.getDrawable( + getApplicationContext(), + R.drawable.button_disabled)); + runTest(); + + testButton.setBackground(ContextCompat.getDrawable( + getApplicationContext(), + R.drawable.button_background)); + } + + public void runTest() { + String accessKey = getApplicationContext().getString(R.string.pvTestingAccessKey); + + ArrayList results = new ArrayList<>(); + + String modelFile = getModelFile(); + + TestResult result = new TestResult(); + result.testName = "Test Init"; + Koala koala = null; + try { + koala = new Koala.Builder() + .setAccessKey(accessKey) + .setModelPath(modelFile) + .build(getApplicationContext()); + result.success = true; + } catch (KoalaException e) { + result.success = false; + result.errorMessage = String.format("Failed to init koala with '%s'", e); + } finally { + results.add(result); + } + + result = new TestResult(); + result.testName = "Test Process"; + try { + String audioPath = "audio_samples/noise.wav"; + + ArrayList processResult = processTestAudio(koala, audioPath); + if (processResult.size() > 0) { + result.success = true; + } else { + result.success = false; + result.errorMessage = "Process returned invalid result."; + } + } catch (Exception e) { + result.success = false; + result.errorMessage = String.format("Failed to process with '%s'", e); + } finally { + results.add(result); + } + + result = new TestResult(); + result.testName = "Test Exception"; + try { + new Koala.Builder() + .setAccessKey("") + .setModelPath(modelFile) + .build(getApplicationContext()); + result.success = false; + result.errorMessage = "Init should have throw an exception"; + } catch (KoalaException e) { + result.success = true; + } finally { + results.add(result); + } + + displayTestResults(results); + } + + private void displayTestResults(ArrayList results) { + ListView resultList = findViewById(R.id.resultList); + + int passed = 0; + int failed = 0; + + ArrayList> list = new ArrayList<>(); + for (TestResult result : results) { + HashMap map = new HashMap<>(); + map.put("testName", result.testName); + + String message; + if (result.success) { + message = "Test Passed"; + passed += 1; + } else { + message = String.format("Test Failed: %s", result.errorMessage); + failed += 1; + } + + map.put("testMessage", message); + list.add(map); + } + + SimpleAdapter adapter = new SimpleAdapter( + getApplicationContext(), + list, + R.layout.list_view, + new String[]{"testName", "testMessage"}, + new int[]{R.id.testName, R.id.testMessage}); + + resultList.setAdapter(adapter); + + TextView passedView = findViewById(R.id.testNumPassed); + TextView failedView = findViewById(R.id.testNumFailed); + + passedView.setText(String.valueOf(passed)); + failedView.setText(String.valueOf(failed)); + + TextView resultView = findViewById(R.id.testResult); + if (passed == 0 || failed > 0) { + resultView.setText("Failed"); + } else { + resultView.setText("Passed"); + } + } + + private String getModelFile() { + return "models/koala_params.pv"; + } + + private ArrayList processTestAudio(@NonNull Koala k, String audioPath) throws Exception { + File testAudio = new File(getApplicationContext().getFilesDir(), audioPath); + + if (!testAudio.exists()) { + testAudio.getParentFile().mkdirs(); + extractFile(audioPath); + } + + FileInputStream audioInputStream = new FileInputStream(testAudio); + + byte[] rawData = new byte[k.getFrameLength() * 2]; + short[] pcm = new short[k.getFrameLength()]; + ByteBuffer pcmBuff = ByteBuffer.wrap(rawData).order(ByteOrder.LITTLE_ENDIAN); + + audioInputStream.skip(44); + + ArrayList results = new ArrayList<>(); + while (audioInputStream.available() > 0) { + int numRead = audioInputStream.read(pcmBuff.array()); + if (numRead == k.getFrameLength() * 2) { + pcmBuff.asShortBuffer().get(pcm); + results.add(k.process(pcm)); + } + } + + return results; + } + + private void extractFile(String filepath) throws IOException { + System.out.println(filepath); + InputStream is = new BufferedInputStream(getAssets().open(filepath), 256); + File absPath = new File(getApplicationContext().getFilesDir(), filepath); + OutputStream os = new BufferedOutputStream(new FileOutputStream(absPath), 256); + int r; + while ((r = is.read()) != -1) { + os.write(r); + } + os.flush(); + + is.close(); + os.close(); + } +} diff --git a/binding/android/KoalaTestApp/koala-test-app/src/main/java/ai/picovoice/koala/testapp/TestResult.java b/binding/android/KoalaTestApp/koala-test-app/src/main/java/ai/picovoice/koala/testapp/TestResult.java new file mode 100644 index 0000000..0526aa7 --- /dev/null +++ b/binding/android/KoalaTestApp/koala-test-app/src/main/java/ai/picovoice/koala/testapp/TestResult.java @@ -0,0 +1,7 @@ +package ai.picovoice.koala.testapp; + +public class TestResult { + public String testName; + public boolean success; + public String errorMessage; +} diff --git a/binding/android/KoalaTestApp/koala-test-app/src/main/res/drawable/button_background.xml b/binding/android/KoalaTestApp/koala-test-app/src/main/res/drawable/button_background.xml new file mode 100644 index 0000000..7e73f1a --- /dev/null +++ b/binding/android/KoalaTestApp/koala-test-app/src/main/res/drawable/button_background.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/binding/android/KoalaTestApp/koala-test-app/src/main/res/drawable/button_disabled.xml b/binding/android/KoalaTestApp/koala-test-app/src/main/res/drawable/button_disabled.xml new file mode 100644 index 0000000..ffe1c93 --- /dev/null +++ b/binding/android/KoalaTestApp/koala-test-app/src/main/res/drawable/button_disabled.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/binding/android/KoalaTestApp/koala-test-app/src/main/res/drawable/ic_launcher_background.xml b/binding/android/KoalaTestApp/koala-test-app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..196e181 --- /dev/null +++ b/binding/android/KoalaTestApp/koala-test-app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,10 @@ + + + + diff --git a/binding/android/KoalaTestApp/koala-test-app/src/main/res/drawable/ic_launcher_foreground.xml b/binding/android/KoalaTestApp/koala-test-app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..d2923ae --- /dev/null +++ b/binding/android/KoalaTestApp/koala-test-app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,19 @@ + + + + + + diff --git a/binding/android/KoalaTestApp/koala-test-app/src/main/res/layout/activity_main.xml b/binding/android/KoalaTestApp/koala-test-app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..5737006 --- /dev/null +++ b/binding/android/KoalaTestApp/koala-test-app/src/main/res/layout/activity_main.xml @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +