diff --git a/.gitignore b/.gitignore index d04b274..1ffd713 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,4 @@ .cxx local.properties -gradle.properties +apikeys.properties diff --git a/README.md b/README.md new file mode 100644 index 0000000..8a29d56 --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ + +# How Long To Beat Demo App + + +## Building + +### Create apikeys.properties + +Add the following to a new file called `apikeys.properties` (in the root folder): + +``` +GoogleVisionApiKey="yourkey" +``` + +Your Google Vision API Key can be fetched through [console.cloud.google.com](https://console.cloud.google.com). + +The key `GoogleVisionApiKey` is used in `build.gradle.kts` of the app and auto-generates the correct string in the class `BuildConfig`, ready to be used in the Kotlin/Java code. See The app Gradle build file for more information. \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index bff1e36..6cfa2f9 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -5,6 +5,11 @@ plugins { kotlin("plugin.serialization") version "1.5.21" } +val apiKeys = file("../apikeys.properties").readLines().map { + val keyvalues = it.split("=") + keyvalues[0] to keyvalues[1] +}.toMap() + android { compileSdk = 31 buildToolsVersion = "31.0.0" @@ -28,6 +33,13 @@ android { ) } } + + buildTypes.forEach { + // Will crash and do weird things if you did not provide your key. + // See README.md for more information. + it.buildConfigField("String", "GOOGLE_VISION_API_KEY", apiKeys["GoogleVisionApiKey"]!!) + } + compileOptions { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 @@ -58,7 +70,6 @@ dependencies { testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.5.1") // --- navigation - // see https://developer.android.com/guide/navigation/navigation-getting-started val nav_version = "2.3.5" implementation("androidx.navigation:navigation-fragment:$nav_version") implementation("androidx.navigation:navigation-ui:$nav_version") @@ -79,16 +90,7 @@ dependencies { // --- Volley for HTTP requests implementation("com.android.volley:volley:1.2.0") - // --- Google Vision API specific dependencies - // firebase specific - //implementation("com.google.firebase:firebase-bom:28.3.1") - - // OAUth play services - //implementation("com.google.android.gms:play-services-auth:19.2.0") - //implementation("com.google.android.gms:play-services-base:17.6.0") - - - // Vision itself + // Google Vision // comes with conflicts, exclude http client using https://docs.gradle.org/current/userguide/dependency_downgrade_and_exclude.html implementation("com.google.api-client:google-api-client-android:1.32.1") { exclude(module = "httpclient") diff --git a/app/src/main/java/be/kuleuven/howlongtobeat/google/GoogleVisionClient.kt b/app/src/main/java/be/kuleuven/howlongtobeat/google/GoogleVisionClient.kt index e71f39b..86b2e28 100644 --- a/app/src/main/java/be/kuleuven/howlongtobeat/google/GoogleVisionClient.kt +++ b/app/src/main/java/be/kuleuven/howlongtobeat/google/GoogleVisionClient.kt @@ -1,6 +1,7 @@ package be.kuleuven.howlongtobeat.google import android.graphics.Bitmap +import be.kuleuven.howlongtobeat.BuildConfig import be.kuleuven.howlongtobeat.ImageRecognizer import be.kuleuven.howlongtobeat.asEncodedGoogleVisionImage import be.kuleuven.howlongtobeat.cartridges.Cartridge @@ -17,13 +18,9 @@ import kotlinx.coroutines.withContext class GoogleVisionClient : ImageRecognizer { - // TODO encrypt and store externally: https://cloud.google.com/docs/authentication/api-keys?hl=en&visit_id=637642790375688006-1838986332&rd=1 - private val vision = Vision.Builder(NetHttpTransport(), GsonFactory.getDefaultInstance(), null) - .setVisionRequestInitializer(VisionRequestInitializer("AIzaSyCaMjQQOY7508y95riDhr25fsrqe3m2JW0")) - .setApplicationName("How Long To Beat") - .build() - private suspend fun findCartCodeViaGoogleVision(cameraSnap: Bitmap): String? { + val vision = buildVisionClient() + var response: BatchAnnotateImagesResponse withContext(Dispatchers.IO) { val sml2Data = cameraSnap.asEncodedGoogleVisionImage() @@ -50,7 +47,16 @@ class GoogleVisionClient : ImageRecognizer { val gbId = response.responses.get(0).textAnnotations.filter { Cartridge.isValid(it.description) }.firstOrNull() - return gbId?.description ?: null + return gbId?.description + } + + private fun buildVisionClient(): Vision { + assert(BuildConfig.GOOGLE_VISION_API_KEY.length > 1) + + return Vision.Builder(NetHttpTransport(), GsonFactory.getDefaultInstance(), null) + .setVisionRequestInitializer(VisionRequestInitializer(BuildConfig.GOOGLE_VISION_API_KEY)) + .setApplicationName("How Long To Beat") + .build() } override suspend fun recognizeCartCode(image: Bitmap): String? = findCartCodeViaGoogleVision(image) diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..cac7c68 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,21 @@ +# 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 +# Automatically convert third-party libraries to use AndroidX +android.enableJetifier=true +# Kotlin code style for this project: "official" or "obsolete": +kotlin.code.style=official