diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 7374a015..285b75e3 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -48,7 +48,7 @@ jobs: uses: stefanzweifel/git-auto-commit-action@v5 with: commit_message: 🤖 Apply Spotless formatting - file_pattern: '**/*.kt **/*.kts **/*.java **/*.xml' + file_pattern: '**/*.kt **/*.kts **/*.xml' - name: Build debug APK run: ./gradlew assembleDebug --no-configuration-cache diff --git a/app/build.gradle.kts b/app/build.gradle.kts index cb0cef6c..c111dba6 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -13,14 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + plugins { alias(libs.plugins.android.application) - alias(libs.plugins.jetbrains.kotlin.android) - alias(libs.plugins.jetbrains.kotlin.serialization) + alias(libs.plugins.compose.compiler) alias(libs.plugins.google.gms.google.services) alias(libs.plugins.hilt.plugin) + alias(libs.plugins.jetbrains.kotlin.android) + alias(libs.plugins.jetbrains.kotlin.serialization) alias(libs.plugins.ksp) - alias(libs.plugins.compose.compiler) } android { @@ -42,19 +45,25 @@ android { buildTypes { release { - isMinifyEnabled = false + isMinifyEnabled = true + isShrinkResources = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro", ) } + create("benchmark") { + initWith(buildTypes.getByName("release")) + signingConfig = signingConfigs.getByName("debug") + matchingFallbacks += listOf("release") + isDebuggable = false + } } compileOptions { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } - kotlinOptions { - jvmTarget = "17" + kotlin { + compilerOptions { jvmTarget.set(JvmTarget.JVM_17) } } buildFeatures { compose = true @@ -62,43 +71,40 @@ android { } dependencies { - + androidTestImplementation(libs.androidx.espresso.core) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.ui.test.junit4) + androidTestImplementation(platform(libs.androidx.compose.bom)) + debugImplementation(libs.androidx.ui.test.manifest) + debugImplementation(libs.androidx.ui.tooling) + implementation(libs.androidx.activity.compose) implementation(libs.androidx.core.ktx) implementation(libs.androidx.lifecycle.runtime.ktx) - implementation(libs.androidx.activity.compose) - implementation(platform(libs.androidx.compose.bom)) - implementation(libs.androidx.ui) - implementation(libs.androidx.ui.graphics) - implementation(libs.androidx.ui.tooling.preview) implementation(libs.androidx.material3) implementation(libs.androidx.navigation.compose) - implementation(libs.kotlinx.serialization.json) implementation(libs.androidx.navigation.runtime.ktx) + implementation(libs.androidx.ui) + implementation(libs.androidx.ui.graphics) + implementation(libs.androidx.ui.tooling.preview) + implementation(libs.firebase.ai) implementation(libs.hilt.android) implementation(libs.hilt.navigation.compose) + implementation(libs.kotlinx.serialization.json) + implementation(platform(libs.androidx.compose.bom)) implementation(platform(libs.firebase.bom)) - implementation(libs.firebase.ai) - ksp(libs.hilt.compiler) - - implementation(project(":ui-component")) - implementation(project(":samples:gemini-multimodal")) implementation(project(":samples:gemini-chatbot")) - implementation(project(":samples:genai-summarization")) + implementation(project(":samples:gemini-image-chat")) + implementation(project(":samples:gemini-live-todo")) + implementation(project(":samples:gemini-multimodal")) + implementation(project(":samples:gemini-video-metadata-creation")) + implementation(project(":samples:gemini-video-summarization")) implementation(project(":samples:genai-image-description")) + implementation(project(":samples:genai-summarization")) implementation(project(":samples:genai-writing-assistance")) implementation(project(":samples:imagen")) implementation(project(":samples:imagen-editing")) implementation(project(":samples:magic-selfie")) - implementation(project(":samples:gemini-video-summarization")) - implementation(project(":samples:gemini-live-todo")) - implementation(project(":samples:gemini-video-metadata-creation")) - implementation(project(":samples:gemini-image-chat")) - + implementation(project(":ui-component")) + ksp(libs.hilt.compiler) testImplementation(libs.junit) - androidTestImplementation(libs.androidx.junit) - androidTestImplementation(libs.androidx.espresso.core) - androidTestImplementation(platform(libs.androidx.compose.bom)) - androidTestImplementation(libs.androidx.ui.test.junit4) - debugImplementation(libs.androidx.ui.tooling) - debugImplementation(libs.androidx.ui.test.manifest) } diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro deleted file mode 100644 index 481bb434..00000000 --- a/app/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# 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 \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2db16161..60e7f7a4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -5,27 +5,31 @@ + tools:targetApi="31"> + + + android:theme="@style/Theme.AISampleCatalog" + android:windowSoftInputMode="adjustResize"> + Unit, val tags: List = emptyList(), val needsFirebase: Boolean = false, val isFeatured: Boolean = false, - @DrawableRes val keyArt: Int? = null, + @param:DrawableRes val keyArt: Int? = null, ) enum class SampleTags( diff --git a/app/src/main/java/com/android/ai/catalog/ui/CatalogApp.kt b/app/src/main/kotlin/com/android/ai/catalog/ui/CatalogApp.kt similarity index 100% rename from app/src/main/java/com/android/ai/catalog/ui/CatalogApp.kt rename to app/src/main/kotlin/com/android/ai/catalog/ui/CatalogApp.kt diff --git a/app/src/main/java/com/android/ai/catalog/ui/CatalogRowCard.kt b/app/src/main/kotlin/com/android/ai/catalog/ui/CatalogRowCard.kt similarity index 100% rename from app/src/main/java/com/android/ai/catalog/ui/CatalogRowCard.kt rename to app/src/main/kotlin/com/android/ai/catalog/ui/CatalogRowCard.kt diff --git a/app/src/main/java/com/android/ai/catalog/ui/CatalogWideCard.kt b/app/src/main/kotlin/com/android/ai/catalog/ui/CatalogWideCard.kt similarity index 100% rename from app/src/main/java/com/android/ai/catalog/ui/CatalogWideCard.kt rename to app/src/main/kotlin/com/android/ai/catalog/ui/CatalogWideCard.kt diff --git a/app/src/main/res/drawable-xhdpi/img_keyart_chatbot.png b/app/src/main/res/drawable-xhdpi/img_keyart_chatbot.png deleted file mode 100644 index e69de29b..00000000 diff --git a/app/src/main/res/drawable-xhdpi/img_keyart_imagen.png b/app/src/main/res/drawable-xhdpi/img_keyart_imagen.png deleted file mode 100644 index e69de29b..00000000 diff --git a/app/src/main/res/drawable-xhdpi/img_keyart_img_desc.png b/app/src/main/res/drawable-xhdpi/img_keyart_img_desc.png deleted file mode 100644 index e69de29b..00000000 diff --git a/app/src/main/res/drawable-xhdpi/img_keyart_magic_selfie.png b/app/src/main/res/drawable-xhdpi/img_keyart_magic_selfie.png deleted file mode 100644 index e69de29b..00000000 diff --git a/app/src/main/res/drawable-xhdpi/img_keyart_metadata.png b/app/src/main/res/drawable-xhdpi/img_keyart_metadata.png deleted file mode 100644 index e69de29b..00000000 diff --git a/app/src/main/res/drawable-xhdpi/img_keyart_multimodal.png b/app/src/main/res/drawable-xhdpi/img_keyart_multimodal.png deleted file mode 100644 index e69de29b..00000000 diff --git a/app/src/main/res/drawable-xhdpi/img_keyart_summary.png b/app/src/main/res/drawable-xhdpi/img_keyart_summary.png deleted file mode 100644 index e69de29b..00000000 diff --git a/app/src/main/res/drawable-xhdpi/img_keyart_text.png b/app/src/main/res/drawable-xhdpi/img_keyart_text.png deleted file mode 100644 index e69de29b..00000000 diff --git a/app/src/main/res/drawable-xhdpi/img_keyart_todo.png b/app/src/main/res/drawable-xhdpi/img_keyart_todo.png deleted file mode 100644 index e69de29b..00000000 diff --git a/app/src/main/res/drawable-xhdpi/img_keyart_video_summary.png b/app/src/main/res/drawable-xhdpi/img_keyart_video_summary.png deleted file mode 100644 index e69de29b..00000000 diff --git a/benchmark/build.gradle.kts b/benchmark/build.gradle.kts new file mode 100644 index 00000000..278c1017 --- /dev/null +++ b/benchmark/build.gradle.kts @@ -0,0 +1,85 @@ +/* + * Copyright 2026 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + +/* + * Copyright 2026 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +plugins { + alias(libs.plugins.android.test) + alias(libs.plugins.jetbrains.kotlin.android) +} + +android { + namespace = "com.android.ai.catalog.benchmark" + compileSdk { + version = release(36) + } + + defaultConfig { + minSdk = 24 + targetSdk = 36 + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + // This benchmark buildType is used for benchmarking, and should function like your + // release build (for example, with minification on). It"s signed with a debug key + // for easy local/CI testing. + create("benchmark") { + isDebuggable = true + signingConfig = getByName("debug").signingConfig + matchingFallbacks += listOf("release") + } + } + + targetProjectPath = ":app" + experimentalProperties["android.experimental.self-instrumenting"] = true + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + kotlin { + compilerOptions { jvmTarget.set(JvmTarget.JVM_17) } + } +} + +dependencies { + implementation(libs.androidx.junit) + implementation(libs.androidx.espresso.core) + implementation(libs.androidx.uiautomator) + implementation(libs.androidx.benchmark.macro.junit4) +} + +androidComponents { + beforeVariants(selector().all()) { + it.enable = it.buildType == "benchmark" + } +} diff --git a/benchmark/src/main/AndroidManifest.xml b/benchmark/src/main/AndroidManifest.xml new file mode 100644 index 00000000..227314ee --- /dev/null +++ b/benchmark/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/benchmark/src/main/kotlin/com/android/ai/catalog/benchmark/StartupBenchmark.kt b/benchmark/src/main/kotlin/com/android/ai/catalog/benchmark/StartupBenchmark.kt new file mode 100644 index 00000000..ed0220ba --- /dev/null +++ b/benchmark/src/main/kotlin/com/android/ai/catalog/benchmark/StartupBenchmark.kt @@ -0,0 +1,52 @@ +/* + * Copyright 2026 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.ai.catalog.benchmark + +import androidx.benchmark.macro.StartupMode +import androidx.benchmark.macro.StartupTimingMetric +import androidx.benchmark.macro.junit4.MacrobenchmarkRule +import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Benchmark class to measure the startup timing of the application. + * + * This benchmark uses [MacrobenchmarkRule] to measure the time it takes for the application + * to start up in [StartupMode.COLD] mode. + */ +@RunWith(AndroidJUnit4::class) +class StartupBenchmark { + @get:Rule + val benchmarkRule = MacrobenchmarkRule() + + /** + * Measures the application startup time. + * + * It runs the benchmark for 5 iterations using [StartupTimingMetric]. + * The application is started in [StartupMode.COLD] mode, ensuring a fresh start for each iteration. + */ + @Test + fun startup() = benchmarkRule.measureRepeated( + packageName = "com.android.ai.catalog", + metrics = listOf(StartupTimingMetric()), + iterations = 5, + startupMode = StartupMode.COLD, + ) { + startApp(packageName) + } +} diff --git a/build.gradle.kts b/build.gradle.kts index dd6161b7..ff7dd185 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -25,6 +25,7 @@ plugins { alias(libs.plugins.ksp) apply false alias(libs.plugins.compose.compiler) apply false alias(libs.plugins.spotless) apply false + alias(libs.plugins.android.test) apply false } subprojects { @@ -43,5 +44,10 @@ subprojects { // Look for the first line that doesn't have a block comment (assumed to be the license) licenseHeaderFile(rootProject.file("spotless/copyright.kt"), "(^(?![\\/ ]\\*).*$)") } + format("toml") { + target("gradle/libs.versions.toml") + prettier(mapOf("prettier" to "3.2.5", "prettier-plugin-toml" to "2.0.1")) + .config(mapOf("plugins" to listOf("prettier-plugin-toml"))) + } } -} \ No newline at end of file +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 19f57495..4b016b99 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,97 +1,102 @@ [versions] -agp = "8.8.2" -coilCompose = "3.1.0" -firebaseBom = "34.5.0" -lifecycleRuntimeCompose = "2.9.1" -mlkitGenAi = "1.0.0-beta1" -kotlin = "2.1.0" -coreKtx = "1.15.0" +activityCompose = "1.12.2" +agp = "8.13.2" +appcompat = "1.7.1" +benchmarkMacroJunit4 = "1.5.0-alpha01" +coilCompose = "3.3.0" +composeBom = "2025.12.01" +coreKtx = "1.17.0" +espressoCore = "3.7.0" +exifinterface = "1.4.2" +firebaseBom = "34.7.0" +firebaseCommonKtx = "21.0.0" +googleGmsGoogleServices = "4.4.4" +hilt = "2.57.2" +hiltNavigationCompose = "1.3.0" junit = "4.13.2" -junitVersion = "1.2.1" -espressoCore = "3.6.1" +junitVersion = "1.3.0" +kotlin = "2.2.0" kotlinxCoroutinesGuava = "1.10.2" -kotlinxSerializationJson = "1.6.2" -lifecycleRuntimeKtx = "2.8.7" -activityCompose = "1.10.1" -composeBom = "2025.06.01" -navigationCompose = "2.9.0" -navigationRuntimeKtx = "2.9.0" -appcompat = "1.7.0" -googleGmsGoogleServices = "4.4.2" -hilt = "2.56.2" -hiltNavigationCompose = "1.2.0" -ksp = "2.1.0-1.0.29" +kotlinxSerializationJson = "1.9.0" +ksp = "2.2.0-2.0.2" +lifecycleRuntimeCompose = "2.10.0" +lifecycleRuntimeKtx = "2.10.0" +lifecycleViewmodelAndroid = "2.10.0" +material3 = "1.5.0-alpha11" +material3WindowSizeClass = "1.4.0" +media3 = "1.9.0" +mlkitGenAi = "1.0.0-beta1" mlkitSegmentation = "16.0.0-beta1" -runtimeLivedata = "1.7.6" -media3 = "1.8.0" -firebaseCommonKtx = "21.0.0" -uiToolingPreviewAndroid = "1.8.1" -spotless = "7.0.4" -uiToolingPreview = "1.8.3" -uiTooling = "1.8.3" -lifecycleViewmodelAndroid = "2.8.7" -material3 = "1.5.0-alpha01" -uiTextGoogleFonts = "1.8.1" -exifinterface = "1.4.1" -material3WindowSizeClass = "1.3.2" -richtext = "1.0.0-alpha02" +navigationCompose = "2.9.6" +navigationRuntimeKtx = "2.9.6" +richtext = "1.0.0-alpha03" +runtimeLivedata = "1.10.0" +spotless = "8.0.0" +uiTextGoogleFonts = "1.10.0" +uiTooling = "1.10.0" +uiToolingPreview = "1.10.0" +uiToolingPreviewAndroid = "1.10.0" +uiautomator = "2.4.0-alpha07" [libraries] +androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" } +androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } +androidx-benchmark-macro-junit4 = { group = "androidx.benchmark", name = "benchmark-macro-junit4", version.ref = "benchmarkMacroJunit4" } +androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } +androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } +androidx-exifinterface = { group = "androidx.exifinterface", name = "exifinterface", version.ref = "exifinterface" } +androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } androidx-lifecycle-runtime-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "lifecycleRuntimeCompose" } +androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" } +androidx-lifecycle-viewmodel-android = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-android", version.ref = "lifecycleViewmodelAndroid" } +androidx-material-icons-extended = { module = "androidx.compose.material:material-icons-extended" } +androidx-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" } +androidx-material3-window = { group = "androidx.compose.material3", name = "material3-window-size-class", version.ref = "material3WindowSizeClass" } +androidx-media3-exoplayer = { module = "androidx.media3:media3-exoplayer", version.ref = "media3" } +androidx-media3-transformer = { module = "androidx.media3:media3-transformer", version.ref = "media3" } +androidx-media3-ui = { module = "androidx.media3:media3-ui", version.ref = "media3" } +androidx-media3-ui-compose = { module = "androidx.media3:media3-ui-compose", version.ref = "media3"} +androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" } +androidx-navigation-runtime-ktx = { group = "androidx.navigation", name = "navigation-runtime-ktx", version.ref = "navigationRuntimeKtx" } +androidx-runtime-livedata = { group = "androidx.compose.runtime", name = "runtime-livedata", version.ref = "runtimeLivedata" } +androidx-ui = { group = "androidx.compose.ui", name = "ui" } +androidx-ui-google-fonts = { group = "androidx.compose.ui", name = "ui-text-google-fonts", version.ref = "uiTextGoogleFonts" } +androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" } +androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } +androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } +androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" } +androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" } +androidx-ui-tooling-preview-android = { group = "androidx.compose.ui", name = "ui-tooling-preview-android", version.ref = "uiToolingPreviewAndroid" } +androidx-uiautomator = { group = "androidx.test.uiautomator", name = "uiautomator", version.ref = "uiautomator" } coil-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coilCompose" } -firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebaseBom" } firebase-ai = { group = "com.google.firebase", name = "firebase-ai" } +firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebaseBom" } firebase-common-ktx = { group = "com.google.firebase", name = "firebase-common-ktx", version.ref = "firebaseCommonKtx" } genai-image-description = { module = "com.google.mlkit:genai-image-description", version.ref = "mlkitGenAi" } genai-proofreading = { module = "com.google.mlkit:genai-proofreading", version.ref = "mlkitGenAi" } genai-rewrite = { module = "com.google.mlkit:genai-rewriting", version.ref = "mlkitGenAi" } genai-summarization = { module = "com.google.mlkit:genai-summarization", version.ref = "mlkitGenAi" } -junit = { group = "junit", name = "junit", version.ref = "junit" } -androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } -androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } -androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" } -androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" } -androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" } -androidx-ui = { group = "androidx.compose.ui", name = "ui" } -androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" } -androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" } -androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" } -androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } -androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } -androidx-ui-google-fonts = { group = "androidx.compose.ui", name = "ui-text-google-fonts", version.ref = "uiTextGoogleFonts" } -androidx-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" } -androidx-material3-window = { group = "androidx.compose.material3", name = "material3-window-size-class", version.ref = "material3WindowSizeClass" } -richtext-material3 = { group = "com.halilibo.compose-richtext", name = "richtext-ui-material3", version.ref = "richtext" } -richtext-commonmark = { group = "com.halilibo.compose-richtext", name = "richtext-commonmark", version.ref = "richtext" } -androidx-material-icons-extended = { module = "androidx.compose.material:material-icons-extended" } -androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" } -androidx-navigation-runtime-ktx = { group = "androidx.navigation", name = "navigation-runtime-ktx", version.ref = "navigationRuntimeKtx" } -kotlinx-coroutines-guava = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-guava", version.ref = "kotlinxCoroutinesGuava" } -kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" } -androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt"} hilt-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hilt"} hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "hiltNavigationCompose" } -androidx-runtime-livedata = { group = "androidx.compose.runtime", name = "runtime-livedata", version.ref = "runtimeLivedata" } -androidx-media3-exoplayer = { module = "androidx.media3:media3-exoplayer", version.ref = "media3" } -androidx-media3-ui = { module = "androidx.media3:media3-ui", version.ref = "media3" } -androidx-media3-ui-compose = { module = "androidx.media3:media3-ui-compose", version.ref = "media3"} -androidx-media3-transformer = { module = "androidx.media3:media3-transformer", version.ref = "media3" } -androidx-ui-tooling-preview-android = { group = "androidx.compose.ui", name = "ui-tooling-preview-android", version.ref = "uiToolingPreviewAndroid" } +junit = { group = "junit", name = "junit", version.ref = "junit" } +kotlinx-coroutines-guava = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-guava", version.ref = "kotlinxCoroutinesGuava" } +kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" } mlkit-segmentation = { module = "com.google.android.gms:play-services-mlkit-subject-segmentation", version.ref = "mlkitSegmentation" } -ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview", version.ref = "uiToolingPreview" } +richtext-commonmark = { group = "com.halilibo.compose-richtext", name = "richtext-commonmark", version.ref = "richtext" } +richtext-material3 = { group = "com.halilibo.compose-richtext", name = "richtext-ui-material3", version.ref = "richtext" } ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling", version.ref = "uiTooling" } -androidx-lifecycle-viewmodel-android = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-android", version.ref = "lifecycleViewmodelAndroid" } -androidx-exifinterface = { group = "androidx.exifinterface", name = "exifinterface", version.ref = "exifinterface" } +ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview", version.ref = "uiToolingPreview" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" } -jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } -jetbrains-kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } android-library = { id = "com.android.library", version.ref = "agp" } +android-test = { id = "com.android.test", version.ref = "agp" } +compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } google-gms-google-services = { id = "com.google.gms.google-services", version.ref = "googleGmsGoogleServices" } hilt-plugin = { id = "com.google.dagger.hilt.android", version.ref = "hilt"} +jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } +jetbrains-kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } -compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } -spotless = { id = "com.diffplug.spotless", version.ref = "spotless" } \ No newline at end of file +spotless = { id = "com.diffplug.spotless", version.ref = "spotless" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index cea7a793..d4081da4 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/samples/gemini-chatbot/build.gradle.kts b/samples/gemini-chatbot/build.gradle.kts index 517f68c0..ef30fb9f 100644 --- a/samples/gemini-chatbot/build.gradle.kts +++ b/samples/gemini-chatbot/build.gradle.kts @@ -14,16 +14,18 @@ * limitations under the License. */ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + plugins { alias(libs.plugins.android.library) + alias(libs.plugins.compose.compiler) alias(libs.plugins.jetbrains.kotlin.android) alias(libs.plugins.ksp) - alias(libs.plugins.compose.compiler) } android { namespace = "com.android.ai.samples.geminichatbot" - compileSdk = 35 + compileSdk = 36 buildFeatures { compose = true @@ -33,47 +35,34 @@ android { minSdk = 24 testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles("consumer-rules.pro") } - buildTypes { - release { - isMinifyEnabled = false - proguardFiles( - getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro", - ) - } - } compileOptions { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } - kotlinOptions { - jvmTarget = "17" + kotlin { + compilerOptions { jvmTarget.set(JvmTarget.JVM_17) } } } dependencies { - - implementation(libs.androidx.core.ktx) + androidTestImplementation(libs.androidx.espresso.core) + androidTestImplementation(libs.androidx.junit) + debugImplementation(libs.ui.tooling) implementation(libs.androidx.appcompat) - implementation(platform(libs.androidx.compose.bom)) + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.lifecycle.runtime.compose) implementation(libs.androidx.material.icons.extended) implementation(libs.androidx.material3) - implementation(platform(libs.firebase.bom)) + implementation(libs.androidx.runtime.livedata) implementation(libs.firebase.ai) implementation(libs.hilt.android) implementation(libs.hilt.navigation.compose) - implementation(libs.androidx.runtime.livedata) - implementation(libs.androidx.lifecycle.runtime.compose) implementation(libs.ui.tooling.preview) - debugImplementation(libs.ui.tooling) - ksp(libs.hilt.compiler) - + implementation(platform(libs.androidx.compose.bom)) + implementation(platform(libs.firebase.bom)) implementation(project(":ui-component")) - + ksp(libs.hilt.compiler) testImplementation(libs.junit) - androidTestImplementation(libs.androidx.junit) - androidTestImplementation(libs.androidx.espresso.core) } diff --git a/samples/gemini-chatbot/consumer-rules.pro b/samples/gemini-chatbot/consumer-rules.pro deleted file mode 100644 index e69de29b..00000000 diff --git a/samples/gemini-chatbot/proguard-rules.pro b/samples/gemini-chatbot/proguard-rules.pro deleted file mode 100644 index 481bb434..00000000 --- a/samples/gemini-chatbot/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# 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 \ No newline at end of file diff --git a/samples/gemini-chatbot/src/main/java/com/android/ai/samples/geminichatbot/GeminiChatbotScreen.kt b/samples/gemini-chatbot/src/main/kotlin/com/android/ai/samples/geminichatbot/GeminiChatbotScreen.kt similarity index 98% rename from samples/gemini-chatbot/src/main/java/com/android/ai/samples/geminichatbot/GeminiChatbotScreen.kt rename to samples/gemini-chatbot/src/main/kotlin/com/android/ai/samples/geminichatbot/GeminiChatbotScreen.kt index 9055f74a..b192d508 100644 --- a/samples/gemini-chatbot/src/main/java/com/android/ai/samples/geminichatbot/GeminiChatbotScreen.kt +++ b/samples/gemini-chatbot/src/main/kotlin/com/android/ai/samples/geminichatbot/GeminiChatbotScreen.kt @@ -47,7 +47,7 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewScreenSizes import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel +import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.ai.theme.AISampleCatalogTheme import com.android.ai.uicomponent.ChatMessage @@ -56,7 +56,6 @@ import com.android.ai.uicomponent.MessageList import com.android.ai.uicomponent.SampleDetailTopAppBar import com.android.ai.uicomponent.TextInput -@OptIn(ExperimentalMaterial3Api::class) @Composable fun GeminiChatbotScreen(viewModel: GeminiChatbotViewModel = hiltViewModel()) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() @@ -163,7 +162,6 @@ private fun GeminiChatbotScreen(uiState: GeminiChatbotUiState, onSendMessage: (S @PreviewScreenSizes @Composable -@OptIn(ExperimentalMaterial3Api::class) private fun GeminiChatbotScreenPreview() { AISampleCatalogTheme { GeminiChatbotScreen( diff --git a/samples/gemini-chatbot/src/main/java/com/android/ai/samples/geminichatbot/GeminiChatbotViewModel.kt b/samples/gemini-chatbot/src/main/kotlin/com/android/ai/samples/geminichatbot/GeminiChatbotViewModel.kt similarity index 100% rename from samples/gemini-chatbot/src/main/java/com/android/ai/samples/geminichatbot/GeminiChatbotViewModel.kt rename to samples/gemini-chatbot/src/main/kotlin/com/android/ai/samples/geminichatbot/GeminiChatbotViewModel.kt diff --git a/samples/gemini-image-chat/build.gradle.kts b/samples/gemini-image-chat/build.gradle.kts index dec2ae64..96481aa9 100644 --- a/samples/gemini-image-chat/build.gradle.kts +++ b/samples/gemini-image-chat/build.gradle.kts @@ -14,16 +14,18 @@ * limitations under the License. */ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + plugins { alias(libs.plugins.android.library) + alias(libs.plugins.compose.compiler) alias(libs.plugins.jetbrains.kotlin.android) alias(libs.plugins.ksp) - alias(libs.plugins.compose.compiler) } android { namespace = "com.android.ai.samples.geminiimagechat" - compileSdk = 35 + compileSdk = 36 buildFeatures { compose = true @@ -33,47 +35,35 @@ android { minSdk = 24 testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles("consumer-rules.pro") } - buildTypes { - release { - isMinifyEnabled = false - proguardFiles( - getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro", - ) - } - } compileOptions { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } - kotlinOptions { - jvmTarget = "17" + + kotlin { + compilerOptions { jvmTarget.set(JvmTarget.JVM_17) } } } dependencies { - - implementation(libs.androidx.core.ktx) + androidTestImplementation(libs.androidx.espresso.core) + androidTestImplementation(libs.androidx.junit) implementation(libs.androidx.appcompat) - implementation(platform(libs.androidx.compose.bom)) + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.exifinterface) + implementation(libs.androidx.lifecycle.runtime.compose) implementation(libs.androidx.material.icons.extended) - implementation(platform(libs.firebase.bom)) + implementation(libs.androidx.material3) + implementation(libs.coil.compose) implementation(libs.firebase.ai) implementation(libs.hilt.android) implementation(libs.hilt.navigation.compose) - implementation(libs.androidx.lifecycle.runtime.compose) - implementation(libs.androidx.material3) - implementation(libs.coil.compose) - implementation(libs.androidx.exifinterface) - ksp(libs.hilt.compiler) implementation(libs.ui.tooling.preview) - + implementation(platform(libs.androidx.compose.bom)) + implementation(platform(libs.firebase.bom)) implementation(project(":ui-component")) - + ksp(libs.hilt.compiler) testImplementation(libs.junit) - androidTestImplementation(libs.androidx.junit) - androidTestImplementation(libs.androidx.espresso.core) } diff --git a/samples/gemini-image-chat/consumer-rules.pro b/samples/gemini-image-chat/consumer-rules.pro deleted file mode 100644 index e69de29b..00000000 diff --git a/samples/gemini-image-chat/proguard-rules.pro b/samples/gemini-image-chat/proguard-rules.pro deleted file mode 100644 index 481bb434..00000000 --- a/samples/gemini-image-chat/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# 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 \ No newline at end of file diff --git a/samples/gemini-image-chat/src/main/java/com/android/ai/samples/geminiimagechat/GeminiImageChatScreen.kt b/samples/gemini-image-chat/src/main/kotlin/com/android/ai/samples/geminiimagechat/GeminiImageChatScreen.kt similarity index 98% rename from samples/gemini-image-chat/src/main/java/com/android/ai/samples/geminiimagechat/GeminiImageChatScreen.kt rename to samples/gemini-image-chat/src/main/kotlin/com/android/ai/samples/geminiimagechat/GeminiImageChatScreen.kt index 2df8f2c4..2a603e6d 100644 --- a/samples/gemini-image-chat/src/main/java/com/android/ai/samples/geminiimagechat/GeminiImageChatScreen.kt +++ b/samples/gemini-image-chat/src/main/kotlin/com/android/ai/samples/geminiimagechat/GeminiImageChatScreen.kt @@ -61,7 +61,7 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewScreenSizes import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel +import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import coil3.compose.AsyncImage import com.android.ai.samples.util.loadBitmapWithCorrectOrientation @@ -76,7 +76,6 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -@OptIn(ExperimentalMaterial3Api::class) @Composable fun GeminiImageChatScreen(viewModel: GeminiImageChatViewModel = hiltViewModel()) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() @@ -243,7 +242,6 @@ private fun GeminiImageChatScreen( @PreviewScreenSizes @Composable -@OptIn(ExperimentalMaterial3Api::class) private fun GeminiImageChatScreenPreview() { AISampleCatalogTheme { GeminiImageChatScreen( diff --git a/samples/gemini-image-chat/src/main/java/com/android/ai/samples/geminiimagechat/GeminiImageChatViewModel.kt b/samples/gemini-image-chat/src/main/kotlin/com/android/ai/samples/geminiimagechat/GeminiImageChatViewModel.kt similarity index 100% rename from samples/gemini-image-chat/src/main/java/com/android/ai/samples/geminiimagechat/GeminiImageChatViewModel.kt rename to samples/gemini-image-chat/src/main/kotlin/com/android/ai/samples/geminiimagechat/GeminiImageChatViewModel.kt diff --git a/samples/gemini-image-chat/src/main/java/com/android/ai/samples/util/BitmapUtils.kt b/samples/gemini-image-chat/src/main/kotlin/com/android/ai/samples/util/BitmapUtils.kt similarity index 100% rename from samples/gemini-image-chat/src/main/java/com/android/ai/samples/util/BitmapUtils.kt rename to samples/gemini-image-chat/src/main/kotlin/com/android/ai/samples/util/BitmapUtils.kt diff --git a/samples/gemini-live-todo/README.md b/samples/gemini-live-todo/README.md index 617c226e..4a90bbbb 100644 --- a/samples/gemini-live-todo/README.md +++ b/samples/gemini-live-todo/README.md @@ -12,7 +12,7 @@ This sample demonstrates how to use the Gemini Live API for real-time, voice-bas ## How it works -The application uses the Firebase AI SDK (see [How to run](../../#how-to-run)) for Android to interact with Gemini Flash. The core logic is in the [`TodoScreenViewModel.kt`](./src/main/java/com/android/ai/samples/geminilivetodo/ui/TodoScreenViewModel.kt) file. A `liveModel` is initialized with a set of function declarations (`addTodo`, `removeTodo`, `toggleTodoStatus`, `getTodoList`) that allow the model to interact with the ToDo list. When the user starts a voice conversation, the model processes the spoken commands and executes the corresponding functions to manage the tasks. +The application uses the Firebase AI SDK (see [How to run](../../#how-to-run)) for Android to interact with Gemini Flash. The core logic is in the [`TodoScreenViewModel.kt`](./src/main/kotlincom/android/ai/samples/geminilivetodo/ui/TodoScreenViewModel.kt) file. A `liveModel` is initialized with a set of function declarations (`addTodo`, `removeTodo`, `toggleTodoStatus`, `getTodoList`) that allow the model to interact with the ToDo list. When the user starts a voice conversation, the model processes the spoken commands and executes the corresponding functions to manage the tasks. Here is the key snippet of code that initializes the model and connects to a live session: diff --git a/samples/gemini-live-todo/build.gradle.kts b/samples/gemini-live-todo/build.gradle.kts index 2eef9bf4..f1d5ea2d 100644 --- a/samples/gemini-live-todo/build.gradle.kts +++ b/samples/gemini-live-todo/build.gradle.kts @@ -13,60 +13,52 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + plugins { alias(libs.plugins.android.library) + alias(libs.plugins.compose.compiler) alias(libs.plugins.jetbrains.kotlin.android) alias(libs.plugins.ksp) - alias(libs.plugins.compose.compiler) } android { namespace = "com.android.ai.samples.geminilivetodo" - compileSdk = 35 + compileSdk = 36 defaultConfig { minSdk = 24 testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles("consumer-rules.pro") } - buildTypes { - release { - isMinifyEnabled = false - proguardFiles( - getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro", - ) - } - } compileOptions { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } - kotlinOptions { - jvmTarget = "17" + kotlin { + compilerOptions { jvmTarget.set(JvmTarget.JVM_17) } } } dependencies { - - implementation(libs.androidx.core.ktx) + androidTestImplementation(libs.androidx.espresso.core) + androidTestImplementation(libs.androidx.junit) + implementation(libs.androidx.activity.compose) implementation(libs.androidx.appcompat) - implementation(platform(libs.androidx.compose.bom)) - implementation(libs.androidx.material.icons.extended) - implementation(platform(libs.firebase.bom)) - implementation(libs.firebase.ai) + implementation(libs.androidx.core.ktx) implementation(libs.androidx.lifecycle.viewmodel.android) + implementation(libs.androidx.material.icons.extended) implementation(libs.androidx.material3) + implementation(libs.firebase.ai) implementation(libs.hilt.android) - implementation(libs.ui.tooling.preview) - ksp(libs.hilt.compiler) - implementation(project(":ui-component")) implementation(libs.hilt.navigation.compose) - implementation(libs.androidx.activity.compose) implementation(libs.kotlinx.serialization.json) + implementation(libs.ui.tooling.preview) + implementation(platform(libs.androidx.compose.bom)) + implementation(platform(libs.firebase.bom)) + implementation(project(":ui-component")) + ksp(libs.hilt.compiler) testImplementation(libs.junit) - androidTestImplementation(libs.androidx.junit) - androidTestImplementation(libs.androidx.espresso.core) } diff --git a/samples/gemini-live-todo/consumer-rules.pro b/samples/gemini-live-todo/consumer-rules.pro deleted file mode 100644 index e69de29b..00000000 diff --git a/samples/gemini-live-todo/proguard-rules.pro b/samples/gemini-live-todo/proguard-rules.pro deleted file mode 100644 index 481bb434..00000000 --- a/samples/gemini-live-todo/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# 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 \ No newline at end of file diff --git a/samples/gemini-live-todo/src/main/java/com/android/ai/samples/geminilivetodo/data/Todo.kt b/samples/gemini-live-todo/src/main/kotlin/com/android/ai/samples/geminilivetodo/data/Todo.kt similarity index 99% rename from samples/gemini-live-todo/src/main/java/com/android/ai/samples/geminilivetodo/data/Todo.kt rename to samples/gemini-live-todo/src/main/kotlin/com/android/ai/samples/geminilivetodo/data/Todo.kt index 953ec7fc..755d72d5 100644 --- a/samples/gemini-live-todo/src/main/java/com/android/ai/samples/geminilivetodo/data/Todo.kt +++ b/samples/gemini-live-todo/src/main/kotlin/com/android/ai/samples/geminilivetodo/data/Todo.kt @@ -17,7 +17,6 @@ package com.android.ai.samples.geminilivetodo.data import kotlin.random.Random - data class Todo( val id: Int = Random.nextInt(), val task: String, diff --git a/samples/gemini-live-todo/src/main/java/com/android/ai/samples/geminilivetodo/data/TodoRepository.kt b/samples/gemini-live-todo/src/main/kotlin/com/android/ai/samples/geminilivetodo/data/TodoRepository.kt similarity index 97% rename from samples/gemini-live-todo/src/main/java/com/android/ai/samples/geminilivetodo/data/TodoRepository.kt rename to samples/gemini-live-todo/src/main/kotlin/com/android/ai/samples/geminilivetodo/data/TodoRepository.kt index 8729e1b6..160e9441 100644 --- a/samples/gemini-live-todo/src/main/java/com/android/ai/samples/geminilivetodo/data/TodoRepository.kt +++ b/samples/gemini-live-todo/src/main/kotlin/com/android/ai/samples/geminilivetodo/data/TodoRepository.kt @@ -41,7 +41,7 @@ class TodoRepository @Inject constructor() { fun getTodoList(): List = _todos.value - fun addTodo(taskDescription: String) : Int? { + fun addTodo(taskDescription: String): Int? { if (taskDescription.isNotBlank()) { val newTodo = Todo(task = taskDescription) _todos.update { currentList -> diff --git a/samples/gemini-live-todo/src/main/java/com/android/ai/samples/geminilivetodo/ui/TodoScreen.kt b/samples/gemini-live-todo/src/main/kotlin/com/android/ai/samples/geminilivetodo/ui/TodoScreen.kt similarity index 99% rename from samples/gemini-live-todo/src/main/java/com/android/ai/samples/geminilivetodo/ui/TodoScreen.kt rename to samples/gemini-live-todo/src/main/kotlin/com/android/ai/samples/geminilivetodo/ui/TodoScreen.kt index 17abc4c1..251bc593 100644 --- a/samples/gemini-live-todo/src/main/java/com/android/ai/samples/geminilivetodo/ui/TodoScreen.kt +++ b/samples/gemini-live-todo/src/main/kotlin/com/android/ai/samples/geminilivetodo/ui/TodoScreen.kt @@ -60,7 +60,7 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel +import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.ai.samples.geminilivetodo.R import com.android.ai.samples.geminilivetodo.data.Todo diff --git a/samples/gemini-live-todo/src/main/java/com/android/ai/samples/geminilivetodo/ui/TodoScreenUiState.kt b/samples/gemini-live-todo/src/main/kotlin/com/android/ai/samples/geminilivetodo/ui/TodoScreenUiState.kt similarity index 100% rename from samples/gemini-live-todo/src/main/java/com/android/ai/samples/geminilivetodo/ui/TodoScreenUiState.kt rename to samples/gemini-live-todo/src/main/kotlin/com/android/ai/samples/geminilivetodo/ui/TodoScreenUiState.kt diff --git a/samples/gemini-live-todo/src/main/java/com/android/ai/samples/geminilivetodo/ui/TodoScreenViewModel.kt b/samples/gemini-live-todo/src/main/kotlin/com/android/ai/samples/geminilivetodo/ui/TodoScreenViewModel.kt similarity index 99% rename from samples/gemini-live-todo/src/main/java/com/android/ai/samples/geminilivetodo/ui/TodoScreenViewModel.kt rename to samples/gemini-live-todo/src/main/kotlin/com/android/ai/samples/geminilivetodo/ui/TodoScreenViewModel.kt index 05dbd7ab..0fb1ddb1 100644 --- a/samples/gemini-live-todo/src/main/java/com/android/ai/samples/geminilivetodo/ui/TodoScreenViewModel.kt +++ b/samples/gemini-live-todo/src/main/kotlin/com/android/ai/samples/geminilivetodo/ui/TodoScreenViewModel.kt @@ -52,7 +52,6 @@ import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonPrimitive import kotlinx.serialization.json.int import kotlinx.serialization.json.jsonPrimitive -import kotlinx.serialization.json.long @OptIn(PublicPreviewAPI::class) @HiltViewModel @@ -198,7 +197,7 @@ class TodoScreenViewModel @Inject constructor(private val todoRepository: TodoRe val taskDescription = functionCall.args["taskDescription"]!!.jsonPrimitive.content val id = todoRepository.addTodo(taskDescription) - if (id!=null) { + if (id != null) { val response = JsonObject( mapOf( "success" to JsonPrimitive(true), @@ -215,7 +214,6 @@ class TodoScreenViewModel @Inject constructor(private val todoRepository: TodoRe ) FunctionResponsePart(functionCall.name, response, functionCall.id) } - } "removeTodo" -> { try { @@ -237,7 +235,6 @@ class TodoScreenViewModel @Inject constructor(private val todoRepository: TodoRe ) FunctionResponsePart(functionCall.name, response, functionCall.id) } - } "toggleTodoStatus" -> { val taskId = functionCall.args["todoId"]!!.jsonPrimitive.int diff --git a/samples/gemini-multimodal/README.md b/samples/gemini-multimodal/README.md index 58ca3459..0de50595 100644 --- a/samples/gemini-multimodal/README.md +++ b/samples/gemini-multimodal/README.md @@ -12,7 +12,7 @@ This sample demonstrates a multimodal (image and text) prompt, using the Gemini ## How it works -The application uses the Firebase AI SDK (see [How to run](../../#how-to-run)) for Android to interact with Gemini Flash. The core logic is in the [`GeminiDataSource.kt`](./src/main/java/com/android/ai/samples/geminimultimodal/data/GeminiDataSource.kt) file. A `generativeModel` is initialized, and then a `chat` session is started from it. When a user provides an image and a text prompt, they are combined into a multimodal prompt and sent to the model, which then generates a text response. +The application uses the Firebase AI SDK (see [How to run](../../#how-to-run)) for Android to interact with Gemini Flash. The core logic is in the [`GeminiDataSource.kt`](./src/main/kotlincom/android/ai/samples/geminimultimodal/data/GeminiDataSource.kt) file. A `generativeModel` is initialized, and then a `chat` session is started from it. When a user provides an image and a text prompt, they are combined into a multimodal prompt and sent to the model, which then generates a text response. Here is the key snippet of code that initializes the generative model: @@ -36,7 +36,7 @@ private val generativeModel by lazy { } ``` -Here is the key snippet of code that calls the [`generateText`](./src/main/java/com/android/ai/samples/geminimultimodal/data/GeminiDataSource.kt) function: +Here is the key snippet of code that calls the [`generateText`](./src/main/kotlincom/android/ai/samples/geminimultimodal/data/GeminiDataSource.kt) function: ```kotlin suspend fun generateText(bitmap: Bitmap, prompt: String): String { diff --git a/samples/gemini-multimodal/build.gradle.kts b/samples/gemini-multimodal/build.gradle.kts index f59af216..fa3ce0a8 100644 --- a/samples/gemini-multimodal/build.gradle.kts +++ b/samples/gemini-multimodal/build.gradle.kts @@ -14,6 +14,8 @@ * limitations under the License. */ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + plugins { alias(libs.plugins.android.library) alias(libs.plugins.jetbrains.kotlin.android) @@ -23,7 +25,7 @@ plugins { android { namespace = "com.android.ai.samples.geminimultimodal" - compileSdk = 35 + compileSdk = 36 buildFeatures { compose = true @@ -33,18 +35,8 @@ android { minSdk = 24 testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles("consumer-rules.pro") } - buildTypes { - release { - isMinifyEnabled = false - proguardFiles( - getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro", - ) - } - } compileOptions { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 @@ -52,32 +44,31 @@ android { composeOptions { kotlinCompilerExtensionVersion = "1.5.15" } - kotlinOptions { - jvmTarget = "17" + kotlin { + compilerOptions { jvmTarget.set(JvmTarget.JVM_17) } } } dependencies { - - implementation(libs.androidx.core.ktx) - implementation(libs.androidx.appcompat) - implementation(libs.androidx.material3) + androidTestImplementation(libs.androidx.espresso.core) + androidTestImplementation(libs.androidx.junit) implementation(libs.androidx.activity.compose) - implementation(platform(libs.androidx.compose.bom)) + implementation(libs.androidx.appcompat) + implementation(libs.androidx.core.ktx) implementation(libs.androidx.material.icons.extended) implementation(libs.androidx.material.icons.extended) - implementation(platform(libs.firebase.bom)) + implementation(libs.androidx.material3) + implementation(libs.androidx.material3.window) + implementation(libs.androidx.runtime.livedata) + implementation(libs.androidx.runtime.livedata) + implementation(libs.coil.compose) implementation(libs.firebase.ai) implementation(libs.hilt.android) implementation(libs.hilt.navigation.compose) - implementation(libs.coil.compose) - implementation(libs.androidx.runtime.livedata) - implementation(libs.androidx.runtime.livedata) - implementation(libs.androidx.material3.window) - debugImplementation(libs.ui.tooling) - ksp(libs.hilt.compiler) + implementation(libs.ui.tooling) + implementation(platform(libs.androidx.compose.bom)) + implementation(platform(libs.firebase.bom)) implementation(project(":ui-component")) + ksp(libs.hilt.compiler) testImplementation(libs.junit) - androidTestImplementation(libs.androidx.junit) - androidTestImplementation(libs.androidx.espresso.core) } diff --git a/samples/gemini-multimodal/consumer-rules.pro b/samples/gemini-multimodal/consumer-rules.pro deleted file mode 100644 index e69de29b..00000000 diff --git a/samples/gemini-multimodal/proguard-rules.pro b/samples/gemini-multimodal/proguard-rules.pro deleted file mode 100644 index 481bb434..00000000 --- a/samples/gemini-multimodal/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# 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 \ No newline at end of file diff --git a/samples/gemini-multimodal/src/main/java/com/android/ai/samples/geminimultimodal/data/GeminiDataSource.kt b/samples/gemini-multimodal/src/main/kotlin/com/android/ai/samples/geminimultimodal/data/GeminiDataSource.kt similarity index 100% rename from samples/gemini-multimodal/src/main/java/com/android/ai/samples/geminimultimodal/data/GeminiDataSource.kt rename to samples/gemini-multimodal/src/main/kotlin/com/android/ai/samples/geminimultimodal/data/GeminiDataSource.kt diff --git a/samples/gemini-multimodal/src/main/java/com/android/ai/samples/geminimultimodal/ui/GeminiMultimodalScreen.kt b/samples/gemini-multimodal/src/main/kotlin/com/android/ai/samples/geminimultimodal/ui/GeminiMultimodalScreen.kt similarity index 97% rename from samples/gemini-multimodal/src/main/java/com/android/ai/samples/geminimultimodal/ui/GeminiMultimodalScreen.kt rename to samples/gemini-multimodal/src/main/kotlin/com/android/ai/samples/geminimultimodal/ui/GeminiMultimodalScreen.kt index af9e4139..feeb4e40 100644 --- a/samples/gemini-multimodal/src/main/java/com/android/ai/samples/geminimultimodal/ui/GeminiMultimodalScreen.kt +++ b/samples/gemini-multimodal/src/main/kotlin/com/android/ai/samples/geminimultimodal/ui/GeminiMultimodalScreen.kt @@ -60,7 +60,7 @@ import androidx.compose.ui.tooling.preview.Devices.PHONE import androidx.compose.ui.tooling.preview.Devices.TABLET import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel +import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.ai.samples.geminimultimodal.R import com.android.ai.theme.AISampleCatalogTheme @@ -256,8 +256,8 @@ private fun PromptInput( enabled = uiState !is GeminiMultimodalUiState.Loading && imageUri != null, onClick = { if (imageUri != null) { + @Suppress("DEPRECATION") val bitmap = MediaStore.Images.Media.getBitmap(context.contentResolver, imageUri) -// val bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(context.contentResolver, imageUri)) onGenerateClick(bitmap, textFieldState.text.toString()) } keyboardController?.hide() @@ -285,7 +285,6 @@ private fun PromptInput( @Preview(name = "Phone", device = PHONE) @Composable -@OptIn(ExperimentalMaterial3Api::class) private fun GeminiMultimodalScreenPreview() { AISampleCatalogTheme { GeminiMultimodalScreen( @@ -301,7 +300,6 @@ private fun GeminiMultimodalScreenPreview() { @Preview(name = "Tablet", device = TABLET) @Composable -@OptIn(ExperimentalMaterial3Api::class) private fun GeminiMultimodalScreenTabletPreview() { AISampleCatalogTheme { GeminiMultimodalScreen( diff --git a/samples/gemini-multimodal/src/main/java/com/android/ai/samples/geminimultimodal/ui/GeminiMultimodalUiState.kt b/samples/gemini-multimodal/src/main/kotlin/com/android/ai/samples/geminimultimodal/ui/GeminiMultimodalUiState.kt similarity index 100% rename from samples/gemini-multimodal/src/main/java/com/android/ai/samples/geminimultimodal/ui/GeminiMultimodalUiState.kt rename to samples/gemini-multimodal/src/main/kotlin/com/android/ai/samples/geminimultimodal/ui/GeminiMultimodalUiState.kt diff --git a/samples/gemini-multimodal/src/main/java/com/android/ai/samples/geminimultimodal/ui/GeminiMultimodalViewModel.kt b/samples/gemini-multimodal/src/main/kotlin/com/android/ai/samples/geminimultimodal/ui/GeminiMultimodalViewModel.kt similarity index 100% rename from samples/gemini-multimodal/src/main/java/com/android/ai/samples/geminimultimodal/ui/GeminiMultimodalViewModel.kt rename to samples/gemini-multimodal/src/main/kotlin/com/android/ai/samples/geminimultimodal/ui/GeminiMultimodalViewModel.kt diff --git a/samples/gemini-video-metadata-creation/README.md b/samples/gemini-video-metadata-creation/README.md index 2fd9c2ba..15acfe66 100644 --- a/samples/gemini-video-metadata-creation/README.md +++ b/samples/gemini-video-metadata-creation/README.md @@ -12,7 +12,7 @@ This sample demonstrates how to generate various types of video metadata (descri ## How it works -The application uses the Firebase AI SDK (see [How to run](../../#how-to-run)) for Android to interact with Gemini Flash. The core logic involves several functions (e.g., [`generateDescription`](./src/main/java/com/android/ai/samples/geminivideometadatacreation/GenerateDescription.kt), `generateHashtags`, `generateChapters`, `generateAccountTags`, `generateLinks`, `generateThumbnails`) that send video content to the Gemini API for analysis. The model processes the video and returns structured metadata based on the specific prompt. +The application uses the Firebase AI SDK (see [How to run](../../#how-to-run)) for Android to interact with Gemini Flash. The core logic involves several functions (e.g., [`generateDescription`](./src/main/kotlincom/android/ai/samples/geminivideometadatacreation/GenerateDescription.kt), `generateHashtags`, `generateChapters`, `generateAccountTags`, `generateLinks`, `generateThumbnails`) that send video content to the Gemini API for analysis. The model processes the video and returns structured metadata based on the specific prompt. Here is a key snippet of code that generates a video description: diff --git a/samples/gemini-video-metadata-creation/build.gradle.kts b/samples/gemini-video-metadata-creation/build.gradle.kts index 5f25e744..59aa8944 100644 --- a/samples/gemini-video-metadata-creation/build.gradle.kts +++ b/samples/gemini-video-metadata-creation/build.gradle.kts @@ -13,6 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + plugins { alias(libs.plugins.android.library) alias(libs.plugins.jetbrains.kotlin.android) @@ -31,21 +34,12 @@ android { testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } - buildTypes { - release { - isMinifyEnabled = false - proguardFiles( - getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro", - ) - } - } compileOptions { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } - kotlinOptions { - jvmTarget = "17" + kotlin { + compilerOptions { jvmTarget.set(JvmTarget.JVM_17) } } buildFeatures { compose = true @@ -53,33 +47,30 @@ android { } dependencies { - implementation(project(":ui-component")) - implementation(libs.androidx.core.ktx) - implementation(libs.androidx.appcompat) + androidTestImplementation(libs.androidx.espresso.core) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.ui.test.junit4) + androidTestImplementation(platform(libs.androidx.compose.bom)) + debugImplementation(libs.androidx.ui.tooling) implementation(libs.androidx.activity.compose) + implementation(libs.androidx.appcompat) + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.lifecycle.runtime.compose) implementation(libs.androidx.material.icons.extended) - implementation(platform(libs.androidx.compose.bom)) - implementation(libs.hilt.android) - implementation(libs.hilt.navigation.compose) implementation(libs.androidx.material3) - implementation(libs.firebase.common.ktx) - implementation(libs.androidx.lifecycle.runtime.compose) - implementation(libs.androidx.ui.tooling.preview.android) - ksp(libs.hilt.compiler) - implementation(platform(libs.firebase.bom)) - implementation(libs.firebase.ai) - implementation(libs.kotlinx.serialization.json) - debugImplementation(libs.androidx.ui.tooling) - - // Media3 ExoPlayer implementation(libs.androidx.media3.exoplayer) - implementation(libs.androidx.media3.ui) implementation(libs.androidx.media3.transformer) + implementation(libs.androidx.media3.ui) implementation(libs.androidx.media3.ui.compose) + implementation(libs.androidx.ui.tooling.preview.android) + implementation(libs.firebase.ai) + implementation(libs.firebase.common.ktx) + implementation(libs.hilt.android) + implementation(libs.hilt.navigation.compose) implementation(libs.kotlinx.coroutines.guava) - - androidTestImplementation(libs.androidx.junit) - androidTestImplementation(libs.androidx.espresso.core) - androidTestImplementation(platform(libs.androidx.compose.bom)) - androidTestImplementation(libs.androidx.ui.test.junit4) + implementation(libs.kotlinx.serialization.json) + implementation(platform(libs.androidx.compose.bom)) + implementation(platform(libs.firebase.bom)) + implementation(project(":ui-component")) + ksp(libs.hilt.compiler) } diff --git a/samples/gemini-video-metadata-creation/proguard-rules.pro b/samples/gemini-video-metadata-creation/proguard-rules.pro deleted file mode 100644 index 481bb434..00000000 --- a/samples/gemini-video-metadata-creation/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# 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 \ No newline at end of file diff --git a/samples/gemini-video-metadata-creation/src/main/java/com/android/ai/samples/geminivideometadatacreation/GenerateAccountTags.kt b/samples/gemini-video-metadata-creation/src/main/kotlin/com/android/ai/samples/geminivideometadatacreation/GenerateAccountTags.kt similarity index 100% rename from samples/gemini-video-metadata-creation/src/main/java/com/android/ai/samples/geminivideometadatacreation/GenerateAccountTags.kt rename to samples/gemini-video-metadata-creation/src/main/kotlin/com/android/ai/samples/geminivideometadatacreation/GenerateAccountTags.kt diff --git a/samples/gemini-video-metadata-creation/src/main/java/com/android/ai/samples/geminivideometadatacreation/GenerateChapters.kt b/samples/gemini-video-metadata-creation/src/main/kotlin/com/android/ai/samples/geminivideometadatacreation/GenerateChapters.kt similarity index 100% rename from samples/gemini-video-metadata-creation/src/main/java/com/android/ai/samples/geminivideometadatacreation/GenerateChapters.kt rename to samples/gemini-video-metadata-creation/src/main/kotlin/com/android/ai/samples/geminivideometadatacreation/GenerateChapters.kt diff --git a/samples/gemini-video-metadata-creation/src/main/java/com/android/ai/samples/geminivideometadatacreation/GenerateDescription.kt b/samples/gemini-video-metadata-creation/src/main/kotlin/com/android/ai/samples/geminivideometadatacreation/GenerateDescription.kt similarity index 100% rename from samples/gemini-video-metadata-creation/src/main/java/com/android/ai/samples/geminivideometadatacreation/GenerateDescription.kt rename to samples/gemini-video-metadata-creation/src/main/kotlin/com/android/ai/samples/geminivideometadatacreation/GenerateDescription.kt diff --git a/samples/gemini-video-metadata-creation/src/main/java/com/android/ai/samples/geminivideometadatacreation/GenerateHashtags.kt b/samples/gemini-video-metadata-creation/src/main/kotlin/com/android/ai/samples/geminivideometadatacreation/GenerateHashtags.kt similarity index 100% rename from samples/gemini-video-metadata-creation/src/main/java/com/android/ai/samples/geminivideometadatacreation/GenerateHashtags.kt rename to samples/gemini-video-metadata-creation/src/main/kotlin/com/android/ai/samples/geminivideometadatacreation/GenerateHashtags.kt diff --git a/samples/gemini-video-metadata-creation/src/main/java/com/android/ai/samples/geminivideometadatacreation/GenerateLinks.kt b/samples/gemini-video-metadata-creation/src/main/kotlin/com/android/ai/samples/geminivideometadatacreation/GenerateLinks.kt similarity index 100% rename from samples/gemini-video-metadata-creation/src/main/java/com/android/ai/samples/geminivideometadatacreation/GenerateLinks.kt rename to samples/gemini-video-metadata-creation/src/main/kotlin/com/android/ai/samples/geminivideometadatacreation/GenerateLinks.kt diff --git a/samples/gemini-video-metadata-creation/src/main/java/com/android/ai/samples/geminivideometadatacreation/GenerateThumbnails.kt b/samples/gemini-video-metadata-creation/src/main/kotlin/com/android/ai/samples/geminivideometadatacreation/GenerateThumbnails.kt similarity index 100% rename from samples/gemini-video-metadata-creation/src/main/java/com/android/ai/samples/geminivideometadatacreation/GenerateThumbnails.kt rename to samples/gemini-video-metadata-creation/src/main/kotlin/com/android/ai/samples/geminivideometadatacreation/GenerateThumbnails.kt diff --git a/samples/gemini-video-metadata-creation/src/main/java/com/android/ai/samples/geminivideometadatacreation/player/ExtractHDRThumbnails.kt b/samples/gemini-video-metadata-creation/src/main/kotlin/com/android/ai/samples/geminivideometadatacreation/player/ExtractHDRThumbnails.kt similarity index 98% rename from samples/gemini-video-metadata-creation/src/main/java/com/android/ai/samples/geminivideometadatacreation/player/ExtractHDRThumbnails.kt rename to samples/gemini-video-metadata-creation/src/main/kotlin/com/android/ai/samples/geminivideometadatacreation/player/ExtractHDRThumbnails.kt index 485a5864..e1b5694b 100644 --- a/samples/gemini-video-metadata-creation/src/main/java/com/android/ai/samples/geminivideometadatacreation/player/ExtractHDRThumbnails.kt +++ b/samples/gemini-video-metadata-creation/src/main/kotlin/com/android/ai/samples/geminivideometadatacreation/player/ExtractHDRThumbnails.kt @@ -21,7 +21,6 @@ import android.graphics.Bitmap import android.net.Uri import android.os.Build import android.util.Log -import androidx.annotation.OptIn import androidx.media3.common.MediaItem import androidx.media3.common.util.UnstableApi import androidx.media3.transformer.ExperimentalFrameExtractor @@ -38,6 +37,7 @@ import kotlinx.coroutines.withContext * * */ @UnstableApi +@Suppress("DEPRECATION") @SuppressLint("UnsafeOptInUsageError", "NewApi") suspend fun extractFrame(context: Context, videoUri: Uri, timestamps: Long): Bitmap? { val mediaItem = MediaItem.fromUri(videoUri) @@ -71,10 +71,10 @@ suspend fun extractFrame(context: Context, videoUri: Uri, timestamps: Long): Bit } } -@OptIn(UnstableApi::class) suspend fun extractListOfThumbnails(context: Context, videoUri: Uri, timestamps: List): List { return withContext(Dispatchers.IO) { timestamps.mapNotNull { timestamp -> + @UnstableApi extractFrame(context, videoUri, timestamp) } } diff --git a/samples/gemini-video-metadata-creation/src/main/java/com/android/ai/samples/geminivideometadatacreation/player/VideoPlayer.kt b/samples/gemini-video-metadata-creation/src/main/kotlin/com/android/ai/samples/geminivideometadatacreation/player/VideoPlayer.kt similarity index 94% rename from samples/gemini-video-metadata-creation/src/main/java/com/android/ai/samples/geminivideometadatacreation/player/VideoPlayer.kt rename to samples/gemini-video-metadata-creation/src/main/kotlin/com/android/ai/samples/geminivideometadatacreation/player/VideoPlayer.kt index 44f28cec..2124f27c 100644 --- a/samples/gemini-video-metadata-creation/src/main/java/com/android/ai/samples/geminivideometadatacreation/player/VideoPlayer.kt +++ b/samples/gemini-video-metadata-creation/src/main/kotlin/com/android/ai/samples/geminivideometadatacreation/player/VideoPlayer.kt @@ -15,7 +15,6 @@ */ package com.android.ai.samples.geminivideometadatacreation.player -import androidx.annotation.OptIn import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.padding @@ -42,7 +41,7 @@ import com.android.ai.samples.geminivideometadatacreation.R /* * A Composable function that displays video using ExoPlayer within a PlayerView in Jetpack Compose. */ -@OptIn(UnstableApi::class) // New Media3 Compose artifact is currently experimental +@UnstableApi @Composable fun VideoPlayer(player: Player?, modifier: Modifier = Modifier) { @@ -72,7 +71,7 @@ fun VideoPlayer(player: Player?, modifier: Modifier = Modifier) { } } -@OptIn(UnstableApi::class) // New Media3 Compose artifact is currently experimental +@UnstableApi @Composable fun PlayPauseButton(player: Player?, modifier: Modifier = Modifier) { if (player == null) return diff --git a/samples/gemini-video-metadata-creation/src/main/java/com/android/ai/samples/geminivideometadatacreation/player/VideoSelectionDropdown.kt b/samples/gemini-video-metadata-creation/src/main/kotlin/com/android/ai/samples/geminivideometadatacreation/player/VideoSelectionDropdown.kt similarity index 100% rename from samples/gemini-video-metadata-creation/src/main/java/com/android/ai/samples/geminivideometadatacreation/player/VideoSelectionDropdown.kt rename to samples/gemini-video-metadata-creation/src/main/kotlin/com/android/ai/samples/geminivideometadatacreation/player/VideoSelectionDropdown.kt diff --git a/samples/gemini-video-metadata-creation/src/main/java/com/android/ai/samples/geminivideometadatacreation/ui/MetadataButtonRow.kt b/samples/gemini-video-metadata-creation/src/main/kotlin/com/android/ai/samples/geminivideometadatacreation/ui/MetadataButtonRow.kt similarity index 100% rename from samples/gemini-video-metadata-creation/src/main/java/com/android/ai/samples/geminivideometadatacreation/ui/MetadataButtonRow.kt rename to samples/gemini-video-metadata-creation/src/main/kotlin/com/android/ai/samples/geminivideometadatacreation/ui/MetadataButtonRow.kt diff --git a/samples/gemini-video-metadata-creation/src/main/java/com/android/ai/samples/geminivideometadatacreation/ui/MetadataComponents.kt b/samples/gemini-video-metadata-creation/src/main/kotlin/com/android/ai/samples/geminivideometadatacreation/ui/MetadataComponents.kt similarity index 100% rename from samples/gemini-video-metadata-creation/src/main/java/com/android/ai/samples/geminivideometadatacreation/ui/MetadataComponents.kt rename to samples/gemini-video-metadata-creation/src/main/kotlin/com/android/ai/samples/geminivideometadatacreation/ui/MetadataComponents.kt diff --git a/samples/gemini-video-metadata-creation/src/main/java/com/android/ai/samples/geminivideometadatacreation/ui/VideoMetadataCreationScreen.kt b/samples/gemini-video-metadata-creation/src/main/kotlin/com/android/ai/samples/geminivideometadatacreation/ui/VideoMetadataCreationScreen.kt similarity index 97% rename from samples/gemini-video-metadata-creation/src/main/java/com/android/ai/samples/geminivideometadatacreation/ui/VideoMetadataCreationScreen.kt rename to samples/gemini-video-metadata-creation/src/main/kotlin/com/android/ai/samples/geminivideometadatacreation/ui/VideoMetadataCreationScreen.kt index 14021175..81315407 100644 --- a/samples/gemini-video-metadata-creation/src/main/java/com/android/ai/samples/geminivideometadatacreation/ui/VideoMetadataCreationScreen.kt +++ b/samples/gemini-video-metadata-creation/src/main/kotlin/com/android/ai/samples/geminivideometadatacreation/ui/VideoMetadataCreationScreen.kt @@ -44,11 +44,11 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalResources import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel +import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.LifecycleStartEffect import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.media3.common.Player @@ -68,7 +68,6 @@ import com.android.ai.uicomponent.VideoPlayer * This screen allows users to select a video, play it, and generate metadata of its content * using Firebase AI. It also provides text-to-speech functionality to read out */ -@OptIn(ExperimentalMaterial3Api::class) @Composable fun VideoMetadataCreationScreen(viewModel: VideoMetadataCreationViewModel = hiltViewModel()) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() @@ -107,7 +106,7 @@ private fun VideoMetadataCreationScreen( onMetadataTypeClicked: (MetadataType) -> Unit, ) { var isDropdownExpanded by remember { mutableStateOf(false) } - val context = LocalContext.current + val resources = LocalResources.current val backDispatcher = LocalOnBackPressedDispatcherOwner.current?.onBackPressedDispatcher Scaffold( topBar = { @@ -130,7 +129,7 @@ private fun VideoMetadataCreationScreen( player = player, videoPicker = { VideoPickerDropdown( - videoItems = sampleVideoList.map { VideoPickerData(context.getString(it.titleResId), it.uri) }, + videoItems = sampleVideoList.map { VideoPickerData(resources.getString(it.titleResId), it.uri) }, selectedVideo = selectedVideoUri, isExpanded = isDropdownExpanded, onDropdownExpandedChanged = { isDropdownExpanded = it }, @@ -213,7 +212,6 @@ private fun MetadataCreationSection( } @Preview -@OptIn(ExperimentalMaterial3Api::class) @Composable fun VideoMetadataCreationScreenPreview() { VideoMetadataCreationScreen( diff --git a/samples/gemini-video-metadata-creation/src/main/java/com/android/ai/samples/geminivideometadatacreation/util/VideoList.kt b/samples/gemini-video-metadata-creation/src/main/kotlin/com/android/ai/samples/geminivideometadatacreation/util/VideoList.kt similarity index 100% rename from samples/gemini-video-metadata-creation/src/main/java/com/android/ai/samples/geminivideometadatacreation/util/VideoList.kt rename to samples/gemini-video-metadata-creation/src/main/kotlin/com/android/ai/samples/geminivideometadatacreation/util/VideoList.kt diff --git a/samples/gemini-video-metadata-creation/src/main/java/com/android/ai/samples/geminivideometadatacreation/viewmodel/VideoMetadataCreationViewModel.kt b/samples/gemini-video-metadata-creation/src/main/kotlin/com/android/ai/samples/geminivideometadatacreation/viewmodel/VideoMetadataCreationViewModel.kt similarity index 98% rename from samples/gemini-video-metadata-creation/src/main/java/com/android/ai/samples/geminivideometadatacreation/viewmodel/VideoMetadataCreationViewModel.kt rename to samples/gemini-video-metadata-creation/src/main/kotlin/com/android/ai/samples/geminivideometadatacreation/viewmodel/VideoMetadataCreationViewModel.kt index 09097d66..88ca4a32 100644 --- a/samples/gemini-video-metadata-creation/src/main/java/com/android/ai/samples/geminivideometadatacreation/viewmodel/VideoMetadataCreationViewModel.kt +++ b/samples/gemini-video-metadata-creation/src/main/kotlin/com/android/ai/samples/geminivideometadatacreation/viewmodel/VideoMetadataCreationViewModel.kt @@ -57,7 +57,6 @@ class VideoMetadataCreationViewModel @Inject constructor(private val application private val _uiState = MutableStateFlow(VideoMetadataCreationState()) val uiState: StateFlow = _uiState.asStateFlow() - @OptIn(UnstableApi::class) fun generateMetadata(metadataType: MetadataType) { val videoUri = _uiState.value.selectedVideoUri ?: return // Since we will start an async call, show a progressbar @@ -144,8 +143,8 @@ class VideoMetadataCreationViewModel @Inject constructor(private val application } enum class MetadataType( - @DrawableRes val iconRes: Int, - @StringRes val titleRes: Int, + @param:DrawableRes val iconRes: Int, + @param:StringRes val titleRes: Int, ) { THUMBNAILS(com.android.ai.uicomponent.R.drawable.ic_ai_img, R.string.thumbnails), DESCRIPTION(com.android.ai.uicomponent.R.drawable.ic_ai_summary, R.string.description), diff --git a/samples/gemini-video-summarization/README.md b/samples/gemini-video-summarization/README.md index 23c54b57..4b6cc79e 100644 --- a/samples/gemini-video-summarization/README.md +++ b/samples/gemini-video-summarization/README.md @@ -12,7 +12,7 @@ This sample demonstrates how to generate a text summary of a video using Gemini ## How it works -The application uses the Firebase AI SDK (see [How to run](../../#how-to-run)) for Android to interact with Gemini Flash. The core logic is in the [`VideoSummarizationViewModel.kt`](./src/main/java/com/android/ai/samples/geminivideosummary/viewmodel/VideoSummarizationViewModel.kt) file. A `generativeModel` is initialized. When a user requests a summary, the video content and a text prompt are sent to the model, which then generates a text summary. +The application uses the Firebase AI SDK (see [How to run](../../#how-to-run)) for Android to interact with Gemini Flash. The core logic is in the [`VideoSummarizationViewModel.kt`](./src/main/kotlincom/android/ai/samples/geminivideosummary/viewmodel/VideoSummarizationViewModel.kt) file. A `generativeModel` is initialized. When a user requests a summary, the video content and a text prompt are sent to the model, which then generates a text summary. Here is the key snippet of code that calls the generative model: diff --git a/samples/gemini-video-summarization/build.gradle.kts b/samples/gemini-video-summarization/build.gradle.kts index 8cd50c2f..d4f27561 100644 --- a/samples/gemini-video-summarization/build.gradle.kts +++ b/samples/gemini-video-summarization/build.gradle.kts @@ -13,6 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + plugins { alias(libs.plugins.android.library) alias(libs.plugins.jetbrains.kotlin.android) @@ -23,28 +26,19 @@ plugins { android { namespace = "com.google.com.android.ai.samples.geminivideosummary" - compileSdk = 35 + compileSdk = 36 defaultConfig { minSdk = 24 testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } - buildTypes { - release { - isMinifyEnabled = false - proguardFiles( - getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro", - ) - } - } compileOptions { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } - kotlinOptions { - jvmTarget = "17" + kotlin { + compilerOptions { jvmTarget.set(JvmTarget.JVM_17) } } buildFeatures { compose = true @@ -52,29 +46,25 @@ android { } dependencies { - implementation(libs.androidx.core.ktx) - implementation(libs.androidx.appcompat) + androidTestImplementation(libs.androidx.espresso.core) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.ui.test.junit4) + androidTestImplementation(platform(libs.androidx.compose.bom)) implementation(libs.androidx.activity.compose) - implementation(libs.androidx.material3) + implementation(libs.androidx.appcompat) + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.lifecycle.runtime.compose) implementation(libs.androidx.material.icons.extended) - implementation(platform(libs.androidx.compose.bom)) + implementation(libs.androidx.material3) + implementation(libs.androidx.media3.exoplayer) + implementation(libs.androidx.media3.ui) + implementation(libs.androidx.ui.tooling.preview.android) + implementation(libs.firebase.ai) + implementation(libs.firebase.common.ktx) implementation(libs.hilt.android) implementation(libs.hilt.navigation.compose) - implementation(libs.firebase.common.ktx) - implementation(libs.androidx.lifecycle.runtime.compose) - implementation(libs.androidx.ui.tooling.preview.android) - ksp(libs.hilt.compiler) + implementation(platform(libs.androidx.compose.bom)) implementation(platform(libs.firebase.bom)) - implementation(libs.firebase.ai) - - // Media3 ExoPlayer - implementation(libs.androidx.media3.exoplayer) - implementation(libs.androidx.media3.ui) - implementation(project(":ui-component")) - - androidTestImplementation(libs.androidx.junit) - androidTestImplementation(libs.androidx.espresso.core) - androidTestImplementation(platform(libs.androidx.compose.bom)) - androidTestImplementation(libs.androidx.ui.test.junit4) + ksp(libs.hilt.compiler) } diff --git a/samples/gemini-video-summarization/proguard-rules.pro b/samples/gemini-video-summarization/proguard-rules.pro deleted file mode 100644 index 481bb434..00000000 --- a/samples/gemini-video-summarization/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# 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 \ No newline at end of file diff --git a/samples/gemini-video-summarization/src/main/java/com/android/ai/samples/geminivideosummary/ui/OutputTextDisplay.kt b/samples/gemini-video-summarization/src/main/kotlin/com/android/ai/samples/geminivideosummary/ui/OutputTextDisplay.kt similarity index 100% rename from samples/gemini-video-summarization/src/main/java/com/android/ai/samples/geminivideosummary/ui/OutputTextDisplay.kt rename to samples/gemini-video-summarization/src/main/kotlin/com/android/ai/samples/geminivideosummary/ui/OutputTextDisplay.kt diff --git a/samples/gemini-video-summarization/src/main/java/com/android/ai/samples/geminivideosummary/ui/SummarizationSheet.kt b/samples/gemini-video-summarization/src/main/kotlin/com/android/ai/samples/geminivideosummary/ui/SummarizationSheet.kt similarity index 100% rename from samples/gemini-video-summarization/src/main/java/com/android/ai/samples/geminivideosummary/ui/SummarizationSheet.kt rename to samples/gemini-video-summarization/src/main/kotlin/com/android/ai/samples/geminivideosummary/ui/SummarizationSheet.kt diff --git a/samples/gemini-video-summarization/src/main/java/com/android/ai/samples/geminivideosummary/ui/TextToSpeechControls.kt b/samples/gemini-video-summarization/src/main/kotlin/com/android/ai/samples/geminivideosummary/ui/TextToSpeechControls.kt similarity index 100% rename from samples/gemini-video-summarization/src/main/java/com/android/ai/samples/geminivideosummary/ui/TextToSpeechControls.kt rename to samples/gemini-video-summarization/src/main/kotlin/com/android/ai/samples/geminivideosummary/ui/TextToSpeechControls.kt diff --git a/samples/gemini-video-summarization/src/main/java/com/android/ai/samples/geminivideosummary/ui/VideoSummarizationScreen.kt b/samples/gemini-video-summarization/src/main/kotlin/com/android/ai/samples/geminivideosummary/ui/VideoSummarizationScreen.kt similarity index 98% rename from samples/gemini-video-summarization/src/main/java/com/android/ai/samples/geminivideosummary/ui/VideoSummarizationScreen.kt rename to samples/gemini-video-summarization/src/main/kotlin/com/android/ai/samples/geminivideosummary/ui/VideoSummarizationScreen.kt index ad2d53fb..e7cfdc93 100644 --- a/samples/gemini-video-summarization/src/main/java/com/android/ai/samples/geminivideosummary/ui/VideoSummarizationScreen.kt +++ b/samples/gemini-video-summarization/src/main/kotlin/com/android/ai/samples/geminivideosummary/ui/VideoSummarizationScreen.kt @@ -48,7 +48,7 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewScreenSizes import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel +import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.media3.common.MediaItem import androidx.media3.exoplayer.ExoPlayer @@ -73,7 +73,7 @@ import com.google.com.android.ai.samples.geminivideosummary.R * This screen allows users to select a video, play it, and generate a summary of its content * using Firebase AI. It also provides text-to-speech functionality to read out */ -@OptIn(ExperimentalMaterial3Api::class) + @Composable fun VideoSummarizationScreen(viewModel: VideoSummarizationViewModel = hiltViewModel()) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() @@ -192,7 +192,6 @@ private fun VideoSummarizationScreen( } } -@OptIn(ExperimentalMaterial3Api::class) @Composable private fun SummarizationSection( uiState: VideoSummarizationState, @@ -284,7 +283,6 @@ private fun SummarizationSection( @PreviewScreenSizes @Composable -@OptIn(ExperimentalMaterial3Api::class) private fun VideoSummarizationScreenPreview() { AISampleCatalogTheme { VideoSummarizationScreen( diff --git a/samples/gemini-video-summarization/src/main/java/com/android/ai/samples/geminivideosummary/util/VideoList.kt b/samples/gemini-video-summarization/src/main/kotlin/com/android/ai/samples/geminivideosummary/util/VideoList.kt similarity index 100% rename from samples/gemini-video-summarization/src/main/java/com/android/ai/samples/geminivideosummary/util/VideoList.kt rename to samples/gemini-video-summarization/src/main/kotlin/com/android/ai/samples/geminivideosummary/util/VideoList.kt diff --git a/samples/gemini-video-summarization/src/main/java/com/android/ai/samples/geminivideosummary/viewmodel/VideoSummarizationViewModel.kt b/samples/gemini-video-summarization/src/main/kotlin/com/android/ai/samples/geminivideosummary/viewmodel/VideoSummarizationViewModel.kt similarity index 100% rename from samples/gemini-video-summarization/src/main/java/com/android/ai/samples/geminivideosummary/viewmodel/VideoSummarizationViewModel.kt rename to samples/gemini-video-summarization/src/main/kotlin/com/android/ai/samples/geminivideosummary/viewmodel/VideoSummarizationViewModel.kt diff --git a/samples/genai-image-description/README.md b/samples/genai-image-description/README.md index 7737c232..a16657a7 100644 --- a/samples/genai-image-description/README.md +++ b/samples/genai-image-description/README.md @@ -12,7 +12,7 @@ This sample demonstrates how to generate short descriptions of images on-device ## How it works -The application uses the ML Kit GenAI Image Description API to interact with the on-device Gemini Nano model. The core logic is in the [`GenAIImageDescriptionViewModel.kt`](https://github.com/android/ai-samples/blob/main/samples/genai-image-description/src/main/java/com/android/ai/samples/genai_image_description/GenAIImageDescriptionViewModel.kt) file. An `ImageDescriber` client is initialized. When a user provides an image, it's converted to a bitmap and sent to the `runInference` method, which streams back the generated description. +The application uses the ML Kit GenAI Image Description API to interact with the on-device Gemini Nano model. The core logic is in the [`GenAIImageDescriptionViewModel.kt`](https://github.com/android/ai-samples/blob/main/samples/genai-image-description/src/main/kotlincom/android/ai/samples/genai_image_description/GenAIImageDescriptionViewModel.kt) file. An `ImageDescriber` client is initialized. When a user provides an image, it's converted to a bitmap and sent to the `runInference` method, which streams back the generated description. Here is the key snippet of code that calls the generative model: diff --git a/samples/genai-image-description/build.gradle.kts b/samples/genai-image-description/build.gradle.kts index 246f9daa..9a8c992f 100644 --- a/samples/genai-image-description/build.gradle.kts +++ b/samples/genai-image-description/build.gradle.kts @@ -14,6 +14,8 @@ * limitations under the License. */ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + plugins { alias(libs.plugins.android.library) alias(libs.plugins.jetbrains.kotlin.android) @@ -22,8 +24,8 @@ plugins { } android { - namespace = "com.android.ai.samples.geminimultimodal" - compileSdk = 35 + namespace = "com.android.ai.samples.imagedescription" + compileSdk = 36 buildFeatures { compose = true @@ -33,18 +35,8 @@ android { minSdk = 26 testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles("consumer-rules.pro") } - buildTypes { - release { - isMinifyEnabled = false - proguardFiles( - getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro", - ) - } - } compileOptions { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 @@ -52,29 +44,28 @@ android { composeOptions { kotlinCompilerExtensionVersion = "1.5.15" } - kotlinOptions { - jvmTarget = "17" + kotlin { + compilerOptions { jvmTarget.set(JvmTarget.JVM_17) } } } dependencies { - - implementation(libs.androidx.core.ktx) - implementation(libs.androidx.appcompat) - implementation(libs.androidx.material3) + debugImplementation(libs.ui.tooling) implementation(libs.androidx.activity.compose) - implementation(platform(libs.androidx.compose.bom)) + implementation(libs.androidx.appcompat) + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.lifecycle.runtime.compose) implementation(libs.androidx.material.icons.extended) implementation(libs.androidx.material.icons.extended) - implementation(libs.hilt.android) - implementation(libs.hilt.navigation.compose) + implementation(libs.androidx.material3) implementation(libs.androidx.runtime.livedata) - implementation(libs.genai.image.description) implementation(libs.coil.compose) + implementation(libs.genai.image.description) + implementation(libs.hilt.android) + implementation(libs.hilt.navigation.compose) implementation(libs.kotlinx.coroutines.guava) - implementation(libs.androidx.lifecycle.runtime.compose) implementation(libs.ui.tooling.preview) - debugImplementation(libs.ui.tooling) - ksp(libs.hilt.compiler) + implementation(platform(libs.androidx.compose.bom)) implementation(project(":ui-component")) + ksp(libs.hilt.compiler) } diff --git a/samples/genai-image-description/consumer-rules.pro b/samples/genai-image-description/consumer-rules.pro deleted file mode 100644 index e69de29b..00000000 diff --git a/samples/genai-image-description/proguard-rules.pro b/samples/genai-image-description/proguard-rules.pro deleted file mode 100644 index 481bb434..00000000 --- a/samples/genai-image-description/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# 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 \ No newline at end of file diff --git a/samples/genai-image-description/src/main/java/com/android/ai/samples/genai_image_description/GenAIImageDescriptionScreen.kt b/samples/genai-image-description/src/main/kotlin/com/android/ai/samples/genai_image_description/GenAIImageDescriptionScreen.kt similarity index 97% rename from samples/genai-image-description/src/main/java/com/android/ai/samples/genai_image_description/GenAIImageDescriptionScreen.kt rename to samples/genai-image-description/src/main/kotlin/com/android/ai/samples/genai_image_description/GenAIImageDescriptionScreen.kt index 3018fc43..5770a874 100644 --- a/samples/genai-image-description/src/main/java/com/android/ai/samples/genai_image_description/GenAIImageDescriptionScreen.kt +++ b/samples/genai-image-description/src/main/kotlin/com/android/ai/samples/genai_image_description/GenAIImageDescriptionScreen.kt @@ -54,11 +54,10 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewScreenSizes import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel +import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle -import androidx.lifecycle.viewmodel.compose.viewModel import coil3.compose.AsyncImage -import com.android.ai.samples.geminimultimodal.R +import com.android.ai.samples.imagedescription.R import com.android.ai.theme.AISampleCatalogTheme import com.android.ai.theme.extendedColorScheme import com.android.ai.uicomponent.GenerateButton @@ -66,7 +65,6 @@ import com.android.ai.uicomponent.PrimaryButton import com.android.ai.uicomponent.SampleDetailTopAppBar import com.android.ai.uicomponent.UndoButton -@OptIn(ExperimentalMaterial3Api::class) @Composable fun GenAIImageDescriptionScreen(viewModel: GenAIImageDescriptionViewModel = hiltViewModel()) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() @@ -233,7 +231,6 @@ private fun GenAIImageDescriptionScreen( @PreviewScreenSizes @Composable -@OptIn(ExperimentalMaterial3Api::class) private fun GenAIImageDescriptionScreenPreview() { AISampleCatalogTheme { GenAIImageDescriptionScreen( diff --git a/samples/genai-image-description/src/main/java/com/android/ai/samples/genai_image_description/GenAIImageDescriptionViewModel.kt b/samples/genai-image-description/src/main/kotlin/com/android/ai/samples/genai_image_description/GenAIImageDescriptionViewModel.kt similarity index 99% rename from samples/genai-image-description/src/main/java/com/android/ai/samples/genai_image_description/GenAIImageDescriptionViewModel.kt rename to samples/genai-image-description/src/main/kotlin/com/android/ai/samples/genai_image_description/GenAIImageDescriptionViewModel.kt index 6e5ef445..06297d69 100644 --- a/samples/genai-image-description/src/main/java/com/android/ai/samples/genai_image_description/GenAIImageDescriptionViewModel.kt +++ b/samples/genai-image-description/src/main/kotlin/com/android/ai/samples/genai_image_description/GenAIImageDescriptionViewModel.kt @@ -22,7 +22,7 @@ import android.util.Log import androidx.annotation.StringRes import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope -import com.android.ai.samples.geminimultimodal.R +import com.android.ai.samples.imagedescription.R import com.google.mlkit.genai.common.DownloadCallback import com.google.mlkit.genai.common.FeatureStatus import com.google.mlkit.genai.common.GenAiException diff --git a/samples/genai-summarization/README.md b/samples/genai-summarization/README.md index f5d07bd2..28f6eba1 100644 --- a/samples/genai-summarization/README.md +++ b/samples/genai-summarization/README.md @@ -14,7 +14,7 @@ This sample demonstrates how to summarize articles and conversations on-device u The application uses the ML Kit GenAI Summarization API to interact with the on-device Gemini Nano model. The core logic is in the `GenAISummarizationViewModel.kt` file. A `Summarizer` client is initialized. When a user provides text, it's passed to the `runInference` method, which streams back the generated summary. -Here is the key snippet of code that calls the generative model from [`GenAISummarizationViewModel.kt`](./src/main/java/com/android/ai/samples/genai_summarization/GenAISummarizationViewModel.kt): +Here is the key snippet of code that calls the generative model from [`GenAISummarizationViewModel.kt`](./src/main/kotlincom/android/ai/samples/genai_summarization/GenAISummarizationViewModel.kt): ```kotlin private suspend fun generateSummarization(summarizer: Summarizer, textToSummarize: String) { diff --git a/samples/genai-summarization/build.gradle.kts b/samples/genai-summarization/build.gradle.kts index 22d965c2..584e851f 100644 --- a/samples/genai-summarization/build.gradle.kts +++ b/samples/genai-summarization/build.gradle.kts @@ -14,6 +14,8 @@ * limitations under the License. */ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + plugins { alias(libs.plugins.android.library) alias(libs.plugins.jetbrains.kotlin.android) @@ -22,8 +24,8 @@ plugins { } android { - namespace = "com.android.ai.samples.geminimultimodal" - compileSdk = 35 + namespace = "com.android.ai.samples.summarization" + compileSdk = 36 buildFeatures { compose = true @@ -33,18 +35,8 @@ android { minSdk = 26 testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles("consumer-rules.pro") } - buildTypes { - release { - isMinifyEnabled = false - proguardFiles( - getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro", - ) - } - } compileOptions { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 @@ -52,27 +44,26 @@ android { composeOptions { kotlinCompilerExtensionVersion = "1.5.15" } - kotlinOptions { - jvmTarget = "17" + kotlin { + compilerOptions { jvmTarget.set(JvmTarget.JVM_17) } } } dependencies { - - implementation(libs.androidx.core.ktx) - implementation(libs.androidx.appcompat) - implementation(libs.androidx.material3) implementation(libs.androidx.activity.compose) - implementation(platform(libs.androidx.compose.bom)) + implementation(libs.androidx.appcompat) + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.lifecycle.runtime.compose) implementation(libs.androidx.material.icons.extended) implementation(libs.androidx.material.icons.extended) - implementation(libs.hilt.android) - implementation(libs.hilt.navigation.compose) + implementation(libs.androidx.material3) implementation(libs.androidx.runtime.livedata) implementation(libs.genai.summarization) + implementation(libs.hilt.android) + implementation(libs.hilt.navigation.compose) implementation(libs.kotlinx.coroutines.guava) - implementation(libs.androidx.lifecycle.runtime.compose) + implementation(libs.ui.tooling) + implementation(platform(libs.androidx.compose.bom)) implementation(project(":ui-component")) - debugImplementation(libs.ui.tooling) ksp(libs.hilt.compiler) } diff --git a/samples/genai-summarization/consumer-rules.pro b/samples/genai-summarization/consumer-rules.pro deleted file mode 100644 index e69de29b..00000000 diff --git a/samples/genai-summarization/proguard-rules.pro b/samples/genai-summarization/proguard-rules.pro deleted file mode 100644 index 481bb434..00000000 --- a/samples/genai-summarization/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# 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 \ No newline at end of file diff --git a/samples/genai-summarization/src/main/java/com/android/ai/samples/genai_summarization/GenAISummarizationScreen.kt b/samples/genai-summarization/src/main/kotlin/com/android/ai/samples/genai_summarization/GenAISummarizationScreen.kt similarity index 98% rename from samples/genai-summarization/src/main/java/com/android/ai/samples/genai_summarization/GenAISummarizationScreen.kt rename to samples/genai-summarization/src/main/kotlin/com/android/ai/samples/genai_summarization/GenAISummarizationScreen.kt index 00a33940..03d26c16 100644 --- a/samples/genai-summarization/src/main/java/com/android/ai/samples/genai_summarization/GenAISummarizationScreen.kt +++ b/samples/genai-summarization/src/main/kotlin/com/android/ai/samples/genai_summarization/GenAISummarizationScreen.kt @@ -30,7 +30,6 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.Undo import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text @@ -51,9 +50,9 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import androidx.hilt.navigation.compose.hiltViewModel +import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.android.ai.samples.geminimultimodal.R +import com.android.ai.samples.summarization.R import com.android.ai.theme.AISampleCatalogTheme import com.android.ai.theme.surfaceContainerHighestLight import com.android.ai.uicomponent.BackButton @@ -61,7 +60,6 @@ import com.android.ai.uicomponent.GenerateButton import com.android.ai.uicomponent.SampleDetailTopAppBar import com.android.ai.uicomponent.SecondaryButton -@OptIn(ExperimentalMaterial3Api::class) @Composable fun GenAISummarizationScreen(viewModel: GenAISummarizationViewModel = hiltViewModel()) { val sampleTextOptions = stringArrayResource(R.array.summarization_sample_text) @@ -215,7 +213,6 @@ fun GenAISummarizationContent( } } -@OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable fun DisplayedText(textToDisplay: String, modifier: Modifier = Modifier, isStatusText: Boolean = false) { Text( diff --git a/samples/genai-summarization/src/main/java/com/android/ai/samples/genai_summarization/GenAISummarizationViewModel.kt b/samples/genai-summarization/src/main/kotlin/com/android/ai/samples/genai_summarization/GenAISummarizationViewModel.kt similarity index 99% rename from samples/genai-summarization/src/main/java/com/android/ai/samples/genai_summarization/GenAISummarizationViewModel.kt rename to samples/genai-summarization/src/main/kotlin/com/android/ai/samples/genai_summarization/GenAISummarizationViewModel.kt index e3f30e43..310f15fd 100644 --- a/samples/genai-summarization/src/main/java/com/android/ai/samples/genai_summarization/GenAISummarizationViewModel.kt +++ b/samples/genai-summarization/src/main/kotlin/com/android/ai/samples/genai_summarization/GenAISummarizationViewModel.kt @@ -19,7 +19,7 @@ import android.app.Application import android.util.Log import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope -import com.android.ai.samples.geminimultimodal.R +import com.android.ai.samples.summarization.R import com.google.mlkit.genai.common.DownloadCallback import com.google.mlkit.genai.common.FeatureStatus import com.google.mlkit.genai.common.GenAiException diff --git a/samples/genai-writing-assistance/README.md b/samples/genai-writing-assistance/README.md index f1dfff24..2e247921 100644 --- a/samples/genai-writing-assistance/README.md +++ b/samples/genai-writing-assistance/README.md @@ -12,9 +12,9 @@ This sample demonstrates how to proofread and rewrite short content on-device us ## How it works -The application uses the ML Kit GenAI Proofreading and Rewriting APIs to interact with the on-device Gemini Nano model. The core logic is in the [`GenAIWritingAssistanceViewModel.kt`](https://github.com/android/ai-samples/blob/main/samples/genai-writing-assistance/src/main/java/com/android/ai/samples/genai_writing_assistance/GenAIWritingAssistanceViewModel.kt) file. `Proofreader` and `Rewriter` clients are initialized. When a user provides text, it's passed to either the `runProofreadingInference` or `runRewritingInference` method, which then returns the polished text. +The application uses the ML Kit GenAI Proofreading and Rewriting APIs to interact with the on-device Gemini Nano model. The core logic is in the [`GenAIWritingAssistanceViewModel.kt`](https://github.com/android/ai-samples/blob/main/samples/genai-writing-assistance/src/main/kotlincom/android/ai/samples/genai_writing_assistance/GenAIWritingAssistanceViewModel.kt) file. `Proofreader` and `Rewriter` clients are initialized. When a user provides text, it's passed to either the `runProofreadingInference` or `runRewritingInference` method, which then returns the polished text. -Here is the key snippet of code that runs the proofreading inference from [`GenAIWritingAssistanceViewModel.kt`](.src/main/java/com/android/ai/samples/genai_writing_assistance/GenAIWritingAssistanceViewModel.kt): +Here is the key snippet of code that runs the proofreading inference from [`GenAIWritingAssistanceViewModel.kt`](.src/main/kotlincom/android/ai/samples/genai_writing_assistance/GenAIWritingAssistanceViewModel.kt): ```kotlin private suspend fun runProofreadingInference(textToProofread: String) { diff --git a/samples/genai-writing-assistance/build.gradle.kts b/samples/genai-writing-assistance/build.gradle.kts index aae39550..3b1dc04a 100644 --- a/samples/genai-writing-assistance/build.gradle.kts +++ b/samples/genai-writing-assistance/build.gradle.kts @@ -14,6 +14,8 @@ * limitations under the License. */ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + plugins { alias(libs.plugins.android.library) alias(libs.plugins.jetbrains.kotlin.android) @@ -22,8 +24,8 @@ plugins { } android { - namespace = "com.android.ai.samples.geminimultimodal" - compileSdk = 35 + namespace = "com.android.ai.samples.writingassistance" + compileSdk = 36 buildFeatures { compose = true @@ -33,44 +35,34 @@ android { minSdk = 26 testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles("consumer-rules.pro") } - buildTypes { - release { - isMinifyEnabled = false - proguardFiles( - getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro", - ) - } - } compileOptions { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } - kotlinOptions { - jvmTarget = "17" + kotlin { + compilerOptions { jvmTarget.set(JvmTarget.JVM_17) } } } dependencies { - implementation(libs.androidx.core.ktx) - implementation(libs.androidx.appcompat) - implementation(libs.androidx.material3) implementation(libs.androidx.activity.compose) - implementation(platform(libs.androidx.compose.bom)) + implementation(libs.androidx.appcompat) + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.lifecycle.runtime.compose) implementation(libs.androidx.material.icons.extended) implementation(libs.androidx.material.icons.extended) - implementation(libs.hilt.android) - implementation(libs.hilt.navigation.compose) + implementation(libs.androidx.material3) implementation(libs.androidx.runtime.livedata) implementation(libs.genai.proofreading) implementation(libs.genai.rewrite) + implementation(libs.hilt.android) + implementation(libs.hilt.navigation.compose) implementation(libs.kotlinx.coroutines.guava) - implementation(libs.androidx.lifecycle.runtime.compose) + implementation(libs.ui.tooling) + implementation(platform(libs.androidx.compose.bom)) implementation(project(":ui-component")) - debugImplementation(libs.ui.tooling) ksp(libs.hilt.compiler) } diff --git a/samples/genai-writing-assistance/consumer-rules.pro b/samples/genai-writing-assistance/consumer-rules.pro deleted file mode 100644 index e69de29b..00000000 diff --git a/samples/genai-writing-assistance/proguard-rules.pro b/samples/genai-writing-assistance/proguard-rules.pro deleted file mode 100644 index 481bb434..00000000 --- a/samples/genai-writing-assistance/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# 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 \ No newline at end of file diff --git a/samples/genai-writing-assistance/src/main/java/com/android/ai/samples/genai_writing_assistance/GenAIWritingAssistanceScreen.kt b/samples/genai-writing-assistance/src/main/kotlin/com/android/ai/samples/genai_writing_assistance/GenAIWritingAssistanceScreen.kt similarity index 98% rename from samples/genai-writing-assistance/src/main/java/com/android/ai/samples/genai_writing_assistance/GenAIWritingAssistanceScreen.kt rename to samples/genai-writing-assistance/src/main/kotlin/com/android/ai/samples/genai_writing_assistance/GenAIWritingAssistanceScreen.kt index 33f525bb..4dc0eca9 100644 --- a/samples/genai-writing-assistance/src/main/java/com/android/ai/samples/genai_writing_assistance/GenAIWritingAssistanceScreen.kt +++ b/samples/genai-writing-assistance/src/main/kotlin/com/android/ai/samples/genai_writing_assistance/GenAIWritingAssistanceScreen.kt @@ -34,7 +34,6 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Card import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.MaterialTheme import androidx.compose.material3.RadioButton import androidx.compose.material3.Scaffold @@ -61,9 +60,9 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.window.Dialog -import androidx.hilt.navigation.compose.hiltViewModel +import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.android.ai.samples.geminimultimodal.R +import com.android.ai.samples.writingassistance.R import com.android.ai.theme.AISampleCatalogTheme import com.android.ai.theme.surfaceContainerHighestLight import com.android.ai.uicomponent.GenerateButton @@ -72,7 +71,6 @@ import com.android.ai.uicomponent.SecondaryButton import com.android.ai.uicomponent.UndoButton import com.google.mlkit.genai.rewriting.RewriterOptions -@OptIn(ExperimentalMaterial3Api::class) @Composable fun GenAIWritingAssistanceScreen(viewModel: GenAIWritingAssistanceViewModel = hiltViewModel()) { var showRewriteOptionsDialog by rememberSaveable { mutableStateOf(false) } @@ -319,7 +317,6 @@ fun RewriteOptionsDialog(onConfirm: (rewriteStyle: RewriteStyle) -> Unit, onDism } } -@OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable fun DisplayedText(textToDisplay: String, modifier: Modifier = Modifier, isStatusText: Boolean = false) { Text( diff --git a/samples/genai-writing-assistance/src/main/java/com/android/ai/samples/genai_writing_assistance/GenAIWritingAssistanceViewModel.kt b/samples/genai-writing-assistance/src/main/kotlin/com/android/ai/samples/genai_writing_assistance/GenAIWritingAssistanceViewModel.kt similarity index 98% rename from samples/genai-writing-assistance/src/main/java/com/android/ai/samples/genai_writing_assistance/GenAIWritingAssistanceViewModel.kt rename to samples/genai-writing-assistance/src/main/kotlin/com/android/ai/samples/genai_writing_assistance/GenAIWritingAssistanceViewModel.kt index c4b17e0f..16e8a966 100644 --- a/samples/genai-writing-assistance/src/main/java/com/android/ai/samples/genai_writing_assistance/GenAIWritingAssistanceViewModel.kt +++ b/samples/genai-writing-assistance/src/main/kotlin/com/android/ai/samples/genai_writing_assistance/GenAIWritingAssistanceViewModel.kt @@ -21,7 +21,7 @@ import android.util.Log import androidx.annotation.StringRes import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope -import com.android.ai.samples.geminimultimodal.R +import com.android.ai.samples.writingassistance.R import com.google.mlkit.genai.common.DownloadCallback import com.google.mlkit.genai.common.FeatureStatus import com.google.mlkit.genai.common.GenAiException @@ -50,7 +50,7 @@ sealed class GenAIWritingAssistanceUiState { data object Generating : GenAIWritingAssistanceUiState() data class Success(val generatedOutput: String) : GenAIWritingAssistanceUiState() - data class Error(@StringRes val errorMessageStringRes: Int) : GenAIWritingAssistanceUiState() + data class Error(@param:StringRes val errorMessageStringRes: Int) : GenAIWritingAssistanceUiState() } class GenAIWritingAssistanceViewModel @Inject constructor(context: Application) : AndroidViewModel(context) { diff --git a/samples/imagen-editing/README.md b/samples/imagen-editing/README.md index 04cf41b2..396da0ff 100644 --- a/samples/imagen-editing/README.md +++ b/samples/imagen-editing/README.md @@ -12,9 +12,9 @@ This sample demonstrates how to edit images using the Imagen editing model. User ## How it works -The application uses the Firebase AI SDK (see [How to run](../../#how-to-run)) for Android to interact with Imagen. The core logic is in the [`ImagenEditingDataSource.kt`](https://github.com/android/ai-samples/blob/main/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/data/ImagenEditingDataSource.kt) file. It first generates a base image using the generation model. Then, for editing, it takes the source image, a user-drawn mask, and a text prompt, and sends them to the editing model's `editImage` method to perform inpainting. +The application uses the Firebase AI SDK (see [How to run](../../#how-to-run)) for Android to interact with Imagen. The core logic is in the [`ImagenEditingDataSource.kt`](https://github.com/android/ai-samples/blob/main/samples/imagen-editing/src/main/kotlincom/android/ai/samples/imagenediting/data/ImagenEditingDataSource.kt) file. It first generates a base image using the generation model. Then, for editing, it takes the source image, a user-drawn mask, and a text prompt, and sends them to the editing model's `editImage` method to perform inpainting. -Here is the key snippet of code that performs inpainting from [`ImagenEditingDataSource.kt`](./src/main/java/com/android/ai/samples/imagenediting/data/ImagenEditingDataSource.kt): +Here is the key snippet of code that performs inpainting from [`ImagenEditingDataSource.kt`](./src/main/kotlincom/android/ai/samples/imagenediting/data/ImagenEditingDataSource.kt): ```kotlin @OptIn(PublicPreviewAPI::class) diff --git a/samples/imagen-editing/build.gradle.kts b/samples/imagen-editing/build.gradle.kts index bf26d7b5..5073de3a 100644 --- a/samples/imagen-editing/build.gradle.kts +++ b/samples/imagen-editing/build.gradle.kts @@ -13,6 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + plugins { alias(libs.plugins.android.library) alias(libs.plugins.jetbrains.kotlin.android) @@ -31,17 +34,6 @@ android { defaultConfig { minSdk = 24 testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles("consumer-rules.pro") - } - - buildTypes { - release { - isMinifyEnabled = false - proguardFiles( - getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro", - ) - } } compileOptions { @@ -49,8 +41,8 @@ android { targetCompatibility = JavaVersion.VERSION_17 } - kotlinOptions { - jvmTarget = "17" + kotlin { + compilerOptions { jvmTarget.set(JvmTarget.JVM_17) } } lint { @@ -59,22 +51,21 @@ android { } dependencies { - implementation(libs.androidx.core.ktx) + androidTestImplementation(libs.androidx.espresso.core) + androidTestImplementation(libs.androidx.junit) + debugImplementation(libs.ui.tooling) implementation(libs.androidx.appcompat) - implementation(libs.androidx.material3) - implementation(platform(libs.androidx.compose.bom)) + implementation(libs.androidx.core.ktx) implementation(libs.androidx.material.icons.extended) - implementation(platform(libs.firebase.bom)) + implementation(libs.androidx.material3) + implementation(libs.androidx.runtime.livedata) implementation(libs.firebase.ai) implementation(libs.hilt.android) implementation(libs.hilt.navigation.compose) - implementation(libs.androidx.runtime.livedata) implementation(libs.ui.tooling.preview) + implementation(platform(libs.androidx.compose.bom)) + implementation(platform(libs.firebase.bom)) implementation(project(":ui-component")) - debugImplementation(libs.ui.tooling) ksp(libs.hilt.compiler) - testImplementation(libs.junit) - androidTestImplementation(libs.androidx.junit) - androidTestImplementation(libs.androidx.espresso.core) } diff --git a/samples/imagen-editing/consumer-rules.pro b/samples/imagen-editing/consumer-rules.pro deleted file mode 100644 index e69de29b..00000000 diff --git a/samples/imagen-editing/proguard-rules.pro b/samples/imagen-editing/proguard-rules.pro deleted file mode 100644 index 481bb434..00000000 --- a/samples/imagen-editing/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# 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 \ No newline at end of file diff --git a/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/data/ImagenEditingDataSource.kt b/samples/imagen-editing/src/main/kotlin/com/android/ai/samples/imagenediting/data/ImagenEditingDataSource.kt similarity index 100% rename from samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/data/ImagenEditingDataSource.kt rename to samples/imagen-editing/src/main/kotlin/com/android/ai/samples/imagenediting/data/ImagenEditingDataSource.kt diff --git a/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingMaskEditor.kt b/samples/imagen-editing/src/main/kotlin/com/android/ai/samples/imagenediting/ui/ImagenEditingMaskEditor.kt similarity index 100% rename from samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingMaskEditor.kt rename to samples/imagen-editing/src/main/kotlin/com/android/ai/samples/imagenediting/ui/ImagenEditingMaskEditor.kt diff --git a/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingScreen.kt b/samples/imagen-editing/src/main/kotlin/com/android/ai/samples/imagenediting/ui/ImagenEditingScreen.kt similarity index 97% rename from samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingScreen.kt rename to samples/imagen-editing/src/main/kotlin/com/android/ai/samples/imagenediting/ui/ImagenEditingScreen.kt index 9699e7aa..9114bcc1 100644 --- a/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingScreen.kt +++ b/samples/imagen-editing/src/main/kotlin/com/android/ai/samples/imagenediting/ui/ImagenEditingScreen.kt @@ -54,20 +54,19 @@ import androidx.compose.ui.graphics.ShaderBrush import androidx.compose.ui.graphics.TileMode import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalResources import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.platform.SoftwareKeyboardController import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel +import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.ai.samples.imagenediting.R import com.android.ai.uicomponent.GenerateButton import com.android.ai.uicomponent.SampleDetailTopAppBar import com.android.ai.uicomponent.TextInput -@OptIn(ExperimentalMaterial3Api::class) @Composable fun ImagenEditingScreen(viewModel: ImagenEditingViewModel = hiltViewModel()) { val uiState: ImagenEditingUIState by viewModel.uiState.collectAsStateWithLifecycle() @@ -113,9 +112,9 @@ private fun ImagenEditingScreenContent( }, modifier = Modifier.fillMaxWidth(), ) { innerPadding -> - val context = LocalContext.current + val resources = LocalResources.current val imageBitmap = remember { - val bitmap = BitmapFactory.decodeResource(context.resources, com.android.ai.uicomponent.R.drawable.img_fill) + val bitmap = BitmapFactory.decodeResource(resources, com.android.ai.uicomponent.R.drawable.img_fill) bitmap.asImageBitmap() } val imageShader = remember { diff --git a/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingUIState.kt b/samples/imagen-editing/src/main/kotlin/com/android/ai/samples/imagenediting/ui/ImagenEditingUIState.kt similarity index 100% rename from samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingUIState.kt rename to samples/imagen-editing/src/main/kotlin/com/android/ai/samples/imagenediting/ui/ImagenEditingUIState.kt diff --git a/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingViewModel.kt b/samples/imagen-editing/src/main/kotlin/com/android/ai/samples/imagenediting/ui/ImagenEditingViewModel.kt similarity index 100% rename from samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingViewModel.kt rename to samples/imagen-editing/src/main/kotlin/com/android/ai/samples/imagenediting/ui/ImagenEditingViewModel.kt diff --git a/samples/imagen/README.md b/samples/imagen/README.md index 9adfa02e..393269d1 100644 --- a/samples/imagen/README.md +++ b/samples/imagen/README.md @@ -12,9 +12,9 @@ This sample demonstrates how to generate images from text prompts using the Imag ## How it works -The application uses the Firebase AI SDK (see [How to run](../../#how-to-run)) for Android to interact with Imagen. The core logic is in the [`ImagenDataSource.kt`](./src/main/java/com/android/ai/samples/imagen/data/ImagenDataSource.kt) file. An `imagenModel` is initialized with specific generation configurations (e.g., number of images, aspect ratio, image format). When a user provides a text prompt, it's passed to the `generateImages` method, which returns the generated image as a bitmap. +The application uses the Firebase AI SDK (see [How to run](../../#how-to-run)) for Android to interact with Imagen. The core logic is in the [`ImagenDataSource.kt`](./src/main/kotlincom/android/ai/samples/imagen/data/ImagenDataSource.kt) file. An `imagenModel` is initialized with specific generation configurations (e.g., number of images, aspect ratio, image format). When a user provides a text prompt, it's passed to the `generateImages` method, which returns the generated image as a bitmap. -Here is the key snippet of code that calls the generative model from [`ImagenDataSource.kt`](./src/main/java/com/android/ai/samples/imagen/data/ImagenDataSource.kt): +Here is the key snippet of code that calls the generative model from [`ImagenDataSource.kt`](./src/main/kotlincom/android/ai/samples/imagen/data/ImagenDataSource.kt): ```kotlin @OptIn(PublicPreviewAPI::class) diff --git a/samples/imagen/build.gradle.kts b/samples/imagen/build.gradle.kts index b2ac122a..af38a7ba 100644 --- a/samples/imagen/build.gradle.kts +++ b/samples/imagen/build.gradle.kts @@ -13,6 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + plugins { alias(libs.plugins.android.library) alias(libs.plugins.jetbrains.kotlin.android) @@ -22,7 +25,7 @@ plugins { android { namespace = "com.android.ai.samples.imagen" - compileSdk = 35 + compileSdk = 36 buildFeatures { compose = true @@ -32,17 +35,6 @@ android { minSdk = 24 testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles("consumer-rules.pro") - } - - buildTypes { - release { - isMinifyEnabled = false - proguardFiles( - getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro", - ) - } } compileOptions { @@ -50,8 +42,8 @@ android { targetCompatibility = JavaVersion.VERSION_17 } - kotlinOptions { - jvmTarget = "17" + kotlin { + compilerOptions { jvmTarget.set(JvmTarget.JVM_17) } } lint { @@ -60,24 +52,21 @@ android { } dependencies { - - implementation(libs.androidx.core.ktx) + androidTestImplementation(libs.androidx.espresso.core) + androidTestImplementation(libs.androidx.junit) + debugImplementation(libs.ui.tooling) implementation(libs.androidx.appcompat) - implementation(libs.androidx.material3) - implementation(platform(libs.androidx.compose.bom)) + implementation(libs.androidx.core.ktx) implementation(libs.androidx.material.icons.extended) - implementation(platform(libs.firebase.bom)) + implementation(libs.androidx.material3) + implementation(libs.androidx.runtime.livedata) implementation(libs.firebase.ai) implementation(libs.hilt.android) implementation(libs.hilt.navigation.compose) - implementation(libs.androidx.runtime.livedata) implementation(libs.ui.tooling.preview) - debugImplementation(libs.ui.tooling) - ksp(libs.hilt.compiler) - + implementation(platform(libs.androidx.compose.bom)) + implementation(platform(libs.firebase.bom)) implementation(project(":ui-component")) - + ksp(libs.hilt.compiler) testImplementation(libs.junit) - androidTestImplementation(libs.androidx.junit) - androidTestImplementation(libs.androidx.espresso.core) } diff --git a/samples/imagen/consumer-rules.pro b/samples/imagen/consumer-rules.pro deleted file mode 100644 index e69de29b..00000000 diff --git a/samples/imagen/proguard-rules.pro b/samples/imagen/proguard-rules.pro deleted file mode 100644 index 481bb434..00000000 --- a/samples/imagen/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# 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 \ No newline at end of file diff --git a/samples/imagen/src/main/java/com/android/ai/samples/imagen/data/ImagenDataSource.kt b/samples/imagen/src/main/kotlin/com/android/ai/samples/imagen/data/ImagenDataSource.kt similarity index 100% rename from samples/imagen/src/main/java/com/android/ai/samples/imagen/data/ImagenDataSource.kt rename to samples/imagen/src/main/kotlin/com/android/ai/samples/imagen/data/ImagenDataSource.kt diff --git a/samples/imagen/src/main/java/com/android/ai/samples/imagen/ui/GeneratedContent.kt b/samples/imagen/src/main/kotlin/com/android/ai/samples/imagen/ui/GeneratedContent.kt similarity index 100% rename from samples/imagen/src/main/java/com/android/ai/samples/imagen/ui/GeneratedContent.kt rename to samples/imagen/src/main/kotlin/com/android/ai/samples/imagen/ui/GeneratedContent.kt diff --git a/samples/imagen/src/main/java/com/android/ai/samples/imagen/ui/GenerationInput.kt b/samples/imagen/src/main/kotlin/com/android/ai/samples/imagen/ui/GenerationInput.kt similarity index 100% rename from samples/imagen/src/main/java/com/android/ai/samples/imagen/ui/GenerationInput.kt rename to samples/imagen/src/main/kotlin/com/android/ai/samples/imagen/ui/GenerationInput.kt diff --git a/samples/imagen/src/main/java/com/android/ai/samples/imagen/ui/ImagenScreen.kt b/samples/imagen/src/main/kotlin/com/android/ai/samples/imagen/ui/ImagenScreen.kt similarity index 95% rename from samples/imagen/src/main/java/com/android/ai/samples/imagen/ui/ImagenScreen.kt rename to samples/imagen/src/main/kotlin/com/android/ai/samples/imagen/ui/ImagenScreen.kt index 10afed0e..1b1019bb 100644 --- a/samples/imagen/src/main/java/com/android/ai/samples/imagen/ui/ImagenScreen.kt +++ b/samples/imagen/src/main/kotlin/com/android/ai/samples/imagen/ui/ImagenScreen.kt @@ -50,12 +50,13 @@ import androidx.compose.ui.graphics.TileMode import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalResources import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewScreenSizes import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel +import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.ai.samples.imagen.R import com.android.ai.theme.AISampleCatalogTheme @@ -63,7 +64,6 @@ import com.android.ai.uicomponent.GenerateButton import com.android.ai.uicomponent.SampleDetailTopAppBar import com.android.ai.uicomponent.TextInput -@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) @Composable fun ImagenScreen(viewModel: ImagenViewModel = hiltViewModel()) { val uiState: ImagenUIState by viewModel.uiState.collectAsStateWithLifecycle() @@ -96,9 +96,9 @@ private fun ImagenScreen(uiState: ImagenUIState, onGenerateClick: (String) -> Un modifier = Modifier.fillMaxWidth(), ) { innerPadding -> - val context = LocalContext.current + val resources = LocalResources.current val imageBitmap = remember { - val bitmap = BitmapFactory.decodeResource(context.resources, com.android.ai.uicomponent.R.drawable.img_fill) + val bitmap = BitmapFactory.decodeResource(resources, com.android.ai.uicomponent.R.drawable.img_fill) bitmap.asImageBitmap() } @@ -181,7 +181,6 @@ private fun ImagenScreen(uiState: ImagenUIState, onGenerateClick: (String) -> Un @PreviewScreenSizes @Composable -@OptIn(ExperimentalMaterial3Api::class) private fun ImagenScreenPreview() { AISampleCatalogTheme { ImagenScreen( diff --git a/samples/imagen/src/main/java/com/android/ai/samples/imagen/ui/ImagenUIState.kt b/samples/imagen/src/main/kotlin/com/android/ai/samples/imagen/ui/ImagenUIState.kt similarity index 100% rename from samples/imagen/src/main/java/com/android/ai/samples/imagen/ui/ImagenUIState.kt rename to samples/imagen/src/main/kotlin/com/android/ai/samples/imagen/ui/ImagenUIState.kt diff --git a/samples/imagen/src/main/java/com/android/ai/samples/imagen/ui/ImagenViewModel.kt b/samples/imagen/src/main/kotlin/com/android/ai/samples/imagen/ui/ImagenViewModel.kt similarity index 100% rename from samples/imagen/src/main/java/com/android/ai/samples/imagen/ui/ImagenViewModel.kt rename to samples/imagen/src/main/kotlin/com/android/ai/samples/imagen/ui/ImagenViewModel.kt diff --git a/samples/magic-selfie/README.md b/samples/magic-selfie/README.md index ef6dcd9c..6b59decf 100644 --- a/samples/magic-selfie/README.md +++ b/samples/magic-selfie/README.md @@ -12,9 +12,9 @@ This sample demonstrates how to create a "magic selfie" by replacing the backgro ## How it works -The application uses two main components. First, the ML Kit Subject Segmentation API processes the user's selfie to create a bitmap containing only the foreground (the person). Second, the Firebase AI SDK (see [How to run](../../#how-to-run)) for Android interacts with the Imagen model to generate a new background image from a user-provided text prompt. Finally, the application combines the foreground bitmap with the newly generated background to create the final magic selfie. The core logic for this process is in the [`MagicSelfieViewModel.kt`](./src/main/java/com/android/ai/samples/magicselfie/ui/MagicSelfieViewModel.kt) and [`MagicSelfieRepository.kt`](./src/main/java/com/android/ai/samples/magicselfie/data/MagicSelfieRepository.kt) files. +The application uses two main components. First, the ML Kit Subject Segmentation API processes the user's selfie to create a bitmap containing only the foreground (the person). Second, the Firebase AI SDK (see [How to run](../../#how-to-run)) for Android interacts with the Imagen model to generate a new background image from a user-provided text prompt. Finally, the application combines the foreground bitmap with the newly generated background to create the final magic selfie. The core logic for this process is in the [`MagicSelfieViewModel.kt`](./src/main/kotlincom/android/ai/samples/magicselfie/ui/MagicSelfieViewModel.kt) and [`MagicSelfieRepository.kt`](./src/main/kotlincom/android/ai/samples/magicselfie/data/MagicSelfieRepository.kt) files. -Here is the key snippet of code that orchestrates the magic selfie creation from [`MagicSelfieViewModel.kt`](./src/main/java/com/android/ai/samples/magicselfie/ui/MagicSelfieViewModel.kt): +Here is the key snippet of code that orchestrates the magic selfie creation from [`MagicSelfieViewModel.kt`](./src/main/kotlincom/android/ai/samples/magicselfie/ui/MagicSelfieViewModel.kt): ```kotlin fun createMagicSelfie(bitmap: Bitmap, prompt: String) { diff --git a/samples/magic-selfie/build.gradle.kts b/samples/magic-selfie/build.gradle.kts index 0b5cd2d6..e3ace0d8 100644 --- a/samples/magic-selfie/build.gradle.kts +++ b/samples/magic-selfie/build.gradle.kts @@ -13,6 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + plugins { alias(libs.plugins.android.library) alias(libs.plugins.jetbrains.kotlin.android) @@ -22,7 +25,7 @@ plugins { android { namespace = "com.android.ai.samples.magicselfie" - compileSdk = 35 + compileSdk = 36 buildFeatures { compose = true @@ -32,18 +35,8 @@ android { minSdk = 24 testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles("consumer-rules.pro") } - buildTypes { - release { - isMinifyEnabled = false - proguardFiles( - getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro", - ) - } - } compileOptions { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 @@ -51,33 +44,29 @@ android { composeOptions { kotlinCompilerExtensionVersion = "1.5.15" } - kotlinOptions { - jvmTarget = "17" + kotlin { + compilerOptions { jvmTarget.set(JvmTarget.JVM_17) } } } dependencies { - - implementation(libs.androidx.core.ktx) - implementation(libs.androidx.appcompat) - implementation(libs.androidx.material3) + androidTestImplementation(libs.androidx.espresso.core) + androidTestImplementation(libs.androidx.junit) + debugImplementation(libs.ui.tooling) implementation(libs.androidx.activity.compose) - implementation(platform(libs.androidx.compose.bom)) + implementation(libs.androidx.appcompat) + implementation(libs.androidx.core.ktx) implementation(libs.androidx.material.icons.extended) - implementation(platform(libs.firebase.bom)) + implementation(libs.androidx.material3) + implementation(libs.androidx.runtime.livedata) implementation(libs.firebase.ai) implementation(libs.hilt.android) implementation(libs.hilt.navigation.compose) - implementation(libs.androidx.runtime.livedata) implementation(libs.mlkit.segmentation) implementation(libs.ui.tooling.preview) - debugImplementation(libs.ui.tooling) - - ksp(libs.hilt.compiler) - + implementation(platform(libs.androidx.compose.bom)) + implementation(platform(libs.firebase.bom)) implementation(project(":ui-component")) - + ksp(libs.hilt.compiler) testImplementation(libs.junit) - androidTestImplementation(libs.androidx.junit) - androidTestImplementation(libs.androidx.espresso.core) } diff --git a/samples/magic-selfie/src/main/java/com/android/ai/samples/magicselfie/data/MagicSelfieRepository.kt b/samples/magic-selfie/src/main/kotlin/com/android/ai/samples/magicselfie/data/MagicSelfieRepository.kt similarity index 100% rename from samples/magic-selfie/src/main/java/com/android/ai/samples/magicselfie/data/MagicSelfieRepository.kt rename to samples/magic-selfie/src/main/kotlin/com/android/ai/samples/magicselfie/data/MagicSelfieRepository.kt diff --git a/samples/magic-selfie/src/main/java/com/android/ai/samples/magicselfie/ui/ImageUtils.kt b/samples/magic-selfie/src/main/kotlin/com/android/ai/samples/magicselfie/ui/ImageUtils.kt similarity index 100% rename from samples/magic-selfie/src/main/java/com/android/ai/samples/magicselfie/ui/ImageUtils.kt rename to samples/magic-selfie/src/main/kotlin/com/android/ai/samples/magicselfie/ui/ImageUtils.kt diff --git a/samples/magic-selfie/src/main/java/com/android/ai/samples/magicselfie/ui/MagicSelfieScreen.kt b/samples/magic-selfie/src/main/kotlin/com/android/ai/samples/magicselfie/ui/MagicSelfieScreen.kt similarity index 94% rename from samples/magic-selfie/src/main/java/com/android/ai/samples/magicselfie/ui/MagicSelfieScreen.kt rename to samples/magic-selfie/src/main/kotlin/com/android/ai/samples/magicselfie/ui/MagicSelfieScreen.kt index bd08381f..6d893f53 100644 --- a/samples/magic-selfie/src/main/java/com/android/ai/samples/magicselfie/ui/MagicSelfieScreen.kt +++ b/samples/magic-selfie/src/main/kotlin/com/android/ai/samples/magicselfie/ui/MagicSelfieScreen.kt @@ -20,6 +20,8 @@ import android.app.Activity import android.content.Intent import android.graphics.Bitmap import android.graphics.BitmapFactory +import android.graphics.ImageDecoder +import android.os.Build import android.provider.MediaStore import androidx.activity.compose.LocalOnBackPressedDispatcherOwner import androidx.activity.compose.rememberLauncherForActivityResult @@ -62,13 +64,14 @@ import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalResources import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewScreenSizes import androidx.compose.ui.unit.dp import androidx.core.content.FileProvider -import androidx.hilt.navigation.compose.hiltViewModel +import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import com.android.ai.samples.magicselfie.R import com.android.ai.theme.AISampleCatalogTheme import com.android.ai.uicomponent.GenerateButton @@ -78,13 +81,13 @@ import com.android.ai.uicomponent.SecondaryButton import com.android.ai.uicomponent.TextInput import java.io.File -@OptIn(ExperimentalMaterial3Api::class) @SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") @Composable fun MagicSelfieScreen(viewModel: MagicSelfieViewModel = hiltViewModel()) { val uiState by viewModel.uiState.collectAsState() val snackbarHostState = remember { SnackbarHostState() } val context = LocalContext.current + val resources = LocalResources.current var selfieBitmap by remember { mutableStateOf(null) } var tempSelfiePhotoFile by remember { mutableStateOf(null) } @@ -101,7 +104,13 @@ fun MagicSelfieScreen(viewModel: MagicSelfieViewModel = hiltViewModel()) { if (result.resultCode == Activity.RESULT_OK) { tempSelfiePhotoFile?.let { file -> val uri = FileProvider.getUriForFile(context, context.packageName + ".provider", file) - val bitmap = MediaStore.Images.Media.getBitmap(context.contentResolver, uri) + val bitmap = if (Build.VERSION.SDK_INT < 28) { + @Suppress("DEPRECATION") + MediaStore.Images.Media.getBitmap(context.contentResolver, uri) + } else { + val source = ImageDecoder.createSource(context.contentResolver, uri) + ImageDecoder.decodeBitmap(source) + } selfieBitmap = rotateImageIfRequired( file, bitmap, @@ -111,7 +120,7 @@ fun MagicSelfieScreen(viewModel: MagicSelfieViewModel = hiltViewModel()) { } if (uiState is MagicSelfieUiState.Error) { - val errorMessage = (uiState as MagicSelfieUiState.Error).message ?: context.getString(R.string.unknown_error) + val errorMessage = (uiState as MagicSelfieUiState.Error).message ?: resources.getString(R.string.unknown_error) LaunchedEffect(uiState) { snackbarHostState.showSnackbar(errorMessage) viewModel.resetError() @@ -259,7 +268,6 @@ private fun MagicSelfieScreen( @PreviewScreenSizes @Composable -@OptIn(ExperimentalMaterial3Api::class) private fun MagicSelfieScreenPreview() { AISampleCatalogTheme { MagicSelfieScreen( diff --git a/samples/magic-selfie/src/main/java/com/android/ai/samples/magicselfie/ui/MagicSelfieUiState.kt b/samples/magic-selfie/src/main/kotlin/com/android/ai/samples/magicselfie/ui/MagicSelfieUiState.kt similarity index 100% rename from samples/magic-selfie/src/main/java/com/android/ai/samples/magicselfie/ui/MagicSelfieUiState.kt rename to samples/magic-selfie/src/main/kotlin/com/android/ai/samples/magicselfie/ui/MagicSelfieUiState.kt diff --git a/samples/magic-selfie/src/main/java/com/android/ai/samples/magicselfie/ui/MagicSelfieViewModel.kt b/samples/magic-selfie/src/main/kotlin/com/android/ai/samples/magicselfie/ui/MagicSelfieViewModel.kt similarity index 100% rename from samples/magic-selfie/src/main/java/com/android/ai/samples/magicselfie/ui/MagicSelfieViewModel.kt rename to samples/magic-selfie/src/main/kotlin/com/android/ai/samples/magicselfie/ui/MagicSelfieViewModel.kt diff --git a/settings.gradle.kts b/settings.gradle.kts index 977e9376..33cc1b6b 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -51,3 +51,4 @@ include(":samples:gemini-live-todo") include(":samples:gemini-video-metadata-creation") include(":samples:gemini-image-chat") include(":ui-component") +include(":benchmark") diff --git a/ui-component/build.gradle.kts b/ui-component/build.gradle.kts index f4f78fd6..bd7e129b 100644 --- a/ui-component/build.gradle.kts +++ b/ui-component/build.gradle.kts @@ -13,6 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + plugins { alias(libs.plugins.android.library) alias(libs.plugins.jetbrains.kotlin.android) @@ -21,7 +24,7 @@ plugins { android { namespace = "com.android.ai.uicomponent" - compileSdk = 35 + compileSdk = 36 buildFeatures { compose = true @@ -31,40 +34,30 @@ android { minSdk = 24 testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles("consumer-rules.pro") } - buildTypes { - release { - isMinifyEnabled = false - proguardFiles( - getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro", - ) - } - } compileOptions { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } - kotlinOptions { - jvmTarget = "17" + kotlin { + compilerOptions { jvmTarget.set(JvmTarget.JVM_17) } } } dependencies { - implementation(libs.androidx.core.ktx) - implementation(libs.androidx.material3) - implementation(libs.androidx.material.icons.extended) + debugImplementation(libs.ui.tooling) implementation(libs.androidx.activity.compose) - implementation(platform(libs.androidx.compose.bom)) + implementation(libs.androidx.core.ktx) implementation(libs.androidx.lifecycle.runtime.compose) - implementation(libs.androidx.ui.google.fonts) + implementation(libs.androidx.material.icons.extended) + implementation(libs.androidx.material3) implementation(libs.androidx.media3.exoplayer) implementation(libs.androidx.media3.ui.compose) + implementation(libs.androidx.ui.google.fonts) implementation(libs.coil.compose) - implementation(libs.richtext.material3) implementation(libs.richtext.commonmark) + implementation(libs.richtext.material3) implementation(libs.ui.tooling.preview) - debugImplementation(libs.ui.tooling) + implementation(platform(libs.androidx.compose.bom)) } diff --git a/ui-component/src/main/java/com/android/ai/theme/Color.kt b/ui-component/src/main/kotlin/com/android/ai/theme/Color.kt similarity index 100% rename from ui-component/src/main/java/com/android/ai/theme/Color.kt rename to ui-component/src/main/kotlin/com/android/ai/theme/Color.kt diff --git a/ui-component/src/main/java/com/android/ai/theme/Theme.kt b/ui-component/src/main/kotlin/com/android/ai/theme/Theme.kt similarity index 100% rename from ui-component/src/main/java/com/android/ai/theme/Theme.kt rename to ui-component/src/main/kotlin/com/android/ai/theme/Theme.kt diff --git a/ui-component/src/main/java/com/android/ai/theme/Type.kt b/ui-component/src/main/kotlin/com/android/ai/theme/Type.kt similarity index 100% rename from ui-component/src/main/java/com/android/ai/theme/Type.kt rename to ui-component/src/main/kotlin/com/android/ai/theme/Type.kt diff --git a/ui-component/src/main/java/com/android/ai/uicomponent/Buttons.kt b/ui-component/src/main/kotlin/com/android/ai/uicomponent/Buttons.kt similarity index 99% rename from ui-component/src/main/java/com/android/ai/uicomponent/Buttons.kt rename to ui-component/src/main/kotlin/com/android/ai/uicomponent/Buttons.kt index 20e2e7cd..93a37d81 100644 --- a/ui-component/src/main/java/com/android/ai/uicomponent/Buttons.kt +++ b/ui-component/src/main/kotlin/com/android/ai/uicomponent/Buttons.kt @@ -57,7 +57,6 @@ import androidx.compose.ui.unit.dp import com.android.ai.theme.AISampleCatalogTheme import com.android.ai.theme.Contrast -@OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable fun PrimaryButton( modifier: Modifier = Modifier diff --git a/ui-component/src/main/java/com/android/ai/uicomponent/ImageInput.kt b/ui-component/src/main/kotlin/com/android/ai/uicomponent/ImageInput.kt similarity index 100% rename from ui-component/src/main/java/com/android/ai/uicomponent/ImageInput.kt rename to ui-component/src/main/kotlin/com/android/ai/uicomponent/ImageInput.kt diff --git a/ui-component/src/main/java/com/android/ai/uicomponent/MarkdownText.kt b/ui-component/src/main/kotlin/com/android/ai/uicomponent/MarkdownText.kt similarity index 100% rename from ui-component/src/main/java/com/android/ai/uicomponent/MarkdownText.kt rename to ui-component/src/main/kotlin/com/android/ai/uicomponent/MarkdownText.kt diff --git a/ui-component/src/main/java/com/android/ai/uicomponent/Message.kt b/ui-component/src/main/kotlin/com/android/ai/uicomponent/Message.kt similarity index 100% rename from ui-component/src/main/java/com/android/ai/uicomponent/Message.kt rename to ui-component/src/main/kotlin/com/android/ai/uicomponent/Message.kt diff --git a/ui-component/src/main/java/com/android/ai/uicomponent/SampleDetailTopAppBar.kt b/ui-component/src/main/kotlin/com/android/ai/uicomponent/SampleDetailTopAppBar.kt similarity index 96% rename from ui-component/src/main/java/com/android/ai/uicomponent/SampleDetailTopAppBar.kt rename to ui-component/src/main/kotlin/com/android/ai/uicomponent/SampleDetailTopAppBar.kt index 12ed888d..4ee6babd 100644 --- a/ui-component/src/main/java/com/android/ai/uicomponent/SampleDetailTopAppBar.kt +++ b/ui-component/src/main/kotlin/com/android/ai/uicomponent/SampleDetailTopAppBar.kt @@ -96,7 +96,7 @@ fun SampleDetailTopAppBar( ) } -@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) +@OptIn(ExperimentalMaterial3Api::class) @Preview(backgroundColor = 0XFF000000, showBackground = true) @Composable fun SampleDetailTopAppBarPreview() { @@ -110,7 +110,7 @@ fun SampleDetailTopAppBarPreview() { } } -@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) +@OptIn(ExperimentalMaterial3Api::class) @Preview @Composable fun SampleDetailTopAppBarPreview_CollapseWhenContentIsScrolled() { @@ -143,7 +143,7 @@ fun SampleDetailTopAppBarPreview_CollapseWhenContentIsScrolled() { } } -@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) +@OptIn(ExperimentalMaterial3Api::class) @Preview @Composable fun SampleDetailTopAppBarPreview_CollapseWhenToolbarIsScrolled() { diff --git a/ui-component/src/main/java/com/android/ai/uicomponent/SeeCodeButton.kt b/ui-component/src/main/kotlin/com/android/ai/uicomponent/SeeCodeButton.kt similarity index 100% rename from ui-component/src/main/java/com/android/ai/uicomponent/SeeCodeButton.kt rename to ui-component/src/main/kotlin/com/android/ai/uicomponent/SeeCodeButton.kt diff --git a/ui-component/src/main/java/com/android/ai/uicomponent/SelectionDropdown.kt b/ui-component/src/main/kotlin/com/android/ai/uicomponent/SelectionDropdown.kt similarity index 100% rename from ui-component/src/main/java/com/android/ai/uicomponent/SelectionDropdown.kt rename to ui-component/src/main/kotlin/com/android/ai/uicomponent/SelectionDropdown.kt diff --git a/ui-component/src/main/java/com/android/ai/uicomponent/Tag.kt b/ui-component/src/main/kotlin/com/android/ai/uicomponent/Tag.kt similarity index 100% rename from ui-component/src/main/java/com/android/ai/uicomponent/Tag.kt rename to ui-component/src/main/kotlin/com/android/ai/uicomponent/Tag.kt diff --git a/ui-component/src/main/java/com/android/ai/uicomponent/TextInput.kt b/ui-component/src/main/kotlin/com/android/ai/uicomponent/TextInput.kt similarity index 100% rename from ui-component/src/main/java/com/android/ai/uicomponent/TextInput.kt rename to ui-component/src/main/kotlin/com/android/ai/uicomponent/TextInput.kt diff --git a/ui-component/src/main/java/com/android/ai/uicomponent/VideoPlayer.kt b/ui-component/src/main/kotlin/com/android/ai/uicomponent/VideoPlayer.kt similarity index 99% rename from ui-component/src/main/java/com/android/ai/uicomponent/VideoPlayer.kt rename to ui-component/src/main/kotlin/com/android/ai/uicomponent/VideoPlayer.kt index b41b002a..7149fe02 100644 --- a/ui-component/src/main/java/com/android/ai/uicomponent/VideoPlayer.kt +++ b/ui-component/src/main/kotlin/com/android/ai/uicomponent/VideoPlayer.kt @@ -220,7 +220,7 @@ fun VideoPickerDropdown( val iconBg = MaterialTheme.colorScheme.surfaceContainerHighest Icon( imageVector = Icons.Filled.ArrowDropDown, - contentDescription = context.getString(R.string.select_video_dropdown), + contentDescription = stringResource(R.string.select_video_dropdown), tint = MaterialTheme.colorScheme.onSurface, modifier = Modifier.drawBehind { drawCircle(iconBg)