├── .github
└── workflows
│ └── build-gemini-nano.yml
├── .gitignore
├── .google
└── packaging.yaml
├── README.md
├── ai-catalog
├── .gitignore
├── README.md
├── app
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── google-services.json
│ ├── proguard-rules.pro
│ └── src
│ │ └── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ └── com
│ │ │ └── android
│ │ │ └── ai
│ │ │ └── catalog
│ │ │ ├── CatalogApp.kt
│ │ │ ├── MainActivity.kt
│ │ │ └── ui
│ │ │ ├── CatalogScreen.kt
│ │ │ ├── domain
│ │ │ └── SampleCatalog.kt
│ │ │ └── theme
│ │ │ ├── Color.kt
│ │ │ ├── Theme.kt
│ │ │ └── Type.kt
│ │ └── res
│ │ ├── drawable
│ │ ├── ic_launcher_background.xml
│ │ └── ic_launcher_foreground.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── values
│ │ ├── strings.xml
│ │ └── themes.xml
│ │ └── xml
│ │ ├── backup_rules.xml
│ │ ├── data_extraction_rules.xml
│ │ └── provider_paths.xml
├── build.gradle.kts
├── gradle.properties
├── gradle
│ ├── libs.versions.toml
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── samples
│ ├── gemini-chatbot
│ │ ├── .gitignore
│ │ ├── build.gradle.kts
│ │ ├── consumer-rules.pro
│ │ ├── proguard-rules.pro
│ │ └── src
│ │ │ └── main
│ │ │ ├── java
│ │ │ └── com
│ │ │ │ └── android
│ │ │ │ └── ai
│ │ │ │ └── samples
│ │ │ │ └── geminichatbot
│ │ │ │ ├── ChatMessage.kt
│ │ │ │ ├── GeminiChatbotScreen.kt
│ │ │ │ ├── GeminiChatbotViewModel.kt
│ │ │ │ └── InputBar.kt
│ │ │ └── res
│ │ │ └── values
│ │ │ └── strings.xml
│ ├── gemini-multimodal
│ │ ├── .gitignore
│ │ ├── build.gradle.kts
│ │ ├── consumer-rules.pro
│ │ ├── proguard-rules.pro
│ │ └── src
│ │ │ └── main
│ │ │ ├── java
│ │ │ └── com
│ │ │ │ └── android
│ │ │ │ └── ai
│ │ │ │ └── samples
│ │ │ │ └── geminimultimodal
│ │ │ │ ├── GeminiMultimodalScreen.kt
│ │ │ │ └── GeminiMultimodalViewModel.kt
│ │ │ └── res
│ │ │ └── values
│ │ │ └── strings.xml
│ ├── gemini-video-summarization
│ │ ├── .gitignore
│ │ ├── build.gradle.kts
│ │ ├── proguard-rules.pro
│ │ └── src
│ │ │ └── main
│ │ │ ├── java
│ │ │ └── com
│ │ │ │ └── android
│ │ │ │ └── ai
│ │ │ │ └── samples
│ │ │ │ └── geminivideosummary
│ │ │ │ ├── VideoSummarizationScreen.kt
│ │ │ │ ├── player
│ │ │ │ ├── VideoPlayer.kt
│ │ │ │ └── VideoSelectionDropdown.kt
│ │ │ │ ├── ui
│ │ │ │ ├── OutputTextDisplay.kt
│ │ │ │ └── TextToSpeechControls.kt
│ │ │ │ ├── util
│ │ │ │ └── VideoList.kt
│ │ │ │ └── viewmodel
│ │ │ │ ├── OutputTextState.kt
│ │ │ │ └── VideoSummarizationViewModel.kt
│ │ │ └── res
│ │ │ └── values
│ │ │ └── strings.xml
│ ├── genai-image-description
│ │ ├── .gitignore
│ │ ├── build.gradle.kts
│ │ ├── consumer-rules.pro
│ │ ├── proguard-rules.pro
│ │ └── src
│ │ │ └── main
│ │ │ ├── java
│ │ │ └── com
│ │ │ │ └── android
│ │ │ │ └── ai
│ │ │ │ └── samples
│ │ │ │ └── genai_image_description
│ │ │ │ ├── GenAIImageDescriptionScreen.kt
│ │ │ │ └── GenAIImageDescriptionViewModel.kt
│ │ │ └── res
│ │ │ └── values
│ │ │ └── strings.xml
│ ├── genai-summarization
│ │ ├── .gitignore
│ │ ├── build.gradle.kts
│ │ ├── consumer-rules.pro
│ │ ├── proguard-rules.pro
│ │ └── src
│ │ │ └── main
│ │ │ ├── java
│ │ │ └── com
│ │ │ │ └── android
│ │ │ │ └── ai
│ │ │ │ └── samples
│ │ │ │ └── genai_summarization
│ │ │ │ ├── GenAISummarizationScreen.kt
│ │ │ │ └── GenAISummarizationViewModel.kt
│ │ │ └── res
│ │ │ └── values
│ │ │ └── strings.xml
│ ├── genai-writing-assistance
│ │ ├── .gitignore
│ │ ├── build.gradle.kts
│ │ ├── consumer-rules.pro
│ │ ├── proguard-rules.pro
│ │ └── src
│ │ │ └── main
│ │ │ ├── java
│ │ │ └── com
│ │ │ │ └── android
│ │ │ │ └── ai
│ │ │ │ └── samples
│ │ │ │ └── genai_writing_assistance
│ │ │ │ ├── GenAIWritingAssistanceScreen.kt
│ │ │ │ └── GenAIWritingAssistanceViewModel.kt
│ │ │ └── res
│ │ │ └── values
│ │ │ └── strings.xml
│ ├── imagen
│ │ ├── .gitignore
│ │ ├── build.gradle.kts
│ │ ├── consumer-rules.pro
│ │ ├── proguard-rules.pro
│ │ └── src
│ │ │ └── main
│ │ │ ├── java
│ │ │ └── com
│ │ │ │ └── android
│ │ │ │ └── ai
│ │ │ │ └── samples
│ │ │ │ └── imagen
│ │ │ │ ├── ImagenScreen.kt
│ │ │ │ └── ImagenViewModel.kt
│ │ │ └── res
│ │ │ └── values
│ │ │ └── strings.xml
│ └── magic-selfie
│ │ ├── .gitignore
│ │ ├── build.gradle.kts
│ │ └── src
│ │ └── main
│ │ ├── java
│ │ └── com
│ │ │ └── android
│ │ │ └── ai
│ │ │ └── samples
│ │ │ └── magicselfie
│ │ │ ├── MagicSelfieScreen.kt
│ │ │ └── MagicSelfieViewModel.kt
│ │ └── res
│ │ └── values
│ │ └── strings.xml
└── settings.gradle.kts
└── gemini-nano
├── .gitignore
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── google
│ │ └── ai
│ │ └── edge
│ │ └── aicore
│ │ └── demo
│ │ ├── ContentAdapter.kt
│ │ ├── EntryChoiceActivity.kt
│ │ ├── GenerationConfigDialog.kt
│ │ ├── GenerationConfigUtils.kt
│ │ ├── java
│ │ └── MainActivity.java
│ │ └── kotlin
│ │ └── MainActivity.kt
│ └── res
│ ├── drawable
│ ├── list_item_background.xml
│ ├── request_item_background.xml
│ └── response_item_background.xml
│ ├── layout
│ ├── activity_entry_choice.xml
│ ├── activity_main.xml
│ ├── dialog_generation_config.xml
│ ├── row_item_request.xml
│ └── row_item_response.xml
│ └── values
│ ├── colors.xml
│ ├── dimens.xml
│ ├── strings.xml
│ └── themes.xml
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.github/workflows/build-gemini-nano.yml:
--------------------------------------------------------------------------------
1 | name: Build Gemini Nano Android App
2 |
3 | on:
4 | workflow_dispatch:
5 | push:
6 | branches: [ "main" ]
7 | paths:
8 | - 'gemini-nano/**'
9 | pull_request:
10 | branches: [ "main" ]
11 | paths:
12 | - 'gemini-nano/**'
13 |
14 | jobs:
15 | build:
16 | runs-on: ubuntu-latest
17 |
18 | steps:
19 | - name: Checkout code
20 | uses: actions/checkout@v4
21 |
22 | - name: Set up JDK 17
23 | uses: actions/setup-java@v4
24 | with:
25 | java-version: '17'
26 | distribution: 'zulu'
27 | cache: 'gradle'
28 |
29 | - name: Setup Gradle
30 | uses: gradle/actions/setup-gradle@v4
31 |
32 | - name: Make gradlew executable
33 | working-directory: ./gemini-nano
34 | run: chmod +x ./gradlew
35 |
36 | - name: Build Gemini Nano app
37 | working-directory: ./gemini-nano
38 | run: ./gradlew :app:assembleDebug
39 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | local.properties
2 | .DS_Store
3 | build
4 | /.idea
5 |
--------------------------------------------------------------------------------
/.google/packaging.yaml:
--------------------------------------------------------------------------------
1 | # Copyright 2024 The Android Open Source Project
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | #
15 | # This file is used by Google as part of our samples packaging process.
16 | # End users may safely ignore this file. It has no relevance to other systems.
17 | ---
18 | status: PUBLISHED
19 | technologies: [Android]
20 | categories: [Artificial Intelligence]
21 | languages: [Java, Kotlin]
22 | solutions: [Mobile]
23 | github: android/ai-samples
24 | license: apache2
25 |
26 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Google AI Samples
2 |
3 | This repository provides a collection of samples showcasing Google AI technologies.
4 |
5 | ## Repository Structure
6 |
7 | The repository is organized into the following folders:
8 |
9 | * [**gemini-nano**](gemini-nano/): This folder contains samples focused on experimenting with
10 | Gemini Nano. You'll find code and resources here to help you explore and utilize Gemini Nano in
11 | your projects.
12 | * [**ai-catalog**](ai-catalog): This folder contains the Android AI Sample app. It demonstrates
13 | various AI capabilities within an Android application.
14 |
15 | ## Getting Started
16 |
17 | To get started with the samples, navigate to the specific folder you're interested in and follow
18 | the instructions within that folder's README.md.
19 |
20 | ## License
21 |
22 | Copyright 2024 Google, Inc.
23 |
24 | Licensed to the Apache Software Foundation (ASF) under one or more contributor
25 | license agreements. See the NOTICE file distributed with this work for
26 | additional information regarding copyright ownership. The ASF licenses this file
27 | to you under the Apache License, Version 2.0 (the "License"); you may not use
28 | this file except in compliance with the License. You may obtain a copy of the
29 | License at
30 |
31 | http://www.apache.org/licenses/LICENSE-2.0
32 |
33 | Unless required by applicable law or agreed to in writing, software distributed
34 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
35 | CONDITIONS OF ANY KIND, either express or implied. See the License for the
36 | specific language governing permissions and limitations under the License.
37 |
--------------------------------------------------------------------------------
/ai-catalog/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /.gradle
3 | /.idea
4 | /.kotlin
5 | app/google-services.json
6 |
--------------------------------------------------------------------------------
/ai-catalog/README.md:
--------------------------------------------------------------------------------
1 | # Android AI Sample Catalog
2 |
3 | 
4 |
5 | This folder contains the Android AI Sample catalog, a stand alone application giving you access to
6 | individual self-contained samples illustrating some of the Generative AI capabilities unlocked by
7 | some of Google's models.
8 |
9 | > **Note:** These samples are intended to showcase specific AI capabilities in isolation, and they may use
10 | > simplified code. They are demo not intended to be used as production-ready code.
11 | > For best practices follow our documentation and check
12 | > [Now In Android](https://github.com/android/nowinandroid)
13 |
14 | Browse the samples inside the `/samples` folder:
15 |
16 | - **gemini-chatbot**: a simple chatbot using Gemini 2.0 Flash
17 | - **gemini-multimodal**: a single screen application leveraging text+image to text generation with Gemini 2.0 Flash
18 | - **genai-summarization**: a text summarization sample using Gemini Nano
19 | - **genai-image-description**: an image description sample using Gemini Nano
20 | - **genai-writing-assistance**: a proofreading and rewriting sample using Gemini Nano
21 | - **imagen**: an image generation sample using Imagen
22 | - **magic-selfie**: an sample using ML Kit subject segmentation and Imagen for image generation
23 | - **gemini-video-summarization**: a video summarization sample using Gemini 2.0 Flash
24 | - More to come...
25 |
26 | > **Requires Firebase setup** the samples relying on Google Cloud models (Gemini Pro, Gemini Flash, etc...)
27 | > require setting up a Firebase project and connecting the app to Firebase (read more [here](https://firebase.google.com/docs/ai-logic/get-started?platform=android&api=dev#set-up-firebase)).
28 |
29 | > 🚧 **Work-in-Progress:** we are working on bringing more samples into the application.
30 |
31 | ## How to run
32 |
33 | 1. Clone the repository
34 | 2. Open the whole project in Android Studio.
35 | 3. Set up a Firebase project and connect your app to Firebase by adding your Firebase configuration
36 | file (google-services.json) to the app. Read more [here](https://firebase.google.com/docs/ai-logic/get-started?platform=android&api=dev#set-up-firebase).
37 | 3. Sync & Run `app` configuration
38 |
39 | The app will open with the samples list screen that allows you to navigate throughout the different
40 | available samples.
41 |
42 | ## Reporting issues
43 |
44 | You can report [issues with the samples](https://github.com/android/ai-samples/issues) using
45 | this repository. When doing so, make sure to specify which sample you are referring to.
46 |
47 | ## Contributions
48 |
49 | We aren't open to contribution to this project at the moment.
50 |
51 | ## License
52 |
53 | ```
54 | Copyright 2023 The Android Open Source Project
55 |
56 | Licensed under the Apache License, Version 2.0 (the "License");
57 | you may not use this file except in compliance with the License.
58 | You may obtain a copy of the License at
59 |
60 | https://www.apache.org/licenses/LICENSE-2.0
61 |
62 | Unless required by applicable law or agreed to in writing, software
63 | distributed under the License is distributed on an "AS IS" BASIS,
64 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
65 | See the License for the specific language governing permissions and
66 | limitations under the License.
67 | ```
68 |
--------------------------------------------------------------------------------
/ai-catalog/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | google-services.json
--------------------------------------------------------------------------------
/ai-catalog/app/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 | plugins {
18 | alias(libs.plugins.android.application)
19 | alias(libs.plugins.jetbrains.kotlin.android)
20 | alias(libs.plugins.jetbrains.kotlin.serialization)
21 | alias(libs.plugins.google.gms.google.services)
22 | alias(libs.plugins.hilt.plugin)
23 | alias(libs.plugins.ksp)
24 | alias(libs.plugins.compose.compiler)
25 | }
26 |
27 | android {
28 | namespace = "com.android.ai.catalog"
29 | compileSdk = 35
30 |
31 | defaultConfig {
32 | applicationId = "com.android.ai.catalog"
33 | minSdk = 26
34 | targetSdk = 35
35 | versionCode = 1
36 | versionName = "1.0"
37 |
38 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
39 | vectorDrawables {
40 | useSupportLibrary = true
41 | }
42 | }
43 |
44 | buildTypes {
45 | release {
46 | isMinifyEnabled = false
47 | proguardFiles(
48 | getDefaultProguardFile("proguard-android-optimize.txt"),
49 | "proguard-rules.pro"
50 | )
51 | }
52 | }
53 | compileOptions {
54 | sourceCompatibility = JavaVersion.VERSION_17
55 | targetCompatibility = JavaVersion.VERSION_17
56 | }
57 | kotlinOptions {
58 | jvmTarget = "17"
59 | }
60 | buildFeatures {
61 | compose = true
62 | }
63 | }
64 |
65 | dependencies {
66 |
67 | implementation(libs.androidx.core.ktx)
68 | implementation(libs.androidx.lifecycle.runtime.ktx)
69 | implementation(libs.androidx.activity.compose)
70 | implementation(platform(libs.androidx.compose.bom))
71 | implementation(libs.androidx.ui)
72 | implementation(libs.androidx.ui.graphics)
73 | implementation(libs.androidx.ui.tooling.preview)
74 | implementation(libs.androidx.material3)
75 | implementation(libs.androidx.navigation.compose)
76 | implementation(libs.kotlinx.serialization.json)
77 | implementation(libs.androidx.navigation.runtime.ktx)
78 | implementation(libs.hilt.android)
79 | implementation(libs.hilt.navigation.compose)
80 | implementation(platform(libs.firebase.bom))
81 | implementation(libs.firebase.ai)
82 | ksp(libs.hilt.compiler)
83 |
84 | implementation(project(":samples:gemini-multimodal"))
85 | implementation(project(":samples:gemini-chatbot"))
86 | implementation(project(":samples:genai-summarization"))
87 | implementation(project(":samples:genai-image-description"))
88 | implementation(project(":samples:genai-writing-assistance"))
89 | implementation(project(":samples:imagen"))
90 | implementation(project(":samples:magic-selfie"))
91 | implementation(project(":samples:gemini-video-summarization"))
92 |
93 | testImplementation(libs.junit)
94 | androidTestImplementation(libs.androidx.junit)
95 | androidTestImplementation(libs.androidx.espresso.core)
96 | androidTestImplementation(platform(libs.androidx.compose.bom))
97 | androidTestImplementation(libs.androidx.ui.test.junit4)
98 | debugImplementation(libs.androidx.ui.tooling)
99 | debugImplementation(libs.androidx.ui.test.manifest)
100 | }
--------------------------------------------------------------------------------
/ai-catalog/app/google-services.json:
--------------------------------------------------------------------------------
1 | {
2 | "comment": "NOTE: Replace this file with your own google-services.json. Learn more at: https://firebase.google.com/docs/vertex-ai/get-started#no-existing-firebase",
3 | "project_info": {
4 | "project_number": "123456",
5 | "project_id": "mock_project",
6 | "storage_bucket": "mock_project.firebasestorage.app"
7 | },
8 | "client": [
9 | {
10 | "client_info": {
11 | "mobilesdk_app_id": "1:123456:android:123456",
12 | "android_client_info": {
13 | "package_name": "com.android.ai.catalog"
14 | }
15 | },
16 | "oauth_client": [],
17 | "api_key": [
18 | {
19 | "current_key": "123456-abcde"
20 | }
21 | ],
22 | "services": {
23 | "appinvite_service": {
24 | "other_platform_oauth_client": []
25 | }
26 | }
27 | }
28 | ],
29 | "configuration_version": "1"
30 | }
--------------------------------------------------------------------------------
/ai-catalog/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/ai-catalog/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
17 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
32 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/ai-catalog/app/src/main/java/com/android/ai/catalog/CatalogApp.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.android.ai.catalog
17 |
18 | import android.app.Application
19 | import dagger.hilt.android.HiltAndroidApp
20 |
21 | @HiltAndroidApp
22 | class CatalogApp: Application()
--------------------------------------------------------------------------------
/ai-catalog/app/src/main/java/com/android/ai/catalog/MainActivity.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.android.ai.catalog
19 |
20 | import android.os.Bundle
21 | import androidx.activity.ComponentActivity
22 | import androidx.activity.compose.setContent
23 | import androidx.activity.enableEdgeToEdge
24 | import androidx.compose.foundation.layout.fillMaxSize
25 | import androidx.compose.ui.Modifier
26 | import com.android.ai.catalog.ui.CatalogScreen
27 | import com.android.ai.catalog.ui.theme.AISampleCatalogTheme
28 | import dagger.hilt.android.AndroidEntryPoint
29 |
30 | @AndroidEntryPoint
31 | class MainActivity : ComponentActivity() {
32 | override fun onCreate(savedInstanceState: Bundle?) {
33 | super.onCreate(savedInstanceState)
34 | enableEdgeToEdge()
35 | setContent {
36 | AISampleCatalogTheme {
37 | CatalogScreen(modifier = Modifier.fillMaxSize())
38 | }
39 | }
40 | }
41 | }
--------------------------------------------------------------------------------
/ai-catalog/app/src/main/java/com/android/ai/catalog/ui/domain/SampleCatalog.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.android.ai.catalog.ui.domain
19 |
20 | import android.content.Context
21 | import androidx.compose.runtime.Composable
22 | import androidx.compose.ui.graphics.Color
23 | import com.android.ai.catalog.R
24 | import com.android.ai.samples.geminichatbot.GeminiChatbotScreen
25 | import com.android.ai.samples.geminimultimodal.GeminiMultimodalScreen
26 | import com.android.ai.samples.geminivideosummary.VideoSummarizationScreen
27 | import com.android.ai.samples.genai_image_description.GenAIImageDescriptionScreen
28 | import com.android.ai.samples.genai_summarization.GenAISummarizationScreen
29 | import com.android.ai.samples.genai_writing_assistance.GenAIWritingAssistanceScreen
30 | import com.android.ai.samples.imagen.ImagenScreen
31 | import com.android.ai.samples.magicselfie.MagicSelfieScreen
32 |
33 | class SampleCatalog(
34 | context: Context
35 | ) {
36 |
37 | val list = listOf(
38 | SampleCatalogItem(
39 | title = context.getString(R.string.gemini_multimodal_sample_title),
40 | description = context.getString(R.string.gemini_multimodal_sample_description),
41 | route = "GeminiMultimodalScreen",
42 | sampleEntryScreen = { GeminiMultimodalScreen() },
43 | tags = listOf(SampleTags.GEMINI_2_0_FLASH, SampleTags.FIREBASE),
44 | needsFirebase = true
45 | ),
46 | SampleCatalogItem(
47 | title = context.getString(R.string.gemini_chatbot_sample_title),
48 | description = context.getString(R.string.gemini_chatbot_sample_description),
49 | route = "GeminiChitchatScreen",
50 | sampleEntryScreen = { GeminiChatbotScreen() },
51 | tags = listOf(SampleTags.GEMINI_2_0_FLASH, SampleTags.FIREBASE),
52 | needsFirebase = true
53 | ),
54 | SampleCatalogItem(
55 | title = context.getString(R.string.genai_summarization_sample_title),
56 | description = context.getString(R.string.genai_summarization_sample_description),
57 | route = "GenAISummarizationScreen",
58 | sampleEntryScreen = { GenAISummarizationScreen() },
59 | tags = listOf(SampleTags.GEMINI_NANO, SampleTags.ML_KIT)
60 | ),
61 | SampleCatalogItem(
62 | title = context.getString(R.string.genai_image_description_sample_title),
63 | description = context.getString(R.string.genai_image_description_sample_description),
64 | route = "GenAIImageDescriptionScreen",
65 | sampleEntryScreen = { GenAIImageDescriptionScreen() },
66 | tags = listOf(SampleTags.GEMINI_NANO, SampleTags.ML_KIT)
67 | ),
68 | SampleCatalogItem(
69 | title = context.getString(R.string.genai_writing_assistance_sample_title),
70 | description = context.getString(R.string.genai_writing_assistance_sample_description),
71 | route = "GenAIWritingAssistanceScreen",
72 | sampleEntryScreen = { GenAIWritingAssistanceScreen() },
73 | tags = listOf(SampleTags.GEMINI_NANO, SampleTags.ML_KIT)
74 | ),
75 | SampleCatalogItem(
76 | title = context.getString(R.string.imagen_sample_title),
77 | description = context.getString(R.string.imagen_sample_description),
78 | route = "ImagenImageGenerationScreen",
79 | sampleEntryScreen = { ImagenScreen() },
80 | tags = listOf(SampleTags.IMAGEN, SampleTags.FIREBASE),
81 | needsFirebase = true
82 | ),
83 | SampleCatalogItem(
84 | title = context.getString(R.string.magic_selfie_sample_title),
85 | description = context.getString(R.string.magic_selfie_sample_description),
86 | route = "MagicSelfieScreen",
87 | sampleEntryScreen = { MagicSelfieScreen() },
88 | tags = listOf(SampleTags.IMAGEN, SampleTags.FIREBASE, SampleTags.ML_KIT),
89 | needsFirebase = true
90 | ),
91 | SampleCatalogItem(
92 | title = context.getString(R.string.gemini_video_summarization_sample_title),
93 | description = context.getString(R.string.gemini_video_summarization_sample_description),
94 | route = "VideoSummarizationScreen",
95 | sampleEntryScreen = { VideoSummarizationScreen() },
96 | tags = listOf(SampleTags.GEMINI_2_0_FLASH, SampleTags.FIREBASE, SampleTags.MEDIA3),
97 | needsFirebase = true
98 | ),
99 |
100 | // To create a new sample entry, add a new SampleCatalogItem here.
101 | )
102 |
103 | }
104 |
105 | data class SampleCatalogItem(
106 | val title: String,
107 | val description: String,
108 | val route: String,
109 | val sampleEntryScreen: @Composable () -> Unit,
110 | val tags: List = emptyList(),
111 | val needsFirebase: Boolean = false
112 | )
113 |
114 | enum class SampleTags(
115 | val label: String,
116 | val backgroundColor: Color,
117 | val textColor: Color
118 | ) {
119 | FIREBASE("Firebase", Color(0xFFFF9100), Color.White),
120 | GEMINI_2_0_FLASH("Gemini 2.0 Flash", Color(0xFF4285F4), Color.White),
121 | GEMINI_NANO("Gemini Nano", Color(0xFF7abafe), Color.White),
122 | IMAGEN("Imagen", Color(0xFF7CB342), Color.White),
123 | MEDIA3("Media3", Color(0xFF7CB584), Color.White),
124 | ML_KIT("ML Kit", Color.White, Color(0xFF4285F4))
125 | }
126 |
127 |
--------------------------------------------------------------------------------
/ai-catalog/app/src/main/java/com/android/ai/catalog/ui/theme/Color.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.android.ai.catalog.ui.theme
19 |
20 | import androidx.compose.ui.graphics.Color
21 |
22 | val Purple80 = Color(0xFFD0BCFF)
23 | val PurpleGrey80 = Color(0xFFCCC2DC)
24 | val Pink80 = Color(0xFFEFB8C8)
25 |
26 | val Purple40 = Color(0xFF6650a4)
27 | val PurpleGrey40 = Color(0xFF625b71)
28 | val Pink40 = Color(0xFF7D5260)
--------------------------------------------------------------------------------
/ai-catalog/app/src/main/java/com/android/ai/catalog/ui/theme/Theme.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.android.ai.catalog.ui.theme
19 |
20 | import android.os.Build
21 | import androidx.compose.foundation.isSystemInDarkTheme
22 | import androidx.compose.material3.MaterialTheme
23 | import androidx.compose.material3.darkColorScheme
24 | import androidx.compose.material3.dynamicDarkColorScheme
25 | import androidx.compose.material3.dynamicLightColorScheme
26 | import androidx.compose.material3.lightColorScheme
27 | import androidx.compose.runtime.Composable
28 | import androidx.compose.ui.platform.LocalContext
29 |
30 | private val DarkColorScheme = darkColorScheme(
31 | primary = Purple80,
32 | secondary = PurpleGrey80,
33 | tertiary = Pink80
34 | )
35 |
36 | private val LightColorScheme = lightColorScheme(
37 | primary = Purple40,
38 | secondary = PurpleGrey40,
39 | tertiary = Pink40
40 |
41 | /* Other default colors to override
42 | background = Color(0xFFFFFBFE),
43 | surface = Color(0xFFFFFBFE),
44 | onPrimary = Color.White,
45 | onSecondary = Color.White,
46 | onTertiary = Color.White,
47 | onBackground = Color(0xFF1C1B1F),
48 | onSurface = Color(0xFF1C1B1F),
49 | */
50 | )
51 |
52 | @Composable
53 | fun AISampleCatalogTheme(
54 | darkTheme: Boolean = isSystemInDarkTheme(),
55 | // Dynamic color is available on Android 12+
56 | dynamicColor: Boolean = true,
57 | content: @Composable () -> Unit
58 | ) {
59 | val colorScheme = when {
60 | dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
61 | val context = LocalContext.current
62 | if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
63 | }
64 |
65 | darkTheme -> DarkColorScheme
66 | else -> LightColorScheme
67 | }
68 |
69 | MaterialTheme(
70 | colorScheme = colorScheme,
71 | typography = Typography,
72 | content = content
73 | )
74 | }
--------------------------------------------------------------------------------
/ai-catalog/app/src/main/java/com/android/ai/catalog/ui/theme/Type.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.android.ai.catalog.ui.theme
19 |
20 | import androidx.compose.material3.Typography
21 | import androidx.compose.ui.text.TextStyle
22 | import androidx.compose.ui.text.font.FontFamily
23 | import androidx.compose.ui.text.font.FontWeight
24 | import androidx.compose.ui.unit.sp
25 |
26 | // Set of Material typography styles to start with
27 | val Typography = Typography(
28 | bodyLarge = TextStyle(
29 | fontFamily = FontFamily.Default,
30 | fontWeight = FontWeight.Normal,
31 | fontSize = 16.sp,
32 | lineHeight = 24.sp,
33 | letterSpacing = 0.5.sp
34 | )
35 | /* Other default text styles to override
36 | titleLarge = TextStyle(
37 | fontFamily = FontFamily.Default,
38 | fontWeight = FontWeight.Normal,
39 | fontSize = 22.sp,
40 | lineHeight = 28.sp,
41 | letterSpacing = 0.sp
42 | ),
43 | labelSmall = TextStyle(
44 | fontFamily = FontFamily.Default,
45 | fontWeight = FontWeight.Medium,
46 | fontSize = 11.sp,
47 | lineHeight = 16.sp,
48 | letterSpacing = 0.5.sp
49 | )
50 | */
51 | )
--------------------------------------------------------------------------------
/ai-catalog/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/ai-catalog/app/src/main/res/drawable/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/ai-catalog/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/ai-catalog/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/ai-catalog/app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/ai-samples/2e0c14cccd01b118e7fe41c55551dd310a2d0375/ai-catalog/app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/ai-catalog/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/ai-samples/2e0c14cccd01b118e7fe41c55551dd310a2d0375/ai-catalog/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/ai-catalog/app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/ai-samples/2e0c14cccd01b118e7fe41c55551dd310a2d0375/ai-catalog/app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/ai-catalog/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/ai-samples/2e0c14cccd01b118e7fe41c55551dd310a2d0375/ai-catalog/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/ai-catalog/app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/ai-samples/2e0c14cccd01b118e7fe41c55551dd310a2d0375/ai-catalog/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/ai-catalog/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/ai-samples/2e0c14cccd01b118e7fe41c55551dd310a2d0375/ai-catalog/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/ai-catalog/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/ai-samples/2e0c14cccd01b118e7fe41c55551dd310a2d0375/ai-catalog/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/ai-catalog/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/ai-samples/2e0c14cccd01b118e7fe41c55551dd310a2d0375/ai-catalog/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/ai-catalog/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/ai-samples/2e0c14cccd01b118e7fe41c55551dd310a2d0375/ai-catalog/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/ai-catalog/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/ai-samples/2e0c14cccd01b118e7fe41c55551dd310a2d0375/ai-catalog/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/ai-catalog/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | AI Sample Catalog
3 | Gemini Multimodal Generation
4 | A very simple example of multimodal generation using the Gemini 2.0 Flash model.
5 | Gemini Chatbot
6 | A simple implementation of chatbot using Gemini 2.0 Flash model.
7 | Summarization with Gemini Nano
8 | Summarize articles and conversations on-device with GenAI API powered by Gemini Nano
9 | Image Description with Gemini Nano
10 | Generate short descriptions of images on-device with GenAI API powered by Gemini Nano
11 | Polish text with Gemini Nano
12 | Proofread and rewrite short content on-device with GenAI API powered by Gemini Nano"
13 | Android AI Samples
14 | Open sample
15 | Image generation with Imagen
16 | Generate images with Imagen, Google image generation model
17 | Magic Selfie with Imagen and ML Kit
18 | Change the background of you selfies with Imagen and the ML Kit Segmentation API
19 | Video Summarization with Gemini and Firebase
20 | "Generate a summary of a video (from a cloud URL or Youtube) with Gemini API powered by Firebase"
21 | Firebase Required
22 | This feature requires Firebase to be initialized.
23 | Close
24 | Show me how
25 |
--------------------------------------------------------------------------------
/ai-catalog/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/ai-catalog/app/src/main/res/xml/backup_rules.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
13 |
--------------------------------------------------------------------------------
/ai-catalog/app/src/main/res/xml/data_extraction_rules.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
12 |
13 |
19 |
--------------------------------------------------------------------------------
/ai-catalog/app/src/main/res/xml/provider_paths.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/ai-catalog/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
19 | plugins {
20 | alias(libs.plugins.android.application) apply false
21 | alias(libs.plugins.jetbrains.kotlin.android) apply false
22 | alias(libs.plugins.android.library) apply false
23 | alias(libs.plugins.google.gms.google.services) apply false
24 | alias(libs.plugins.hilt.plugin) apply false
25 | alias(libs.plugins.ksp) apply false
26 | alias(libs.plugins.compose.compiler) apply false
27 | }
--------------------------------------------------------------------------------
/ai-catalog/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | org.gradle.caching=true
11 | org.gradle.parallel=true
12 | org.gradle.unsafe.configuration-cache=true
13 | # When configured, Gradle will run in incubating parallel mode.
14 | # This option should only be used with decoupled projects. For more details, visit
15 | # https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
16 | # org.gradle.parallel=true
17 | # AndroidX package structure to make it clearer which packages are bundled with the
18 | # Android operating system, and which are packaged with your app's APK
19 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
20 | android.useAndroidX=true
21 | # Kotlin code style for this project: "official" or "obsolete":
22 | kotlin.code.style=official
23 | # Enables namespacing of each library's R class so that its R class includes only the
24 | # resources declared in the library itself and none from the library's dependencies,
25 | # thereby reducing the size of the R class for that library
26 | android.nonTransitiveRClass=true
--------------------------------------------------------------------------------
/ai-catalog/gradle/libs.versions.toml:
--------------------------------------------------------------------------------
1 | [versions]
2 | agp = "8.8.0"
3 | coilCompose = "3.1.0"
4 | firebaseBom = "33.14.0"
5 | mlkitGenAi = "1.0.0-beta1"
6 | kotlin = "2.1.0"
7 | coreKtx = "1.15.0"
8 | junit = "4.13.2"
9 | junitVersion = "1.2.1"
10 | espressoCore = "3.6.1"
11 | kotlinxCoroutinesGuava = "1.10.2"
12 | kotlinxSerializationJson = "1.6.2"
13 | lifecycleRuntimeKtx = "2.8.7"
14 | activityCompose = "1.10.0"
15 | composeBom = "2025.01.00"
16 | navigationCompose = "2.8.5"
17 | navigationRuntimeKtx = "2.8.5"
18 | appcompat = "1.7.0"
19 | googleGmsGoogleServices = "4.4.2"
20 | hilt = "2.51.1"
21 | hiltNavigationCompose = "1.2.0"
22 | ksp = "2.1.0-1.0.29"
23 | runtimeLivedata = "1.7.6"
24 | material3Android = "1.3.1"
25 | media3 = "1.6.1"
26 | firebaseCommonKtx = "21.0.0"
27 | uiToolingPreviewAndroid = "1.8.1"
28 |
29 | [libraries]
30 | androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
31 | coil-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coilCompose" }
32 | firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebaseBom" }
33 | firebase-ai = { group = "com.google.firebase", name = "firebase-ai" }
34 | firebase-common-ktx = { group = "com.google.firebase", name = "firebase-common-ktx", version.ref = "firebaseCommonKtx" }
35 | genai-image-description = { module = "com.google.mlkit:genai-image-description", version.ref = "mlkitGenAi" }
36 | genai-proofreading = { module = "com.google.mlkit:genai-proofreading", version.ref = "mlkitGenAi" }
37 | genai-rewrite = { module = "com.google.mlkit:genai-rewriting", version.ref = "mlkitGenAi" }
38 | genai-summarization = { module = "com.google.mlkit:genai-summarization", version.ref = "mlkitGenAi" }
39 | junit = { group = "junit", name = "junit", version.ref = "junit" }
40 | androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
41 | androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
42 | androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
43 | androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
44 | androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
45 | androidx-ui = { group = "androidx.compose.ui", name = "ui" }
46 | androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
47 | androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
48 | androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
49 | androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
50 | androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
51 | androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
52 | androidx-material-icons-extended = { module = "androidx.compose.material:material-icons-extended" }
53 | androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" }
54 | androidx-navigation-runtime-ktx = { group = "androidx.navigation", name = "navigation-runtime-ktx", version.ref = "navigationRuntimeKtx" }
55 | kotlinx-coroutines-guava = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-guava", version.ref = "kotlinxCoroutinesGuava" }
56 | kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
57 | androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
58 | hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt"}
59 | hilt-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hilt"}
60 | hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "hiltNavigationCompose" }
61 | androidx-runtime-livedata = { group = "androidx.compose.runtime", name = "runtime-livedata", version.ref = "runtimeLivedata" }
62 | androidx-material3-android = { group = "androidx.compose.material3", name = "material3-android", version.ref = "material3Android" }
63 | androidx-media3-exoplayer = { module = "androidx.media3:media3-exoplayer", version.ref = "media3" }
64 | androidx-media3-ui = { module = "androidx.media3:media3-ui", version.ref = "media3" }
65 | androidx-ui-tooling-preview-android = { group = "androidx.compose.ui", name = "ui-tooling-preview-android", version.ref = "uiToolingPreviewAndroid" }
66 |
67 | [plugins]
68 | android-application = { id = "com.android.application", version.ref = "agp" }
69 | jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
70 | jetbrains-kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
71 | android-library = { id = "com.android.library", version.ref = "agp" }
72 | google-gms-google-services = { id = "com.google.gms.google-services", version.ref = "googleGmsGoogleServices" }
73 | hilt-plugin = { id = "com.google.dagger.hilt.android", version.ref = "hilt"}
74 | ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
75 | compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
--------------------------------------------------------------------------------
/ai-catalog/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/ai-samples/2e0c14cccd01b118e7fe41c55551dd310a2d0375/ai-catalog/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/ai-catalog/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/ai-catalog/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 | @rem SPDX-License-Identifier: Apache-2.0
17 | @rem
18 |
19 | @if "%DEBUG%"=="" @echo off
20 | @rem ##########################################################################
21 | @rem
22 | @rem Gradle startup script for Windows
23 | @rem
24 | @rem ##########################################################################
25 |
26 | @rem Set local scope for the variables with windows NT shell
27 | if "%OS%"=="Windows_NT" setlocal
28 |
29 | set DIRNAME=%~dp0
30 | if "%DIRNAME%"=="" set DIRNAME=.
31 | @rem This is normally unused
32 | set APP_BASE_NAME=%~n0
33 | set APP_HOME=%DIRNAME%
34 |
35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
37 |
38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
40 |
41 | @rem Find java.exe
42 | if defined JAVA_HOME goto findJavaFromJavaHome
43 |
44 | set JAVA_EXE=java.exe
45 | %JAVA_EXE% -version >NUL 2>&1
46 | if %ERRORLEVEL% equ 0 goto execute
47 |
48 | echo. 1>&2
49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
50 | echo. 1>&2
51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
52 | echo location of your Java installation. 1>&2
53 |
54 | goto fail
55 |
56 | :findJavaFromJavaHome
57 | set JAVA_HOME=%JAVA_HOME:"=%
58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
59 |
60 | if exist "%JAVA_EXE%" goto execute
61 |
62 | echo. 1>&2
63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
64 | echo. 1>&2
65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
66 | echo location of your Java installation. 1>&2
67 |
68 | goto fail
69 |
70 | :execute
71 | @rem Setup the command line
72 |
73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
74 |
75 |
76 | @rem Execute Gradle
77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
78 |
79 | :end
80 | @rem End local scope for the variables with windows NT shell
81 | if %ERRORLEVEL% equ 0 goto mainEnd
82 |
83 | :fail
84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
85 | rem the _cmd.exe /c_ return code!
86 | set EXIT_CODE=%ERRORLEVEL%
87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
89 | exit /b %EXIT_CODE%
90 |
91 | :mainEnd
92 | if "%OS%"=="Windows_NT" endlocal
93 |
94 | :omega
95 |
--------------------------------------------------------------------------------
/ai-catalog/samples/gemini-chatbot/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/ai-catalog/samples/gemini-chatbot/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | plugins {
19 | alias(libs.plugins.android.library)
20 | alias(libs.plugins.jetbrains.kotlin.android)
21 | alias(libs.plugins.ksp)
22 | alias(libs.plugins.compose.compiler)
23 | }
24 |
25 | android {
26 | namespace = "com.android.ai.samples.geminichatbot"
27 | compileSdk = 35
28 |
29 | buildFeatures {
30 | compose = true
31 | }
32 |
33 | defaultConfig {
34 | minSdk = 24
35 |
36 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
37 | consumerProguardFiles("consumer-rules.pro")
38 | }
39 |
40 | buildTypes {
41 | release {
42 | isMinifyEnabled = false
43 | proguardFiles(
44 | getDefaultProguardFile("proguard-android-optimize.txt"),
45 | "proguard-rules.pro"
46 | )
47 | }
48 | }
49 | compileOptions {
50 | sourceCompatibility = JavaVersion.VERSION_17
51 | targetCompatibility = JavaVersion.VERSION_17
52 | }
53 | kotlinOptions {
54 | jvmTarget = "17"
55 | }
56 | }
57 |
58 | dependencies {
59 |
60 | implementation(libs.androidx.core.ktx)
61 | implementation(libs.androidx.appcompat)
62 | implementation(platform(libs.androidx.compose.bom))
63 | implementation(libs.androidx.material.icons.extended)
64 | implementation(platform(libs.firebase.bom))
65 | implementation(libs.firebase.ai)
66 | implementation(libs.hilt.android)
67 | implementation(libs.hilt.navigation.compose)
68 | implementation(libs.androidx.runtime.livedata)
69 | implementation(libs.androidx.material3.android)
70 | ksp(libs.hilt.compiler)
71 |
72 | testImplementation(libs.junit)
73 | androidTestImplementation(libs.androidx.junit)
74 | androidTestImplementation(libs.androidx.espresso.core)
75 | }
--------------------------------------------------------------------------------
/ai-catalog/samples/gemini-chatbot/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/ai-samples/2e0c14cccd01b118e7fe41c55551dd310a2d0375/ai-catalog/samples/gemini-chatbot/consumer-rules.pro
--------------------------------------------------------------------------------
/ai-catalog/samples/gemini-chatbot/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/ai-catalog/samples/gemini-chatbot/src/main/java/com/android/ai/samples/geminichatbot/ChatMessage.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.android.ai.samples.geminichatbot
19 |
20 | import android.net.Uri
21 |
22 | data class ChatMessage(
23 | val text: String,
24 | val timestamp: Long,
25 | val isIncoming: Boolean,
26 | val senderIconUrl: Uri?
27 | )
--------------------------------------------------------------------------------
/ai-catalog/samples/gemini-chatbot/src/main/java/com/android/ai/samples/geminichatbot/GeminiChatbotViewModel.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.android.ai.samples.geminichatbot
19 |
20 | import androidx.lifecycle.ViewModel
21 | import androidx.lifecycle.viewModelScope
22 | import com.google.firebase.Firebase
23 | import com.google.firebase.ai.ai
24 | import com.google.firebase.ai.type.GenerativeBackend
25 | import com.google.firebase.ai.type.HarmBlockThreshold
26 | import com.google.firebase.ai.type.HarmCategory
27 | import com.google.firebase.ai.type.SafetySetting
28 | import com.google.firebase.ai.type.content
29 | import com.google.firebase.ai.type.generationConfig
30 | import kotlinx.coroutines.flow.MutableStateFlow
31 | import kotlinx.coroutines.flow.StateFlow
32 | import kotlinx.coroutines.launch
33 | import javax.inject.Inject
34 |
35 | class GeminiChatbotViewModel @Inject constructor(): ViewModel() {
36 |
37 | private val _messageList = MutableStateFlow(mutableListOf())
38 | val messageList: StateFlow> = _messageList
39 |
40 | private val generativeModel by lazy {
41 | Firebase.ai(backend = GenerativeBackend.googleAI()).generativeModel(
42 | "gemini-2.0-flash",
43 | generationConfig = generationConfig {
44 | temperature = 0.9f
45 | topK = 32
46 | topP = 1f
47 | maxOutputTokens = 4096
48 | },
49 | safetySettings = listOf(
50 | SafetySetting(HarmCategory.HARASSMENT, HarmBlockThreshold.MEDIUM_AND_ABOVE),
51 | SafetySetting(HarmCategory.HATE_SPEECH, HarmBlockThreshold.MEDIUM_AND_ABOVE),
52 | SafetySetting(HarmCategory.SEXUALLY_EXPLICIT, HarmBlockThreshold.MEDIUM_AND_ABOVE),
53 | SafetySetting(HarmCategory.DANGEROUS_CONTENT, HarmBlockThreshold.MEDIUM_AND_ABOVE)
54 | ),
55 | systemInstruction = content {
56 | text("""You are a friendly assistant. Keep your response short.""")
57 | }
58 | )
59 | }
60 |
61 | private val chat = generativeModel.startChat()
62 |
63 | fun sendMessage(message: String) {
64 | viewModelScope.launch {
65 | _messageList.value.add(
66 | ChatMessage(
67 | message,
68 | System.currentTimeMillis(),
69 | false,
70 | null
71 | )
72 | )
73 |
74 | val response = chat.sendMessage(message)
75 |
76 | response.text?.let {
77 | _messageList.value = _messageList.value.toMutableList().apply {
78 | add(
79 | ChatMessage(
80 | it.trim(),
81 | System.currentTimeMillis(),
82 | true,
83 | null
84 | )
85 | )
86 | }
87 | }
88 | }
89 | }
90 | }
--------------------------------------------------------------------------------
/ai-catalog/samples/gemini-chatbot/src/main/java/com/android/ai/samples/geminichatbot/InputBar.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.android.ai.samples.geminichatbot
19 |
20 | import androidx.compose.foundation.layout.Arrangement
21 | import androidx.compose.foundation.layout.PaddingValues
22 | import androidx.compose.foundation.layout.Row
23 | import androidx.compose.foundation.layout.Spacer
24 | import androidx.compose.foundation.layout.height
25 | import androidx.compose.foundation.layout.padding
26 | import androidx.compose.foundation.layout.size
27 | import androidx.compose.foundation.layout.width
28 | import androidx.compose.foundation.text.KeyboardActions
29 | import androidx.compose.foundation.text.KeyboardOptions
30 | import androidx.compose.material.icons.Icons
31 | import androidx.compose.material.icons.automirrored.filled.Send
32 | import androidx.compose.material3.FilledIconButton
33 | import androidx.compose.material3.Icon
34 | import androidx.compose.material3.MaterialTheme
35 | import androidx.compose.material3.Surface
36 | import androidx.compose.material3.Text
37 | import androidx.compose.material3.TextField
38 | import androidx.compose.material3.TextFieldDefaults
39 | import androidx.compose.runtime.Composable
40 | import androidx.compose.ui.Alignment
41 | import androidx.compose.ui.Modifier
42 | import androidx.compose.ui.graphics.Color
43 | import androidx.compose.ui.text.input.ImeAction
44 | import androidx.compose.ui.text.input.KeyboardCapitalization
45 | import androidx.compose.ui.unit.dp
46 |
47 | @Composable
48 | internal fun InputBar(
49 | modifier: Modifier = Modifier,
50 | value: String,
51 | placeholder: String,
52 | contentPadding: PaddingValues,
53 | sendEnabled: Boolean,
54 | onInputChanged: (String) -> Unit,
55 | onSendClick: () -> Unit,
56 | ) {
57 | Surface(
58 | modifier = modifier,
59 | tonalElevation = 3.dp,
60 | ) {
61 | Row(
62 | modifier = Modifier
63 | .padding(contentPadding)
64 | .padding(16.dp),
65 | verticalAlignment = Alignment.CenterVertically,
66 | horizontalArrangement = Arrangement.spacedBy(4.dp),
67 | ) {
68 | TextField(
69 | value = value,
70 | onValueChange = onInputChanged,
71 | modifier = Modifier
72 | .weight(1f)
73 | .height(56.dp),
74 | keyboardOptions = KeyboardOptions(
75 | capitalization = KeyboardCapitalization.Sentences,
76 | imeAction = ImeAction.Send,
77 | ),
78 | keyboardActions = KeyboardActions(onSend = { onSendClick() }),
79 | placeholder = { Text(placeholder) },
80 | shape = MaterialTheme.shapes.extraLarge,
81 | colors = TextFieldDefaults.colors(
82 | focusedContainerColor = MaterialTheme.colorScheme.background,
83 | unfocusedContainerColor = MaterialTheme.colorScheme.background,
84 | focusedIndicatorColor = Color.Transparent,
85 | unfocusedIndicatorColor = Color.Transparent,
86 | disabledIndicatorColor = Color.Transparent,
87 | ),
88 | )
89 | Spacer(modifier = Modifier.width(4.dp))
90 | FilledIconButton(
91 | onClick = onSendClick,
92 | modifier = Modifier.size(56.dp),
93 | enabled = sendEnabled,
94 | ) {
95 | Icon(
96 | imageVector = Icons.AutoMirrored.Filled.Send,
97 | contentDescription = null,
98 | )
99 | }
100 | }
101 | }
102 | }
--------------------------------------------------------------------------------
/ai-catalog/samples/gemini-chatbot/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Gemini chatbot
4 | Type your message
5 | See code
6 |
--------------------------------------------------------------------------------
/ai-catalog/samples/gemini-multimodal/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/ai-catalog/samples/gemini-multimodal/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | plugins {
19 | alias(libs.plugins.android.library)
20 | alias(libs.plugins.jetbrains.kotlin.android)
21 | alias(libs.plugins.ksp)
22 | alias(libs.plugins.compose.compiler)
23 | }
24 |
25 | android {
26 | namespace = "com.android.ai.samples.geminimultimodal"
27 | compileSdk = 35
28 |
29 | buildFeatures {
30 | compose = true
31 | }
32 |
33 | defaultConfig {
34 | minSdk = 24
35 |
36 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
37 | consumerProguardFiles("consumer-rules.pro")
38 | }
39 |
40 | buildTypes {
41 | release {
42 | isMinifyEnabled = false
43 | proguardFiles(
44 | getDefaultProguardFile("proguard-android-optimize.txt"),
45 | "proguard-rules.pro"
46 | )
47 | }
48 | }
49 | compileOptions {
50 | sourceCompatibility = JavaVersion.VERSION_17
51 | targetCompatibility = JavaVersion.VERSION_17
52 | }
53 | composeOptions {
54 | kotlinCompilerExtensionVersion = "1.5.15"
55 | }
56 | kotlinOptions {
57 | jvmTarget = "17"
58 | }
59 | }
60 |
61 | dependencies {
62 |
63 | implementation(libs.androidx.core.ktx)
64 | implementation(libs.androidx.appcompat)
65 | implementation(libs.androidx.material3)
66 | implementation(libs.androidx.activity.compose)
67 | implementation(platform(libs.androidx.compose.bom))
68 | implementation(libs.androidx.material.icons.extended)
69 | implementation(libs.androidx.material.icons.extended)
70 | implementation(platform(libs.firebase.bom))
71 | implementation(libs.firebase.ai)
72 | implementation(libs.hilt.android)
73 | implementation(libs.hilt.navigation.compose)
74 | implementation(libs.androidx.runtime.livedata)
75 | ksp(libs.hilt.compiler)
76 |
77 | testImplementation(libs.junit)
78 | androidTestImplementation(libs.androidx.junit)
79 | androidTestImplementation(libs.androidx.espresso.core)
80 | }
--------------------------------------------------------------------------------
/ai-catalog/samples/gemini-multimodal/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/ai-samples/2e0c14cccd01b118e7fe41c55551dd310a2d0375/ai-catalog/samples/gemini-multimodal/consumer-rules.pro
--------------------------------------------------------------------------------
/ai-catalog/samples/gemini-multimodal/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/ai-catalog/samples/gemini-multimodal/src/main/java/com/android/ai/samples/geminimultimodal/GeminiMultimodalViewModel.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.android.ai.samples.geminimultimodal
17 |
18 | import android.graphics.Bitmap
19 | import androidx.lifecycle.LiveData
20 | import androidx.lifecycle.MutableLiveData
21 | import androidx.lifecycle.ViewModel
22 | import androidx.lifecycle.viewModelScope
23 | import com.google.firebase.Firebase
24 | import com.google.firebase.ai.ai
25 | import com.google.firebase.ai.type.GenerativeBackend
26 | import com.google.firebase.ai.type.HarmBlockThreshold
27 | import com.google.firebase.ai.type.HarmCategory
28 | import com.google.firebase.ai.type.SafetySetting
29 | import com.google.firebase.ai.type.content
30 | import com.google.firebase.ai.type.generationConfig
31 | import kotlinx.coroutines.flow.MutableStateFlow
32 | import kotlinx.coroutines.flow.StateFlow
33 | import kotlinx.coroutines.launch
34 | import javax.inject.Inject
35 |
36 | class GeminiMultimodalViewModel @Inject constructor(): ViewModel() {
37 |
38 | private val _textGenerated = MutableStateFlow("")
39 | val textGenerated: StateFlow = _textGenerated
40 |
41 | private val _isGenerating = MutableLiveData(false)
42 | val isGenerating: LiveData = _isGenerating
43 |
44 | private val generativeModel by lazy {
45 | Firebase.ai(backend = GenerativeBackend.googleAI()).generativeModel(
46 | "gemini-2.0-flash",
47 | generationConfig = generationConfig {
48 | temperature = 0.9f
49 | topK = 32
50 | topP = 1f
51 | maxOutputTokens = 4096
52 | },
53 | safetySettings = listOf(
54 | SafetySetting(HarmCategory.HARASSMENT, HarmBlockThreshold.MEDIUM_AND_ABOVE),
55 | SafetySetting(HarmCategory.HATE_SPEECH, HarmBlockThreshold.MEDIUM_AND_ABOVE),
56 | SafetySetting(HarmCategory.SEXUALLY_EXPLICIT, HarmBlockThreshold.MEDIUM_AND_ABOVE),
57 | SafetySetting(HarmCategory.DANGEROUS_CONTENT, HarmBlockThreshold.MEDIUM_AND_ABOVE))
58 | )
59 | }
60 |
61 | fun generate(
62 | bitmap: Bitmap,
63 | prompt: String
64 | ) {
65 | _isGenerating.value = true
66 |
67 | val multimodalPrompt = content {
68 | image(bitmap)
69 | text(prompt)
70 | }
71 | viewModelScope.launch {
72 | val result = generativeModel.generateContent(multimodalPrompt)
73 |
74 | result.text?.let {
75 | _textGenerated.value = result.text!!
76 | }
77 | _isGenerating.postValue(false)
78 | }
79 | }
80 | }
--------------------------------------------------------------------------------
/ai-catalog/samples/gemini-multimodal/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Gemini Multimodal
4 | Describe this picture in a funny way with a lot of emojis
5 | Generating…
6 | See code
7 |
--------------------------------------------------------------------------------
/ai-catalog/samples/gemini-video-summarization/.gitignore:
--------------------------------------------------------------------------------
1 | /build:
2 | `
--------------------------------------------------------------------------------
/ai-catalog/samples/gemini-video-summarization/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.library)
3 | alias(libs.plugins.jetbrains.kotlin.android)
4 | alias(libs.plugins.ksp)
5 | alias(libs.plugins.compose.compiler)
6 | alias(libs.plugins.hilt.plugin)
7 | }
8 |
9 | android {
10 | namespace = "com.google.com.android.ai.samples.geminivideosummary"
11 | compileSdk = 35
12 |
13 | defaultConfig {
14 | minSdk = 24
15 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
16 | }
17 |
18 | buildTypes {
19 | release {
20 | isMinifyEnabled = false
21 | proguardFiles(
22 | getDefaultProguardFile("proguard-android-optimize.txt"),
23 | "proguard-rules.pro"
24 | )
25 | }
26 | }
27 | compileOptions {
28 | sourceCompatibility = JavaVersion.VERSION_17
29 | targetCompatibility = JavaVersion.VERSION_17
30 | }
31 | kotlinOptions {
32 | jvmTarget = "17"
33 | }
34 | buildFeatures {
35 | compose = true
36 | }
37 | composeOptions {
38 | kotlinCompilerExtensionVersion = "1.5.15"
39 | }
40 | }
41 |
42 | dependencies {
43 |
44 | implementation(libs.androidx.core.ktx)
45 | implementation(libs.androidx.appcompat)
46 | implementation(libs.androidx.activity.compose)
47 | implementation(libs.androidx.material.icons.extended)
48 | implementation(platform(libs.androidx.compose.bom))
49 | implementation(libs.hilt.android)
50 | implementation(libs.hilt.navigation.compose)
51 | implementation(libs.androidx.material3.android)
52 | implementation(libs.firebase.common.ktx)
53 | implementation(libs.androidx.ui.tooling.preview.android)
54 | ksp(libs.hilt.compiler)
55 | implementation(platform(libs.firebase.bom))
56 | implementation(libs.firebase.ai)
57 |
58 | // Media3 ExoPlayer
59 | implementation(libs.androidx.media3.exoplayer)
60 | implementation(libs.androidx.media3.ui)
61 | }
--------------------------------------------------------------------------------
/ai-catalog/samples/gemini-video-summarization/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/ai-catalog/samples/gemini-video-summarization/src/main/java/com/android/ai/samples/geminivideosummary/player/VideoPlayer.kt:
--------------------------------------------------------------------------------
1 | package com.android.ai.samples.geminivideosummary.player
2 |
3 | import androidx.compose.foundation.layout.height
4 | import androidx.compose.runtime.Composable
5 | import androidx.compose.ui.Modifier
6 | import androidx.compose.ui.platform.LocalContext
7 | import androidx.compose.ui.unit.dp
8 | import androidx.compose.ui.viewinterop.AndroidView
9 | import androidx.media3.exoplayer.ExoPlayer
10 | import androidx.media3.ui.PlayerView
11 |
12 | /*
13 | * Copyright 2024 Google LLC
14 | *
15 | * Licensed under the Apache License, Version 2.0 (the "License");
16 | * you may not use this file except in compliance with the License.
17 | * You may obtain a copy of the License at
18 | *
19 | * https://www.apache.org/licenses/LICENSE-2.0
20 | *
21 | * Unless required by applicable law or agreed to in writing, software
22 | * distributed under the License is distributed on an "AS IS" BASIS,
23 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24 | * See the License for the specific language governing permissions and
25 | * limitations under the License.
26 | */
27 |
28 | /*
29 | * A Composable function that displays video using ExoPlayer within a PlayerView in Jetpack Compose.
30 | */
31 | @Composable
32 | fun VideoPlayer(exoPlayer: ExoPlayer, modifier: Modifier = Modifier) {
33 | val context = LocalContext.current
34 | AndroidView(
35 | factory = {
36 | PlayerView(context).apply {
37 | player = exoPlayer
38 | }
39 | }, modifier = modifier.height(200.dp)
40 | )
41 | }
--------------------------------------------------------------------------------
/ai-catalog/samples/gemini-video-summarization/src/main/java/com/android/ai/samples/geminivideosummary/player/VideoSelectionDropdown.kt:
--------------------------------------------------------------------------------
1 | package com.android.ai.samples.geminivideosummary.player
2 |
3 | import android.net.Uri
4 | import androidx.compose.foundation.clickable
5 | import androidx.compose.foundation.layout.Box
6 | import androidx.compose.foundation.layout.fillMaxWidth
7 | import androidx.compose.material.icons.Icons
8 | import androidx.compose.material.icons.filled.ArrowDropDown
9 | import androidx.compose.material3.DropdownMenu
10 | import androidx.compose.material3.DropdownMenuItem
11 | import androidx.compose.material3.Icon
12 | import androidx.compose.material3.OutlinedTextField
13 | import androidx.compose.material3.Text
14 | import androidx.compose.runtime.Composable
15 | import androidx.compose.ui.Modifier
16 |
17 | /**
18 | * A composable function that displays a dropdown menu for selecting a video from a list of options.
19 | */
20 | @Composable
21 | fun VideoSelectionDropdown(
22 | selectedVideoUri: Uri?,
23 | isDropdownExpanded: Boolean,
24 | videoOptions: List>,
25 | onVideoUriSelected: (Uri) -> Unit,
26 | onNewVideoUrlChanged: (String) -> Unit,
27 | onDropdownExpanded: (Boolean) -> Unit
28 | ) {
29 | Box {
30 | OutlinedTextField(value = selectedVideoUri?.let {
31 | videoOptions.firstOrNull { it.second == selectedVideoUri }?.first
32 | } ?: "Select Video",
33 | onValueChange = { },
34 | readOnly = true,
35 | trailingIcon = {
36 | Icon(imageVector = Icons.Filled.ArrowDropDown,
37 | contentDescription = "Dropdown",
38 | modifier = Modifier.clickable { onDropdownExpanded(!isDropdownExpanded) })
39 | },
40 | modifier = Modifier.fillMaxWidth()
41 | .clickable { onDropdownExpanded(!isDropdownExpanded) })
42 |
43 | DropdownMenu(
44 | expanded = isDropdownExpanded,
45 | onDismissRequest = { onDropdownExpanded(false) },
46 | modifier = Modifier.fillMaxWidth()
47 | ) {
48 | videoOptions.forEach { (label, uri) ->
49 | DropdownMenuItem(text = { Text(label) }, onClick = {
50 | onVideoUriSelected(uri)
51 | onDropdownExpanded(false)
52 | onNewVideoUrlChanged("")
53 | })
54 | }
55 | }
56 | }
57 | }
--------------------------------------------------------------------------------
/ai-catalog/samples/gemini-video-summarization/src/main/java/com/android/ai/samples/geminivideosummary/ui/OutputTextDisplay.kt:
--------------------------------------------------------------------------------
1 | package com.android.ai.samples.geminivideosummary.ui
2 |
3 | import androidx.compose.foundation.layout.fillMaxWidth
4 | import androidx.compose.foundation.rememberScrollState
5 | import androidx.compose.foundation.verticalScroll
6 | import androidx.compose.material3.MaterialTheme
7 | import androidx.compose.material3.Text
8 | import androidx.compose.runtime.Composable
9 | import androidx.compose.ui.Modifier
10 | import androidx.compose.ui.res.stringResource
11 | import androidx.compose.ui.text.font.FontStyle
12 | import com.android.ai.samples.geminivideosummary.viewmodel.OutputTextState
13 | import com.google.com.android.ai.samples.geminivideosummary.R
14 |
15 | /**
16 | * Composable function that displays text based on the state of the `outputText`.
17 | *
18 | * This function renders different text content depending on the `OutputTextState` provided.
19 | * It handles four states:
20 | * - `Initial`: Displays a placeholder message indicating that text is being generated.
21 | * - `Loading`: Shows a loading message to indicate that text generation is in progress.
22 | * - `Success`: Renders the generated text, providing a styled display within a scrollable container.
23 | * - `Error`: Displays an error message along with the specific error encountered during text generation.
24 | */
25 | @Composable
26 | fun OutputTextDisplay(outputText: OutputTextState, modifier: Modifier = Modifier) {
27 |
28 | when (outputText) {
29 | is OutputTextState.Initial -> {
30 | Text(
31 | text = stringResource(
32 | R.string.output_text_combined,
33 | stringResource(R.string.output_text_generated_placeholder),
34 | stringResource(R.string.output_text_initial)
35 | ), fontStyle = FontStyle.Italic, modifier = Modifier.fillMaxWidth()
36 | )
37 | }
38 |
39 | is OutputTextState.Loading -> {
40 | Text(
41 | text = stringResource(
42 | R.string.output_text_combined,
43 | stringResource(R.string.output_text_generated_placeholder),
44 | stringResource(R.string.output_text_loading)
45 | ), fontStyle = FontStyle.Italic, modifier = Modifier.fillMaxWidth()
46 | )
47 | }
48 |
49 | is OutputTextState.Success -> {
50 | Text(
51 | text = stringResource(
52 | R.string.output_text_combined,
53 | stringResource(R.string.output_text_generated_placeholder),
54 | outputText.text
55 | ),
56 | fontStyle = FontStyle.Italic,
57 | modifier = Modifier
58 | .fillMaxWidth()
59 | .verticalScroll(rememberScrollState()),
60 | style = MaterialTheme.typography.labelLarge
61 | )
62 | }
63 |
64 | is OutputTextState.Error -> {
65 | Text(
66 | text = stringResource(
67 | R.string.output_text_combined,
68 | stringResource(R.string.output_text_generated_placeholder),
69 | outputText.errorMessage
70 | ), fontStyle = FontStyle.Italic, modifier = Modifier.fillMaxWidth()
71 | )
72 | }
73 | }
74 | }
--------------------------------------------------------------------------------
/ai-catalog/samples/gemini-video-summarization/src/main/java/com/android/ai/samples/geminivideosummary/ui/TextToSpeechControls.kt:
--------------------------------------------------------------------------------
1 | package com.android.ai.samples.geminivideosummary.ui
2 |
3 | import android.speech.tts.TextToSpeech
4 | import android.util.Log
5 | import androidx.compose.foundation.clickable
6 | import androidx.compose.foundation.layout.Arrangement
7 | import androidx.compose.foundation.layout.Row
8 | import androidx.compose.foundation.layout.fillMaxWidth
9 | import androidx.compose.foundation.layout.padding
10 | import androidx.compose.material.icons.Icons
11 | import androidx.compose.material.icons.filled.ArrowDropDown
12 | import androidx.compose.material3.Button
13 | import androidx.compose.material3.DropdownMenu
14 | import androidx.compose.material3.DropdownMenuItem
15 | import androidx.compose.material3.Icon
16 | import androidx.compose.material3.OutlinedTextField
17 | import androidx.compose.material3.Text
18 | import androidx.compose.runtime.Composable
19 | import androidx.compose.ui.Alignment
20 | import androidx.compose.ui.Modifier
21 | import androidx.compose.ui.res.stringResource
22 | import androidx.compose.ui.unit.dp
23 | import com.google.com.android.ai.samples.geminivideosummary.R
24 | import java.util.Locale
25 |
26 |
27 | /**
28 | * Composable function that provides controls for Text-to-Speech functionality.
29 | *
30 | * This function displays a UI that allows the user to:
31 | * - Select a language accent for the Text-to-Speech engine.
32 | * - Initiate speech synthesis for the provided text.
33 | * - Pause the ongoing speech.
34 | */
35 | @Composable
36 | fun TextToSpeechControls(
37 | isInitialized: Boolean,
38 | isSpeaking: Boolean,
39 | isPaused: Boolean,
40 | textToSpeech: TextToSpeech?,
41 | speechText: String,
42 | selectedAccent: Locale,
43 | accentOptions: List,
44 | onSpeakingStateChange: (speaking: Boolean, paused: Boolean) -> Unit,
45 | isAccentDropdownExpanded: Boolean,
46 | onAccentSelected: (Locale) -> Unit,
47 | onAccentDropdownExpanded: (Boolean) -> Unit,
48 | ) {
49 |
50 | Row(
51 | modifier = Modifier.fillMaxWidth(),
52 | horizontalArrangement = Arrangement.SpaceBetween,
53 | verticalAlignment = Alignment.CenterVertically
54 | ) {
55 | OutlinedTextField(value = selectedAccent.displayLanguage,
56 | onValueChange = { },
57 | readOnly = true,
58 | trailingIcon = {
59 | Icon(imageVector = Icons.Filled.ArrowDropDown,
60 | contentDescription = "Dropdown",
61 | modifier = Modifier.clickable { onAccentDropdownExpanded(!isAccentDropdownExpanded) })
62 | },
63 | modifier = Modifier
64 | .clickable { onAccentDropdownExpanded(!isAccentDropdownExpanded) }
65 | .padding(end = 8.dp)
66 | .weight(1f))
67 | DropdownMenu(
68 | expanded = isAccentDropdownExpanded,
69 | onDismissRequest = { onAccentDropdownExpanded(false) },
70 | ) {
71 | accentOptions.forEach { accent ->
72 | DropdownMenuItem(text = { Text(accent.displayLanguage) }, onClick = {
73 | onAccentSelected(accent)
74 | onAccentDropdownExpanded(false)
75 | })
76 | }
77 | }
78 |
79 | if (isInitialized && !isSpeaking) {
80 | Button(onClick = {
81 | handleSpeakButtonClick(
82 | textToSpeech, speechText, selectedAccent, onSpeakingStateChange
83 | )
84 | }) {
85 | Text(text = stringResource(R.string.text_listen_to_ai_output))
86 | }
87 | }
88 |
89 | if (isSpeaking && !isPaused) {
90 | Button(onClick = {
91 | textToSpeech?.stop()
92 | onSpeakingStateChange(false, true)
93 | }) {
94 | Text(text = stringResource(R.string.pause))
95 | }
96 | }
97 | }
98 | }
99 |
100 | private fun handleSpeakButtonClick(
101 | textToSpeech: TextToSpeech?,
102 | textForSpeech: String,
103 | selectedAccent: Locale,
104 | onSpeakingStateChange: (speaking: Boolean, paused: Boolean) -> Unit
105 | ) {
106 | // Check if the voice and language is supported
107 | val result = textToSpeech?.language?.let {
108 | textToSpeech.setLanguage(selectedAccent)
109 | }
110 | if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED) {
111 | Log.e("TextToSpeech", "Language not supported")
112 | } else {
113 | // Start speaking
114 | textToSpeech?.speak(
115 | textForSpeech, TextToSpeech.QUEUE_FLUSH, null, null
116 | )
117 | onSpeakingStateChange(true, false)
118 | }
119 | }
--------------------------------------------------------------------------------
/ai-catalog/samples/gemini-video-summarization/src/main/java/com/android/ai/samples/geminivideosummary/util/VideoList.kt:
--------------------------------------------------------------------------------
1 | package com.android.ai.samples.geminivideosummary.util
2 |
3 | import android.net.Uri
4 |
5 | /**
6 | * Class containing a list of hardcoded video URIs and their titles.
7 | */
8 | class VideoList {
9 | val videos = listOf(
10 | VideoItem(
11 | "Big Buck Bunny",
12 | Uri.parse("https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4")
13 | ), VideoItem(
14 | "Android Spotlight Week (Shorts video)",
15 | Uri.parse("https://storage.googleapis.com/exoplayer-test-media-0/shorts_android_developers/shorts_10.mp4")
16 | ), VideoItem(
17 | "Rio De Janerio",
18 | Uri.parse("gs://cloud-samples-data/generative-ai/video/rio_de_janeiro_beyond_the_map_rio.mp4")
19 | ),
20 | VideoItem(
21 | "Youtube Link (On Device Watch Next with Google TV)",
22 | Uri.parse("https://www.youtube.com/watch?v=QFMIP5GOo70")
23 | ),
24 | VideoItem(
25 | "Tears of Steel",
26 | Uri.parse("https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/TearsOfSteel.mp4")
27 | ), VideoItem(
28 | "For Bigger Blazes",
29 | Uri.parse("https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4")
30 | ), VideoItem(
31 | "For Bigger Escape",
32 | Uri.parse("https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerEscapes.mp4")
33 | )
34 | )
35 | }
36 |
37 | /**
38 | * Data class to represent a video item with a title and URI.
39 | */
40 | data class VideoItem(val title: String, val uri: Uri)
--------------------------------------------------------------------------------
/ai-catalog/samples/gemini-video-summarization/src/main/java/com/android/ai/samples/geminivideosummary/viewmodel/OutputTextState.kt:
--------------------------------------------------------------------------------
1 | package com.android.ai.samples.geminivideosummary.viewmodel
2 |
3 | sealed class OutputTextState {
4 | data object Initial : OutputTextState()
5 | data class Success(val text: String) : OutputTextState()
6 | data class Error(val errorMessage: String) : OutputTextState()
7 | data object Loading : OutputTextState()
8 | }
--------------------------------------------------------------------------------
/ai-catalog/samples/gemini-video-summarization/src/main/java/com/android/ai/samples/geminivideosummary/viewmodel/VideoSummarizationViewModel.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.android.ai.samples.geminivideosummary.viewmodel
19 |
20 | import android.net.Uri
21 | import android.util.Log
22 | import androidx.lifecycle.ViewModel
23 | import androidx.lifecycle.viewModelScope
24 | import com.google.firebase.Firebase
25 | import com.google.firebase.ai.ai
26 | import com.google.firebase.ai.type.GenerativeBackend
27 | import com.google.firebase.ai.type.content
28 | import kotlinx.coroutines.flow.MutableStateFlow
29 | import kotlinx.coroutines.flow.StateFlow
30 | import kotlinx.coroutines.launch
31 | import javax.inject.Inject
32 |
33 | /**
34 | * ViewModel class responsible for handling video summarization using Gemini API.
35 | *
36 | * This ViewModel interacts with the Gemini API to generate a text summary of a provided video.
37 | * It manages the state of the summarization process and exposes the output text through a
38 | * [StateFlow].
39 | */
40 | class VideoSummarizationViewModel @Inject constructor() : ViewModel() {
41 |
42 | private val tag = "VideoSummaryVM"
43 | private val _outputText = MutableStateFlow(OutputTextState.Initial)
44 | val outputText: StateFlow = _outputText
45 |
46 | fun getVideoSummary(videoSource: Uri) {
47 | clearOutputText()
48 | viewModelScope.launch {
49 | val promptData =
50 | "Summarize this video in the form of top 3-4 takeaways only. Write in the form of bullet points. Don't assume if you don't know"
51 | _outputText.value = OutputTextState.Loading
52 |
53 | try {
54 | val generativeModel = Firebase.ai(backend = GenerativeBackend.googleAI()).generativeModel("gemini-2.0-flash")
55 |
56 | val requestContent = content {
57 | fileData(videoSource.toString(), "video/mp4")
58 | text(promptData)
59 | }
60 | val outputStringBuilder = StringBuilder()
61 | generativeModel.generateContentStream(requestContent).collect { response ->
62 | outputStringBuilder.append(response.text)
63 | }
64 | _outputText.value = OutputTextState.Success(outputStringBuilder.toString())
65 |
66 | } catch (error: Exception) {
67 | _outputText.value = error.localizedMessage?.let { OutputTextState.Error(it) }!!
68 | Log.e(tag, "Error processing prompt : $error")
69 | }
70 | }
71 | }
72 |
73 | fun clearOutputText() {
74 | _outputText.value = OutputTextState.Loading
75 | }
76 | }
--------------------------------------------------------------------------------
/ai-catalog/samples/gemini-video-summarization/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Summarize Video
4 | Video summarization
5 | %s%s
6 | Initial
7 | "Text generated with Gemini : "
8 | Loading…
9 | Listen to AI output
10 | Pause
11 | See Code
12 |
--------------------------------------------------------------------------------
/ai-catalog/samples/genai-image-description/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/ai-catalog/samples/genai-image-description/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | plugins {
19 | alias(libs.plugins.android.library)
20 | alias(libs.plugins.jetbrains.kotlin.android)
21 | alias(libs.plugins.ksp)
22 | alias(libs.plugins.compose.compiler)
23 | }
24 |
25 | android {
26 | namespace = "com.android.ai.samples.geminimultimodal"
27 | compileSdk = 35
28 |
29 | buildFeatures {
30 | compose = true
31 | }
32 |
33 | defaultConfig {
34 | minSdk = 26
35 |
36 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
37 | consumerProguardFiles("consumer-rules.pro")
38 | }
39 |
40 | buildTypes {
41 | release {
42 | isMinifyEnabled = false
43 | proguardFiles(
44 | getDefaultProguardFile("proguard-android-optimize.txt"),
45 | "proguard-rules.pro"
46 | )
47 | }
48 | }
49 | compileOptions {
50 | sourceCompatibility = JavaVersion.VERSION_17
51 | targetCompatibility = JavaVersion.VERSION_17
52 | }
53 | composeOptions {
54 | kotlinCompilerExtensionVersion = "1.5.15"
55 | }
56 | kotlinOptions {
57 | jvmTarget = "17"
58 | }
59 | }
60 |
61 | dependencies {
62 |
63 | implementation(libs.androidx.core.ktx)
64 | implementation(libs.androidx.appcompat)
65 | implementation(libs.androidx.material3)
66 | implementation(libs.androidx.activity.compose)
67 | implementation(platform(libs.androidx.compose.bom))
68 | implementation(libs.androidx.material.icons.extended)
69 | implementation(libs.androidx.material.icons.extended)
70 | implementation(libs.hilt.android)
71 | implementation(libs.hilt.navigation.compose)
72 | implementation(libs.androidx.runtime.livedata)
73 | implementation(libs.genai.image.description)
74 | implementation(libs.coil.compose)
75 | implementation(libs.kotlinx.coroutines.guava)
76 | ksp(libs.hilt.compiler)
77 | }
--------------------------------------------------------------------------------
/ai-catalog/samples/genai-image-description/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/ai-samples/2e0c14cccd01b118e7fe41c55551dd310a2d0375/ai-catalog/samples/genai-image-description/consumer-rules.pro
--------------------------------------------------------------------------------
/ai-catalog/samples/genai-image-description/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/ai-catalog/samples/genai-image-description/src/main/java/com/android/ai/samples/genai_image_description/GenAIImageDescriptionScreen.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.android.ai.samples.genai_image_description
19 |
20 | import android.net.Uri
21 | import androidx.activity.compose.rememberLauncherForActivityResult
22 | import androidx.activity.result.PickVisualMediaRequest
23 | import androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia
24 | import androidx.compose.foundation.layout.Box
25 | import androidx.compose.foundation.layout.Column
26 | import androidx.compose.foundation.layout.Spacer
27 | import androidx.compose.foundation.layout.fillMaxSize
28 | import androidx.compose.foundation.layout.fillMaxWidth
29 | import androidx.compose.foundation.layout.padding
30 | import androidx.compose.foundation.layout.size
31 | import androidx.compose.material3.Button
32 | import androidx.compose.material3.Card
33 | import androidx.compose.material3.ExperimentalMaterial3Api
34 | import androidx.compose.material3.MaterialTheme
35 | import androidx.compose.material3.ModalBottomSheet
36 | import androidx.compose.material3.Scaffold
37 | import androidx.compose.material3.Text
38 | import androidx.compose.material3.TopAppBar
39 | import androidx.compose.material3.TopAppBarDefaults.topAppBarColors
40 | import androidx.compose.material3.rememberModalBottomSheetState
41 | import androidx.compose.runtime.Composable
42 | import androidx.compose.runtime.collectAsState
43 | import androidx.compose.runtime.getValue
44 | import androidx.compose.runtime.mutableStateOf
45 | import androidx.compose.runtime.remember
46 | import androidx.compose.runtime.setValue
47 | import androidx.compose.ui.Alignment
48 | import androidx.compose.ui.Modifier
49 | import androidx.compose.ui.layout.ContentScale
50 | import androidx.compose.ui.platform.LocalContext
51 | import androidx.compose.ui.res.stringResource
52 | import androidx.compose.ui.unit.dp
53 | import androidx.compose.ui.unit.sp
54 | import androidx.hilt.navigation.compose.hiltViewModel
55 | import coil3.compose.AsyncImage
56 | import com.android.ai.samples.geminimultimodal.R
57 |
58 | @OptIn(ExperimentalMaterial3Api::class)
59 | @Composable
60 | fun GenAIImageDescriptionScreen(viewModel: GenAIImageDescriptionViewModel = hiltViewModel()) {
61 |
62 | val sheetState = rememberModalBottomSheetState()
63 | var showBottomSheet by remember { mutableStateOf(false) }
64 |
65 | val context = LocalContext.current
66 |
67 | val imageDescriptionResult = viewModel.resultGenerated.collectAsState()
68 |
69 | var imageUri by remember { mutableStateOf(null) }
70 | val photoPickerLauncher = rememberLauncherForActivityResult(PickVisualMedia()) { uri ->
71 | uri?.let {
72 | imageUri = it
73 | }
74 | }
75 |
76 | Scaffold(
77 | modifier = Modifier.fillMaxSize(), topBar = {
78 | TopAppBar(
79 | colors = topAppBarColors(
80 | containerColor = MaterialTheme.colorScheme.primaryContainer,
81 | titleContentColor = MaterialTheme.colorScheme.primary,
82 | ), title = {
83 | Text(text = stringResource(id = R.string.genai_image_description_title_bar))
84 | })
85 | }) { innerPadding ->
86 | Column(
87 | modifier = Modifier.padding(innerPadding)
88 | ) {
89 | // Displayed image
90 | Card(
91 | modifier = Modifier
92 | .size(width = 450.dp, height = 450.dp)
93 | .padding(20.dp)
94 | ) {
95 | AsyncImage(
96 | model = imageUri,
97 | contentDescription = null,
98 | contentScale = ContentScale.Fit,
99 | modifier = Modifier.fillMaxSize()
100 | )
101 | }
102 |
103 | // Select image button
104 | Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {
105 | Button(
106 | onClick = {
107 | photoPickerLauncher.launch(PickVisualMediaRequest(PickVisualMedia.ImageOnly))
108 | },
109 | modifier = Modifier.padding(10.dp)
110 | ) {
111 | Text(
112 | text = stringResource(id = R.string.genai_image_description_add_image),
113 | fontSize = 16.sp
114 | )
115 | }
116 | }
117 |
118 | Spacer(modifier = Modifier.weight(1f))
119 |
120 | // Generate image description button
121 | Box(
122 | modifier = Modifier
123 | .fillMaxWidth()
124 | .padding(20.dp),
125 | contentAlignment = Alignment.Center
126 | ) {
127 | Button(
128 | onClick = {
129 | showBottomSheet = true
130 | viewModel.getImageDescription(imageUri, context)
131 | }, modifier = Modifier.padding(10.dp)
132 | ) {
133 | Text(
134 | text = stringResource(id = R.string.genai_image_description_run_inference),
135 | fontSize = 20.sp
136 | )
137 | }
138 | }
139 | }
140 |
141 | if (showBottomSheet) {
142 | ModalBottomSheet(
143 | onDismissRequest = {
144 | showBottomSheet = false
145 | viewModel.clearGeneratedText()
146 | }, sheetState = sheetState
147 | ) {
148 | Text(
149 | text = imageDescriptionResult.value,
150 | modifier = Modifier.padding(top = 8.dp, bottom = 24.dp, start = 24.dp, end = 24.dp)
151 | )
152 | }
153 | }
154 | }
155 | }
--------------------------------------------------------------------------------
/ai-catalog/samples/genai-image-description/src/main/java/com/android/ai/samples/genai_image_description/GenAIImageDescriptionViewModel.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.android.ai.samples.genai_image_description
19 |
20 | import android.content.Context
21 | import android.net.Uri
22 | import android.provider.MediaStore
23 | import androidx.lifecycle.ViewModel
24 | import androidx.lifecycle.viewModelScope
25 | import com.android.ai.samples.geminimultimodal.R
26 | import com.google.mlkit.genai.common.FeatureStatus
27 | import com.google.mlkit.genai.imagedescription.ImageDescriber
28 | import com.google.mlkit.genai.imagedescription.ImageDescriberOptions
29 | import com.google.mlkit.genai.imagedescription.ImageDescription
30 | import com.google.mlkit.genai.imagedescription.ImageDescriptionRequest
31 | import kotlinx.coroutines.flow.MutableStateFlow
32 | import kotlinx.coroutines.flow.StateFlow
33 | import kotlinx.coroutines.guava.await
34 | import kotlinx.coroutines.launch
35 | import javax.inject.Inject
36 |
37 | class GenAIImageDescriptionViewModel @Inject constructor() : ViewModel() {
38 | private val _resultGenerated = MutableStateFlow("")
39 | val resultGenerated: StateFlow = _resultGenerated
40 |
41 | private var imageDescriber: ImageDescriber? = null
42 |
43 | fun getImageDescription(imageUri: Uri?, context: Context) {
44 | if (imageUri == null) {
45 | _resultGenerated.value =
46 | context.getString(R.string.genai_image_description_no_image_selected)
47 | return
48 | }
49 |
50 | val imageDescriberOptions = ImageDescriberOptions.builder(context).build()
51 | imageDescriber = ImageDescription.getClient(imageDescriberOptions)
52 |
53 | viewModelScope.launch {
54 | imageDescriber?.let { imageDescriber ->
55 | val featureStatus = imageDescriber.checkFeatureStatus().await()
56 |
57 | if (featureStatus == FeatureStatus.UNAVAILABLE) {
58 | _resultGenerated.value =
59 | context.getString(R.string.genai_image_description_not_available)
60 | return@launch
61 | }
62 |
63 | // If feature is downloadable, making an inference call will automatically start
64 | // the downloading process.
65 | // If feature is downloading, the inference request will automatically execute after
66 | // the feature has been downloaded.
67 | // Alternatively, you can call imageDescriber.downloadFeature() to monitor the
68 | // progress of the download.
69 | if (featureStatus == FeatureStatus.DOWNLOADABLE ||
70 | featureStatus == FeatureStatus.DOWNLOADING
71 | ) {
72 | _resultGenerated.value =
73 | context.getString(R.string.genai_image_description_downloading)
74 | }
75 |
76 | val bitmap = MediaStore.Images.Media.getBitmap(context.contentResolver, imageUri)
77 | val request = ImageDescriptionRequest.builder(bitmap).build()
78 | imageDescriber.runInference(request) { newText ->
79 | if (_resultGenerated.value ==
80 | context.getString(R.string.genai_image_description_downloading)) {
81 | clearGeneratedText()
82 | }
83 | _resultGenerated.value += newText
84 | }
85 | return@launch
86 | }
87 | }
88 | }
89 |
90 | fun clearGeneratedText() {
91 | _resultGenerated.value = ""
92 | }
93 |
94 | override fun onCleared() {
95 | imageDescriber?.close()
96 | }
97 | }
--------------------------------------------------------------------------------
/ai-catalog/samples/genai-image-description/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Image Description with Nano
4 | Add image
5 | Generate image description
6 | No image selected
7 | Feature is not available on this device
8 | Downloading feature
9 |
10 |
--------------------------------------------------------------------------------
/ai-catalog/samples/genai-summarization/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/ai-catalog/samples/genai-summarization/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | plugins {
19 | alias(libs.plugins.android.library)
20 | alias(libs.plugins.jetbrains.kotlin.android)
21 | alias(libs.plugins.ksp)
22 | alias(libs.plugins.compose.compiler)
23 | }
24 |
25 | android {
26 | namespace = "com.android.ai.samples.geminimultimodal"
27 | compileSdk = 35
28 |
29 | buildFeatures {
30 | compose = true
31 | }
32 |
33 | defaultConfig {
34 | minSdk = 26
35 |
36 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
37 | consumerProguardFiles("consumer-rules.pro")
38 | }
39 |
40 | buildTypes {
41 | release {
42 | isMinifyEnabled = false
43 | proguardFiles(
44 | getDefaultProguardFile("proguard-android-optimize.txt"),
45 | "proguard-rules.pro"
46 | )
47 | }
48 | }
49 | compileOptions {
50 | sourceCompatibility = JavaVersion.VERSION_17
51 | targetCompatibility = JavaVersion.VERSION_17
52 | }
53 | composeOptions {
54 | kotlinCompilerExtensionVersion = "1.5.15"
55 | }
56 | kotlinOptions {
57 | jvmTarget = "17"
58 | }
59 | }
60 |
61 | dependencies {
62 |
63 | implementation(libs.androidx.core.ktx)
64 | implementation(libs.androidx.appcompat)
65 | implementation(libs.androidx.material3)
66 | implementation(libs.androidx.activity.compose)
67 | implementation(platform(libs.androidx.compose.bom))
68 | implementation(libs.androidx.material.icons.extended)
69 | implementation(libs.androidx.material.icons.extended)
70 | implementation(libs.hilt.android)
71 | implementation(libs.hilt.navigation.compose)
72 | implementation(libs.androidx.runtime.livedata)
73 | implementation(libs.genai.summarization)
74 | implementation(libs.kotlinx.coroutines.guava)
75 | ksp(libs.hilt.compiler)
76 | }
--------------------------------------------------------------------------------
/ai-catalog/samples/genai-summarization/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/ai-samples/2e0c14cccd01b118e7fe41c55551dd310a2d0375/ai-catalog/samples/genai-summarization/consumer-rules.pro
--------------------------------------------------------------------------------
/ai-catalog/samples/genai-summarization/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/ai-catalog/samples/genai-summarization/src/main/java/com/android/ai/samples/genai_summarization/GenAISummarizationScreen.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.android.ai.samples.genai_summarization
19 |
20 | import androidx.compose.foundation.layout.Box
21 | import androidx.compose.foundation.layout.Column
22 | import androidx.compose.foundation.layout.Row
23 | import androidx.compose.foundation.layout.fillMaxSize
24 | import androidx.compose.foundation.layout.fillMaxWidth
25 | import androidx.compose.foundation.layout.padding
26 | import androidx.compose.foundation.rememberScrollState
27 | import androidx.compose.foundation.verticalScroll
28 | import androidx.compose.material3.Button
29 | import androidx.compose.material3.ButtonDefaults
30 | import androidx.compose.material3.ExperimentalMaterial3Api
31 | import androidx.compose.material3.MaterialTheme
32 | import androidx.compose.material3.ModalBottomSheet
33 | import androidx.compose.material3.Scaffold
34 | import androidx.compose.material3.Text
35 | import androidx.compose.material3.TextField
36 | import androidx.compose.material3.TopAppBar
37 | import androidx.compose.material3.TopAppBarDefaults.topAppBarColors
38 | import androidx.compose.material3.rememberModalBottomSheetState
39 | import androidx.compose.runtime.Composable
40 | import androidx.compose.runtime.collectAsState
41 | import androidx.compose.runtime.getValue
42 | import androidx.compose.runtime.mutableStateOf
43 | import androidx.compose.runtime.remember
44 | import androidx.compose.runtime.setValue
45 | import androidx.compose.ui.Alignment
46 | import androidx.compose.ui.Modifier
47 | import androidx.compose.ui.platform.LocalContext
48 | import androidx.compose.ui.res.stringArrayResource
49 | import androidx.compose.ui.res.stringResource
50 | import androidx.compose.ui.unit.dp
51 | import androidx.compose.ui.unit.sp
52 | import androidx.hilt.navigation.compose.hiltViewModel
53 | import com.android.ai.samples.geminimultimodal.R
54 |
55 | @OptIn(ExperimentalMaterial3Api::class)
56 | @Composable
57 | fun GenAISummarizationScreen(viewModel: GenAISummarizationViewModel = hiltViewModel()) {
58 | val sampleTextOptions = stringArrayResource(R.array.summarization_sample_text)
59 |
60 | val sheetState = rememberModalBottomSheetState()
61 | var showBottomSheet by remember { mutableStateOf(false) }
62 | val context = LocalContext.current
63 |
64 | val summarizationResult = viewModel.summarizationGenerated.collectAsState()
65 | var textInput by remember { mutableStateOf("") }
66 |
67 | Scaffold(
68 | modifier = Modifier.fillMaxSize(), topBar = {
69 | TopAppBar(
70 | colors = topAppBarColors(
71 | containerColor = MaterialTheme.colorScheme.primaryContainer,
72 | titleContentColor = MaterialTheme.colorScheme.primary,
73 | ), title = {
74 | Text(text = stringResource(id = R.string.genai_summarization_title_bar))
75 | })
76 | }) { innerPadding ->
77 |
78 | Column(
79 | Modifier
80 | .padding(12.dp)
81 | .padding(innerPadding)
82 | ) {
83 | // Text input box
84 | TextField(
85 | value = textInput,
86 | onValueChange = { textInput = it },
87 | label = { Text(stringResource(id = R.string.genai_summarization_text_input_label)) },
88 | modifier = Modifier
89 | .fillMaxSize()
90 | .weight(.8f)
91 | )
92 |
93 | // Summarize button
94 | Box(
95 | modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center
96 | ) {
97 | Button(
98 | onClick = {
99 | showBottomSheet = true
100 | viewModel.summarize(textInput, context)
101 | }, modifier = Modifier.padding(10.dp)
102 | ) {
103 | Text(
104 | text = stringResource(id = R.string.genai_summarization_summarize_btn),
105 | fontSize = 24.sp
106 | )
107 | }
108 | }
109 |
110 | // Extra options buttons
111 | Box(
112 | modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center
113 | ) {
114 | Row {
115 | SecondaryButton(
116 | buttonText = stringResource(id = R.string.genai_summarization_add_text_btn),
117 | onClick = { textInput = sampleTextOptions.random() }
118 | )
119 |
120 | SecondaryButton(
121 | buttonText = stringResource(id = R.string.genai_summarization_reset_btn),
122 | onClick = { textInput = "" }
123 | )
124 | }
125 | }
126 | }
127 |
128 | if (showBottomSheet) {
129 | ModalBottomSheet(
130 | onDismissRequest = {
131 | showBottomSheet = false
132 | viewModel.clearGeneratedSummary()
133 | }, sheetState = sheetState
134 | ) {
135 | Text(
136 | text = summarizationResult.value,
137 | modifier = Modifier.padding(top = 8.dp, bottom = 24.dp, start = 24.dp, end = 24.dp)
138 | )
139 | }
140 | }
141 | }
142 | }
143 |
144 | @Composable
145 | private fun SecondaryButton(buttonText: String, onClick: () -> Unit) {
146 | Button(
147 | onClick = onClick,
148 | colors = ButtonDefaults.buttonColors(containerColor = MaterialTheme.colorScheme.secondaryContainer),
149 | modifier = Modifier.padding(10.dp)
150 | ) {
151 | Text(
152 | text = buttonText,
153 | fontSize = 16.sp,
154 | color = MaterialTheme.colorScheme.onSecondaryContainer
155 | )
156 | }
157 | }
--------------------------------------------------------------------------------
/ai-catalog/samples/genai-summarization/src/main/java/com/android/ai/samples/genai_summarization/GenAISummarizationViewModel.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.android.ai.samples.genai_summarization
19 |
20 | import android.content.Context
21 | import androidx.lifecycle.ViewModel
22 | import androidx.lifecycle.viewModelScope
23 | import com.google.mlkit.genai.common.FeatureStatus
24 | import com.google.mlkit.genai.summarization.Summarization
25 | import com.google.mlkit.genai.summarization.SummarizationRequest
26 | import com.google.mlkit.genai.summarization.Summarizer
27 | import com.google.mlkit.genai.summarization.SummarizerOptions
28 | import kotlinx.coroutines.flow.MutableStateFlow
29 | import kotlinx.coroutines.flow.StateFlow
30 | import kotlinx.coroutines.guava.await
31 | import kotlinx.coroutines.launch
32 | import javax.inject.Inject
33 | import com.android.ai.samples.geminimultimodal.R
34 |
35 | class GenAISummarizationViewModel @Inject constructor() : ViewModel() {
36 | private val _summarizationGenerated = MutableStateFlow("")
37 | val summarizationGenerated: StateFlow = _summarizationGenerated
38 |
39 | private var summarizer: Summarizer? = null
40 |
41 | fun summarize(textToSummarize: String, context: Context) {
42 | if (textToSummarize.isEmpty()) {
43 | _summarizationGenerated.value = context.getString(R.string.summarization_no_input)
44 | return
45 | }
46 |
47 | val summarizationOptions =
48 | SummarizerOptions.builder(context)
49 | .setOutputType(SummarizerOptions.OutputType.THREE_BULLETS)
50 | .build()
51 | summarizer = Summarization.getClient(summarizationOptions)
52 |
53 | viewModelScope.launch {
54 | summarizer?.let { summarizer ->
55 | val featureStatus = summarizer.checkFeatureStatus().await()
56 | if (featureStatus == FeatureStatus.UNAVAILABLE) {
57 | _summarizationGenerated.value =
58 | context.getString(R.string.summarization_not_available)
59 | return@launch
60 | }
61 |
62 | // If feature is downloadable, making an inference call will automatically start
63 | // the downloading process.
64 | // If feature is downloading, the inference request will automatically execute after
65 | // the feature has been downloaded.
66 | // Alternatively, you can call summarizer.downloadFeature() to monitor the
67 | // progress of the download.
68 | if (featureStatus == FeatureStatus.DOWNLOADABLE ||
69 | featureStatus == FeatureStatus.DOWNLOADING
70 | ) {
71 | _summarizationGenerated.value =
72 | context.getString(R.string.summarization_downloading)
73 | }
74 |
75 | val summarizationRequest = SummarizationRequest.builder(textToSummarize).build()
76 | summarizer.runInference(summarizationRequest) { newText ->
77 | if (_summarizationGenerated.value ==
78 | context.getString(R.string.summarization_downloading)
79 | ) {
80 | clearGeneratedSummary()
81 | }
82 | _summarizationGenerated.value += newText
83 | }
84 | return@launch
85 | }
86 | }
87 | }
88 |
89 | fun clearGeneratedSummary() {
90 | _summarizationGenerated.value = ""
91 | }
92 |
93 | override fun onCleared() {
94 | summarizer?.close()
95 | }
96 | }
--------------------------------------------------------------------------------
/ai-catalog/samples/genai-writing-assistance/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/ai-catalog/samples/genai-writing-assistance/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | plugins {
19 | alias(libs.plugins.android.library)
20 | alias(libs.plugins.jetbrains.kotlin.android)
21 | alias(libs.plugins.ksp)
22 | alias(libs.plugins.compose.compiler)
23 | }
24 |
25 | android {
26 | namespace = "com.android.ai.samples.geminimultimodal"
27 | compileSdk = 35
28 |
29 | buildFeatures {
30 | compose = true
31 | }
32 |
33 | defaultConfig {
34 | minSdk = 26
35 |
36 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
37 | consumerProguardFiles("consumer-rules.pro")
38 | }
39 |
40 | buildTypes {
41 | release {
42 | isMinifyEnabled = false
43 | proguardFiles(
44 | getDefaultProguardFile("proguard-android-optimize.txt"),
45 | "proguard-rules.pro"
46 | )
47 | }
48 | }
49 | compileOptions {
50 | sourceCompatibility = JavaVersion.VERSION_17
51 | targetCompatibility = JavaVersion.VERSION_17
52 | }
53 | composeOptions {
54 | kotlinCompilerExtensionVersion = "1.5.15"
55 | }
56 | kotlinOptions {
57 | jvmTarget = "17"
58 | }
59 | }
60 |
61 | dependencies {
62 |
63 | implementation(libs.androidx.core.ktx)
64 | implementation(libs.androidx.appcompat)
65 | implementation(libs.androidx.material3)
66 | implementation(libs.androidx.activity.compose)
67 | implementation(platform(libs.androidx.compose.bom))
68 | implementation(libs.androidx.material.icons.extended)
69 | implementation(libs.androidx.material.icons.extended)
70 | implementation(libs.hilt.android)
71 | implementation(libs.hilt.navigation.compose)
72 | implementation(libs.androidx.runtime.livedata)
73 | implementation(libs.genai.proofreading)
74 | implementation(libs.genai.rewrite)
75 | implementation(libs.kotlinx.coroutines.guava)
76 | ksp(libs.hilt.compiler)
77 | }
--------------------------------------------------------------------------------
/ai-catalog/samples/genai-writing-assistance/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/ai-samples/2e0c14cccd01b118e7fe41c55551dd310a2d0375/ai-catalog/samples/genai-writing-assistance/consumer-rules.pro
--------------------------------------------------------------------------------
/ai-catalog/samples/genai-writing-assistance/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/ai-catalog/samples/genai-writing-assistance/src/main/java/com/android/ai/samples/genai_writing_assistance/GenAIWritingAssistanceViewModel.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.android.ai.samples.genai_writing_assistance
19 |
20 | import android.content.Context
21 | import androidx.lifecycle.ViewModel
22 | import androidx.lifecycle.viewModelScope
23 | import com.google.mlkit.genai.common.FeatureStatus
24 | import com.google.mlkit.genai.proofreading.Proofreader
25 | import com.google.mlkit.genai.proofreading.ProofreaderOptions
26 | import com.google.mlkit.genai.proofreading.Proofreading
27 | import com.google.mlkit.genai.proofreading.ProofreadingRequest
28 | import com.google.mlkit.genai.rewriting.Rewriter
29 | import com.google.mlkit.genai.rewriting.RewriterOptions
30 | import com.google.mlkit.genai.rewriting.Rewriting
31 | import com.google.mlkit.genai.rewriting.RewritingRequest
32 | import kotlinx.coroutines.flow.MutableStateFlow
33 | import kotlinx.coroutines.flow.StateFlow
34 | import kotlinx.coroutines.guava.await
35 | import kotlinx.coroutines.launch
36 | import javax.inject.Inject
37 | import com.android.ai.samples.geminimultimodal.R
38 |
39 | class GenAIWritingAssistanceViewModel @Inject constructor() : ViewModel() {
40 | private val _resultGenerated = MutableStateFlow("")
41 | val resultGenerated: StateFlow = _resultGenerated
42 |
43 | private var proofreader: Proofreader? = null
44 | private var rewriter: Rewriter? = null
45 |
46 | fun proofread(text: String, context: Context) {
47 | if (text.isEmpty()) {
48 | _resultGenerated.value = context.getString(R.string.genai_writing_assistance_no_input)
49 | return
50 | }
51 |
52 | val proofreadOptions = ProofreaderOptions.builder(context)
53 | .setLanguage(ProofreaderOptions.Language.ENGLISH)
54 | // If input was transcript of speech-to-text, this should be InputType.SPEECH
55 | .setInputType(ProofreaderOptions.InputType.KEYBOARD)
56 | .build()
57 |
58 | proofreader = Proofreading.getClient(proofreadOptions)
59 |
60 | viewModelScope.launch {
61 | proofreader?.let { proofreader ->
62 | val proofreadFeatureStatus = proofreader.checkFeatureStatus().await()
63 | if (proofreadFeatureStatus == FeatureStatus.UNAVAILABLE) {
64 | _resultGenerated.value =
65 | context.getString(R.string.genai_writing_assistance_not_available)
66 | return@launch
67 | }
68 |
69 | // If feature is downloadable, making an inference call will automatically start
70 | // the downloading process.
71 | // If feature is downloading, the inference request will automatically execute after
72 | // the feature has been downloaded.
73 | // Alternatively, you can call proofreader.downloadFeature() to monitor the
74 | // progress of the download.
75 | if (proofreadFeatureStatus == FeatureStatus.DOWNLOADABLE ||
76 | proofreadFeatureStatus == FeatureStatus.DOWNLOADING
77 | ) {
78 | _resultGenerated.value =
79 | context.getString(R.string.genai_writing_assistance_downloading)
80 | }
81 |
82 | val proofreadRequest = ProofreadingRequest.builder(text).build()
83 | // More than 1 result may be generated. Results are returned in descending order of
84 | // quality of confidence. Here we use the first result which has the highest quality
85 | // of confidence.
86 | val results = proofreader.runInference(proofreadRequest).await()
87 | _resultGenerated.value = results.results[0].text
88 | return@launch
89 | }
90 | }
91 | }
92 |
93 | fun rewrite(text: String, rewriteStyle: Int, context: Context) {
94 | if (text.isEmpty()) {
95 | _resultGenerated.value = context.getString(R.string.genai_writing_assistance_no_input)
96 | return
97 | }
98 |
99 | val rewriteOptions = RewriterOptions.builder(context)
100 | .setLanguage(RewriterOptions.Language.ENGLISH)
101 | .setOutputType(rewriteStyle)
102 | .build()
103 |
104 | rewriter = Rewriting.getClient(rewriteOptions)
105 |
106 | viewModelScope.launch {
107 | rewriter?.let { rewriter ->
108 | val rewriteFeatureStatus = rewriter.checkFeatureStatus().await()
109 | if (rewriteFeatureStatus == FeatureStatus.UNAVAILABLE) {
110 | _resultGenerated.value =
111 | context.getString(R.string.genai_writing_assistance_not_available)
112 | return@launch
113 | }
114 |
115 | // If feature is downloadable, making an inference call will automatically start
116 | // the downloading process.
117 | // If feature is downloading, the inference request will automatically execute after
118 | // the feature has been downloaded.
119 | // Alternatively, you can call rewriter.downloadFeature() to monitor the
120 | // progress of the download.
121 | if (rewriteFeatureStatus == FeatureStatus.DOWNLOADABLE ||
122 | rewriteFeatureStatus == FeatureStatus.DOWNLOADING
123 | ) {
124 | _resultGenerated.value =
125 | context.getString(R.string.genai_writing_assistance_downloading)
126 | }
127 |
128 | val rewriteRequest = RewritingRequest.builder(text).build()
129 | // More than 1 result may be generated. Results are returned in descending order of
130 | // quality of confidence. Here we use the first result which has the highest quality of
131 | // confidence.
132 | val results = rewriter.runInference(rewriteRequest).await()
133 | _resultGenerated.value = results.results[0].text
134 | return@launch
135 | }
136 | }
137 | }
138 |
139 | fun clearGeneratedText() {
140 | _resultGenerated.value = ""
141 | }
142 |
143 | override fun onCleared() {
144 | proofreader?.close()
145 | rewriter?.close()
146 | }
147 | }
--------------------------------------------------------------------------------
/ai-catalog/samples/genai-writing-assistance/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Writing Assistance with Nano
4 | Enter text for writing assistance
5 | Proofread
6 | Rewrite
7 | Dismiss
8 | Confirm
9 | Add text to \n proofread
10 | Add text \nto rewrite
11 |
12 | - @string/genai_proofread_sample_text_1
13 | - @string/genai_proofread_sample_text_2
14 | - @string/genai_proofread_sample_text_3
15 |
16 | Their are many reasons why effectiv comunication is importent; it ensures youre message is recieved correctly and avoids confusing. A good proofreading tool can catch embarassing errors that we often miss ourselves, helping to polish our writing so it shines.
17 | Yesterday, we visitted the city zoo, an amazing place too sea wild animuls from all over the world. We saw proud lyons, tall giraffes, and even sum playful monkeys swingin in theyre enclosures which was alot of fun.
18 | My freind and I just grabed sum boba from that new place downtown, there tapioca perls are so chewy and tasty.
19 |
20 | - @string/genai_rewrite_sample_text_1
21 | - @string/genai_rewrite_sample_text_2
22 | - @string/genai_rewrite_sample_text_3
23 |
24 | I was thinking maybe we could potentially get together sometime soon to catch up with each other. Let me know if that is something that would work for you in your schedule.
25 | Regarding the report you finished recently, I wanted to mention that some parts of it were a little bit unclear and I had some difficulty understanding what you were trying to say in those sections. Maybe you could try to explain things more simply next time you write something like this.
26 | Hey, how are things going with you at the moment? Just wanted to check in and see what you have been up to recently and if everything is okay.
27 | Reset
28 |
29 | No text inputted
30 | Feature is not available on this device
31 | Downloading feature
32 |
33 |
--------------------------------------------------------------------------------
/ai-catalog/samples/imagen/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/ai-catalog/samples/imagen/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.library)
3 | alias(libs.plugins.jetbrains.kotlin.android)
4 | alias(libs.plugins.ksp)
5 | alias(libs.plugins.compose.compiler)
6 | }
7 |
8 | android {
9 | namespace = "com.android.ai.samples.imagen"
10 | compileSdk = 35
11 |
12 | buildFeatures {
13 | compose = true
14 | }
15 |
16 | defaultConfig {
17 | minSdk = 24
18 |
19 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
20 | consumerProguardFiles("consumer-rules.pro")
21 | }
22 |
23 | buildTypes {
24 | release {
25 | isMinifyEnabled = false
26 | proguardFiles(
27 | getDefaultProguardFile("proguard-android-optimize.txt"),
28 | "proguard-rules.pro"
29 | )
30 | }
31 | }
32 | compileOptions {
33 | sourceCompatibility = JavaVersion.VERSION_17
34 | targetCompatibility = JavaVersion.VERSION_17
35 | }
36 | composeOptions {
37 | kotlinCompilerExtensionVersion = "1.5.15"
38 | }
39 | kotlinOptions {
40 | jvmTarget = "17"
41 | }
42 | }
43 |
44 | dependencies {
45 |
46 | implementation(libs.androidx.core.ktx)
47 | implementation(libs.androidx.appcompat)
48 | implementation(libs.androidx.material3)
49 | implementation(platform(libs.androidx.compose.bom))
50 | implementation(libs.androidx.material.icons.extended)
51 | implementation(platform(libs.firebase.bom))
52 | implementation(libs.firebase.ai)
53 | implementation(libs.hilt.android)
54 | implementation(libs.hilt.navigation.compose)
55 | implementation(libs.androidx.runtime.livedata)
56 | ksp(libs.hilt.compiler)
57 |
58 | testImplementation(libs.junit)
59 | androidTestImplementation(libs.androidx.junit)
60 | androidTestImplementation(libs.androidx.espresso.core)
61 | }
--------------------------------------------------------------------------------
/ai-catalog/samples/imagen/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/ai-samples/2e0c14cccd01b118e7fe41c55551dd310a2d0375/ai-catalog/samples/imagen/consumer-rules.pro
--------------------------------------------------------------------------------
/ai-catalog/samples/imagen/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/ai-catalog/samples/imagen/src/main/java/com/android/ai/samples/imagen/ImagenViewModel.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.android.ai.samples.imagen
19 |
20 | import android.graphics.Bitmap
21 | import androidx.lifecycle.LiveData
22 | import androidx.lifecycle.MutableLiveData
23 | import androidx.lifecycle.ViewModel
24 | import androidx.lifecycle.viewModelScope
25 | import com.google.firebase.Firebase
26 | import com.google.firebase.ai.ai
27 | import com.google.firebase.ai.type.GenerativeBackend
28 | import com.google.firebase.ai.type.ImagenAspectRatio
29 | import com.google.firebase.ai.type.ImagenGenerationConfig
30 | import com.google.firebase.ai.type.ImagenImageFormat
31 | import com.google.firebase.ai.type.PublicPreviewAPI
32 | import kotlinx.coroutines.flow.MutableStateFlow
33 | import kotlinx.coroutines.launch
34 | import javax.inject.Inject
35 |
36 | class ImagenViewModel @Inject constructor(): ViewModel() {
37 |
38 | private val _imageGenerated: MutableStateFlow = MutableStateFlow(null)
39 | val imageGenerated: MutableStateFlow = _imageGenerated
40 |
41 | private val _isGenerating = MutableLiveData(false)
42 | val isGenerating: LiveData = _isGenerating
43 |
44 | @OptIn(PublicPreviewAPI::class)
45 | private val imagenModel = Firebase.ai(backend = GenerativeBackend.vertexAI()).imagenModel(
46 | modelName = "imagen-4.0-generate-preview-05-20",
47 | generationConfig = ImagenGenerationConfig(
48 | numberOfImages = 1,
49 | aspectRatio = ImagenAspectRatio.SQUARE_1x1,
50 | imageFormat = ImagenImageFormat.jpeg(compressionQuality = 75),
51 | ),
52 | )
53 |
54 | @OptIn(PublicPreviewAPI::class)
55 | fun generateImage(prompt: String) {
56 | _isGenerating.value = true
57 | viewModelScope.launch {
58 | val imageResponse = imagenModel.generateImages(
59 | prompt = prompt,
60 | )
61 | val image = imageResponse.images.first()
62 |
63 | val bitmapImage = image.asBitmap()
64 | _imageGenerated.value = bitmapImage
65 | _isGenerating.postValue(false)
66 | }
67 | }
68 | }
--------------------------------------------------------------------------------
/ai-catalog/samples/imagen/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
17 |
18 |
19 | See Code
20 | An oil painting of Alcatraz
21 | Imagen image generation
22 | Generate
23 | Generating...
24 | Prompt
25 |
--------------------------------------------------------------------------------
/ai-catalog/samples/magic-selfie/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/ai-catalog/samples/magic-selfie/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.library)
3 | alias(libs.plugins.jetbrains.kotlin.android)
4 | alias(libs.plugins.ksp)
5 | alias(libs.plugins.compose.compiler)
6 | }
7 |
8 | android {
9 | namespace = "com.android.ai.samples.magicselfie"
10 | compileSdk = 35
11 |
12 | buildFeatures {
13 | compose = true
14 | }
15 |
16 | defaultConfig {
17 | minSdk = 24
18 |
19 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
20 | consumerProguardFiles("consumer-rules.pro")
21 | }
22 |
23 | buildTypes {
24 | release {
25 | isMinifyEnabled = false
26 | proguardFiles(
27 | getDefaultProguardFile("proguard-android-optimize.txt"),
28 | "proguard-rules.pro"
29 | )
30 | }
31 | }
32 | compileOptions {
33 | sourceCompatibility = JavaVersion.VERSION_17
34 | targetCompatibility = JavaVersion.VERSION_17
35 | }
36 | composeOptions {
37 | kotlinCompilerExtensionVersion = "1.5.15"
38 | }
39 | kotlinOptions {
40 | jvmTarget = "17"
41 | }
42 | }
43 |
44 | dependencies {
45 |
46 | implementation(libs.androidx.core.ktx)
47 | implementation(libs.androidx.appcompat)
48 | implementation(libs.androidx.material3)
49 | implementation(libs.androidx.activity.compose)
50 | implementation(platform(libs.androidx.compose.bom))
51 | implementation(libs.androidx.material.icons.extended)
52 | implementation(platform(libs.firebase.bom))
53 | implementation(libs.firebase.ai)
54 | implementation(libs.hilt.android)
55 | implementation(libs.hilt.navigation.compose)
56 | implementation(libs.androidx.runtime.livedata)
57 | implementation("com.google.android.gms:play-services-mlkit-subject-segmentation:16.0.0-beta1")
58 | ksp(libs.hilt.compiler)
59 |
60 | testImplementation(libs.junit)
61 | androidTestImplementation(libs.androidx.junit)
62 | androidTestImplementation(libs.androidx.espresso.core)
63 | }
--------------------------------------------------------------------------------
/ai-catalog/samples/magic-selfie/src/main/java/com/android/ai/samples/magicselfie/MagicSelfieViewModel.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.android.ai.samples.magicselfie
19 |
20 | import android.graphics.Bitmap
21 | import android.graphics.Canvas
22 | import android.graphics.Paint
23 | import androidx.lifecycle.LiveData
24 | import androidx.lifecycle.MutableLiveData
25 | import androidx.lifecycle.ViewModel
26 | import androidx.lifecycle.viewModelScope
27 | import com.google.firebase.Firebase
28 | import com.google.firebase.ai.ai
29 | import com.google.firebase.ai.type.GenerativeBackend
30 | import com.google.firebase.ai.type.ImagenAspectRatio
31 | import com.google.firebase.ai.type.ImagenGenerationConfig
32 | import com.google.firebase.ai.type.ImagenImageFormat
33 | import com.google.firebase.ai.type.PublicPreviewAPI
34 | import com.google.mlkit.vision.common.InputImage
35 | import com.google.mlkit.vision.segmentation.subject.SubjectSegmentation
36 | import com.google.mlkit.vision.segmentation.subject.SubjectSegmenterOptions
37 | import kotlinx.coroutines.flow.MutableStateFlow
38 | import kotlinx.coroutines.launch
39 | import javax.inject.Inject
40 | import kotlin.math.roundToInt
41 |
42 | @OptIn(PublicPreviewAPI::class)
43 | class MagicSelfieViewModel @Inject constructor(): ViewModel() {
44 |
45 |
46 | private val _foregroundBitmap = MutableStateFlow(null)
47 | val foregroundBitmap: MutableStateFlow = _foregroundBitmap
48 |
49 | private val _progress = MutableLiveData(null)
50 | val progress: LiveData = _progress
51 |
52 | private val imagenModel = Firebase.ai(backend = GenerativeBackend.vertexAI()).imagenModel(
53 | modelName = "imagen-4.0-generate-preview-05-20",
54 | generationConfig = ImagenGenerationConfig(
55 | numberOfImages = 1,
56 | aspectRatio = ImagenAspectRatio.PORTRAIT_3x4,
57 | imageFormat = ImagenImageFormat.jpeg(compressionQuality = 75),
58 | ),
59 | )
60 |
61 |
62 | private val subjectSegmenter = SubjectSegmentation.getClient(
63 | SubjectSegmenterOptions.Builder()
64 | .enableForegroundBitmap()
65 | .build()
66 | )
67 |
68 | fun createMagicSelfie(bitmap: Bitmap, prompt: String) {
69 | val image = InputImage.fromBitmap(bitmap, 0)
70 |
71 | _progress.value = "Removing selfie background..."
72 |
73 | subjectSegmenter.process(image)
74 | .addOnSuccessListener {
75 | it.foregroundBitmap?.let {
76 | _foregroundBitmap.value = it
77 | generateBackground(prompt)
78 | }
79 | }.addOnFailureListener() {
80 | _progress.postValue("Something went wrong :(")
81 | }
82 | }
83 |
84 | private fun generateBackground(prompt: String) {
85 | _progress.value = "Generating new background..."
86 |
87 | viewModelScope.launch {
88 | val imageResponse = imagenModel.generateImages(
89 | prompt = prompt
90 | )
91 | val image = imageResponse.images.first()
92 |
93 | val bitmapImage = image.asBitmap()
94 |
95 | _foregroundBitmap.value = combineBitmaps(_foregroundBitmap.value!!, bitmapImage)
96 | _progress.postValue(null)
97 | }
98 | }
99 |
100 | fun combineBitmaps(foreground: Bitmap, background: Bitmap): Bitmap {
101 | val height = background.height
102 | val width = background.width
103 |
104 | val resultBitmap = Bitmap.createBitmap(width, height, background.config!!)
105 | val canvas = Canvas(resultBitmap)
106 | val paint = Paint()
107 | canvas.drawBitmap(background, 0f, 0f, paint)
108 |
109 | var foregroundHeight = foreground.height
110 | var foregroundWidth = foreground.width
111 | val ratio = foregroundWidth.toFloat() / foregroundHeight.toFloat()
112 |
113 | foregroundHeight = height
114 | foregroundWidth = (foregroundHeight * ratio).roundToInt()
115 |
116 | val scaledForeground = Bitmap.createScaledBitmap(foreground, foregroundWidth, foregroundHeight, false)
117 |
118 | val left = (width - scaledForeground.width) / 2f
119 | val top = (height - scaledForeground.height.toFloat())
120 | canvas.drawBitmap(scaledForeground, left, top, paint)
121 |
122 | return resultBitmap
123 | }
124 |
125 | }
--------------------------------------------------------------------------------
/ai-catalog/samples/magic-selfie/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Magic Selfie
4 | See code
5 | Share image
6 |
--------------------------------------------------------------------------------
/ai-catalog/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | pluginManagement {
19 | repositories {
20 | google {
21 | content {
22 | includeGroupByRegex("com\\.android.*")
23 | includeGroupByRegex("com\\.google.*")
24 | includeGroupByRegex("androidx.*")
25 | }
26 | }
27 | mavenCentral()
28 | gradlePluginPortal()
29 | }
30 | }
31 | dependencyResolutionManagement {
32 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
33 | repositories {
34 | google()
35 | mavenCentral()
36 | }
37 | }
38 |
39 | rootProject.name = "AI Sample Catalog"
40 | include(":app")
41 | include(":samples:gemini-multimodal")
42 | include(":samples:gemini-chatbot")
43 | include(":samples:genai-summarization")
44 | include(":samples:genai-writing-assistance")
45 | include(":samples:genai-image-description")
46 | include(":samples:imagen")
47 | include(":samples:magic-selfie")
48 | include(":samples:gemini-video-summarization")
49 |
--------------------------------------------------------------------------------
/gemini-nano/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /.gradle
3 | /.idea
4 | /.kotlin
--------------------------------------------------------------------------------
/gemini-nano/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to Contribute
2 |
3 | We would love to accept your patches and contributions to this project.
4 |
5 | ## Before you begin
6 |
7 | ### Sign our Contributor License Agreement
8 |
9 | Contributions to this project must be accompanied by a
10 | [Contributor License Agreement](https://cla.developers.google.com/about) (CLA).
11 | You (or your employer) retain the copyright to your contribution; this simply
12 | gives us permission to use and redistribute your contributions as part of the
13 | project.
14 |
15 | If you or your current employer have already signed the Google CLA (even if it
16 | was for a different project), you probably don't need to do it again.
17 |
18 | Visit to see your current agreements or to
19 | sign a new one.
20 |
21 | ### Review our Community Guidelines
22 |
23 | This project follows [Google's Open Source Community
24 | Guidelines](https://opensource.google/conduct/).
25 |
26 | ## Contribution process
27 |
28 | ### Code Reviews
29 |
30 | All submissions, including submissions by project members, require review. We
31 | use [GitHub pull requests](https://docs.github.com/articles/about-pull-requests)
32 | for this purpose.
33 |
--------------------------------------------------------------------------------
/gemini-nano/README.md:
--------------------------------------------------------------------------------
1 | # Google AI Edge SDK Quickstart
2 |
3 | * [Read more about Google AI Edge SDK](https://developer.android.com/ai/gemini-nano-experimental)
4 |
5 | This sample app demonstrates how to use the Google AI Edge SDK to access Gemini
6 | Nano on Android.
7 |
8 | ## Getting Started
9 |
10 | * Run the sample code on your Android device with AICore installed.
11 |
12 | ## License
13 |
14 | Copyright 2024 Google, Inc.
15 |
16 | Licensed to the Apache Software Foundation (ASF) under one or more contributor
17 | license agreements. See the NOTICE file distributed with this work for
18 | additional information regarding copyright ownership. The ASF licenses this file
19 | to you under the Apache License, Version 2.0 (the "License"); you may not use
20 | this file except in compliance with the License. You may obtain a copy of the
21 | License at
22 |
23 | http://www.apache.org/licenses/LICENSE-2.0
24 |
25 | Unless required by applicable law or agreed to in writing, software distributed
26 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
27 | CONDITIONS OF ANY KIND, either express or implied. See the License for the
28 | specific language governing permissions and limitations under the License.
29 |
--------------------------------------------------------------------------------
/gemini-nano/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/gemini-nano/app/build.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | apply plugin: 'com.android.application'
18 | apply plugin: 'kotlin-android'
19 |
20 | android {
21 | namespace = "com.google.ai.edge.aicore.demo"
22 | compileSdk 34
23 |
24 | defaultConfig {
25 | applicationId 'com.google.ai.edge.aicore.demo'
26 | minSdk 31
27 | targetSdk 34
28 | versionCode 1
29 | versionName '1.0'
30 |
31 | testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
32 | }
33 |
34 | buildTypes {
35 | all {
36 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
37 | }
38 | release {
39 | minifyEnabled true
40 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
41 | }
42 | debug {
43 | isDefault true
44 | minifyEnabled false
45 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
46 | }
47 | testBuildType "debug"
48 | }
49 | compileOptions {
50 | sourceCompatibility JavaVersion.VERSION_17
51 | targetCompatibility JavaVersion.VERSION_17
52 | }
53 |
54 | }
55 |
56 | dependencies {
57 | implementation 'androidx.preference:preference-ktx:1.2.1'
58 | implementation 'androidx.recyclerview:recyclerview:1.3.2'
59 | implementation 'com.google.ai.edge.aicore:aicore:0.0.1-exp01'
60 | implementation 'com.google.android.material:material:1.12.0'
61 | implementation 'com.google.guava:guava:31.1-jre'
62 | implementation 'org.reactivestreams:reactive-streams:1.0.4'
63 |
64 | androidTestImplementation 'androidx.test:rules:1.6.1'
65 | androidTestImplementation 'androidx.test:runner:1.6.2'
66 | androidTestImplementation 'androidx.test.ext:junit:1.2.1'
67 | androidTestImplementation 'com.google.truth:truth:1.1.3'
68 | }
69 |
--------------------------------------------------------------------------------
/gemini-nano/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/gemini-nano/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
20 |
21 |
26 |
27 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
41 |
42 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/gemini-nano/app/src/main/java/com/google/ai/edge/aicore/demo/ContentAdapter.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Google LLC. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.ai.edge.aicore.demo
18 |
19 | import android.content.res.ColorStateList
20 | import android.graphics.Color
21 | import androidx.recyclerview.widget.RecyclerView
22 | import android.util.Pair
23 | import android.view.LayoutInflater
24 | import android.view.View
25 | import android.view.ViewGroup
26 | import android.widget.TextView
27 |
28 | /** A [RecyclerView.Adapter] for displaying the request and response views. */
29 | class ContentAdapter : RecyclerView.Adapter() {
30 | private val contentList: MutableList> = ArrayList()
31 |
32 | fun addContent(viewType: Int, content: String?) {
33 | contentList.add(Pair(viewType, content))
34 | notifyDataSetChanged()
35 | }
36 |
37 | fun updateStreamingResponse(response: String) {
38 | contentList[contentList.size - 1] = Pair(VIEW_TYPE_RESPONSE, response)
39 | notifyDataSetChanged()
40 | }
41 |
42 | override fun getItemViewType(position: Int): Int {
43 | return contentList[position].first
44 | }
45 |
46 | override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
47 | val layoutId =
48 | when (viewType) {
49 | VIEW_TYPE_REQUEST -> R.layout.row_item_request
50 | VIEW_TYPE_RESPONSE -> R.layout.row_item_response
51 | VIEW_TYPE_RESPONSE_ERROR -> R.layout.row_item_response
52 | else -> throw IllegalArgumentException("Invalid view type $viewType")
53 | }
54 |
55 | val layoutInflater = LayoutInflater.from(viewGroup.context)
56 | return ViewHolder(layoutInflater.inflate(layoutId, viewGroup, false))
57 | }
58 |
59 | override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
60 | viewHolder.bind(contentList[position])
61 | }
62 |
63 | override fun getItemCount(): Int {
64 | return contentList.size
65 | }
66 |
67 | class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
68 | private val contentTextView: TextView
69 | private val defaultTextColors: ColorStateList
70 |
71 | init {
72 | contentTextView = view.findViewById(R.id.content_text_view)
73 | defaultTextColors = contentTextView.textColors
74 | }
75 |
76 | fun bind(content: Pair) {
77 | contentTextView.text = content.second
78 | if (content.first == VIEW_TYPE_RESPONSE_ERROR) {
79 | contentTextView.setTextColor(Color.RED)
80 | } else {
81 | contentTextView.setTextColor(defaultTextColors)
82 | }
83 | }
84 | }
85 |
86 | companion object {
87 | const val VIEW_TYPE_REQUEST = 0
88 | const val VIEW_TYPE_RESPONSE = 1
89 | const val VIEW_TYPE_RESPONSE_ERROR = 2
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/gemini-nano/app/src/main/java/com/google/ai/edge/aicore/demo/EntryChoiceActivity.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Google LLC. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.ai.edge.aicore.demo
18 |
19 | import android.content.Intent
20 | import android.os.Bundle
21 | import androidx.appcompat.app.AppCompatActivity
22 | import android.util.Log
23 | import android.view.View
24 | import android.widget.TextView
25 | import android.widget.Toast
26 | import androidx.lifecycle.lifecycleScope
27 | import com.google.ai.edge.aicore.DownloadCallback
28 | import com.google.ai.edge.aicore.DownloadConfig
29 | import com.google.ai.edge.aicore.GenerativeAIException
30 | import com.google.ai.edge.aicore.GenerativeModel
31 | import com.google.ai.edge.aicore.demo.java.MainActivity
32 | import com.google.ai.edge.aicore.generationConfig
33 | import java.lang.String.format
34 | import java.util.Locale
35 | import kotlinx.coroutines.launch
36 |
37 | class EntryChoiceActivity : AppCompatActivity() {
38 |
39 | private var modelDownloaded = false
40 | private var model: GenerativeModel? = null
41 |
42 | override fun onCreate(savedInstanceState: Bundle?) {
43 | super.onCreate(savedInstanceState)
44 | setContentView(R.layout.activity_entry_choice)
45 |
46 | findViewById(R.id.kotlin_entry_point).setOnClickListener {
47 | if (modelDownloaded) {
48 | val intent =
49 | Intent(
50 | this@EntryChoiceActivity,
51 | com.google.ai.edge.aicore.demo.kotlin.MainActivity::class.java,
52 | )
53 | startActivity(intent)
54 | } else {
55 | Toast.makeText(this, R.string.model_unavailable, Toast.LENGTH_SHORT).show()
56 | }
57 | }
58 |
59 | findViewById(R.id.java_entry_point).setOnClickListener {
60 | if (modelDownloaded) {
61 | val intent = Intent(this@EntryChoiceActivity, MainActivity::class.java)
62 | startActivity(intent)
63 | } else {
64 | Toast.makeText(this, R.string.model_unavailable, Toast.LENGTH_SHORT).show()
65 | }
66 | }
67 |
68 | ensureModelDownloaded()
69 | }
70 |
71 | private fun ensureModelDownloaded() {
72 | val downloadProgressTextView = findViewById(R.id.download_progress_text_view)
73 | var totalBytesToDownload = 0L
74 | val downloadConfig =
75 | DownloadConfig(
76 | object : DownloadCallback {
77 | override fun onDownloadStarted(bytesToDownload: Long) {
78 | totalBytesToDownload = bytesToDownload
79 | }
80 |
81 | override fun onDownloadFailed(failureStatus: String, e: GenerativeAIException) {
82 | Log.e(TAG, "Failed to download model.", e)
83 | }
84 |
85 | override fun onDownloadProgress(totalBytesDownloaded: Long) {
86 | if (totalBytesToDownload > 0) {
87 | downloadProgressTextView?.visibility = View.VISIBLE
88 | downloadProgressTextView?.text =
89 | format(
90 | Locale.ENGLISH,
91 | "Downloading model: %d / %d MB (%.2f%%)",
92 | totalBytesDownloaded / MEGABYTE,
93 | totalBytesToDownload / MEGABYTE,
94 | 100.0 * totalBytesDownloaded / totalBytesToDownload,
95 | )
96 | }
97 | }
98 |
99 | override fun onDownloadCompleted() {
100 | modelDownloaded = true
101 | }
102 | }
103 | )
104 |
105 | model = GenerativeModel(generationConfig { context = applicationContext }, downloadConfig)
106 | lifecycleScope.launch {
107 | try {
108 | model?.prepareInferenceEngine()
109 | } catch (e: GenerativeAIException) {
110 | Log.e(TAG, "Failed to check model availability.", e)
111 | Toast.makeText(applicationContext, e.message, Toast.LENGTH_SHORT).show()
112 | }
113 | }
114 | }
115 |
116 | override fun onDestroy() {
117 | super.onDestroy()
118 | model?.close()
119 | }
120 |
121 | companion object {
122 | private const val TAG = "EntryChoiceActivity"
123 | private const val MEGABYTE = 1024 * 1024
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/gemini-nano/app/src/main/java/com/google/ai/edge/aicore/demo/GenerationConfigDialog.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Google LLC. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.ai.edge.aicore.demo
18 |
19 | import android.app.Activity
20 | import android.app.AlertDialog
21 | import android.app.Dialog
22 | import android.content.DialogInterface
23 | import android.os.Bundle
24 | import androidx.fragment.app.DialogFragment
25 | import android.widget.EditText
26 | import com.google.ai.edge.aicore.demo.GenerationConfigUtils.getMaxOutputTokens
27 | import com.google.ai.edge.aicore.demo.GenerationConfigUtils.getTemperature
28 | import com.google.ai.edge.aicore.demo.GenerationConfigUtils.getTopK
29 | import com.google.ai.edge.aicore.demo.GenerationConfigUtils.setMaxOutputTokens
30 | import com.google.ai.edge.aicore.demo.GenerationConfigUtils.setTemperature
31 | import com.google.ai.edge.aicore.demo.GenerationConfigUtils.setTopK
32 |
33 | class GenerationConfigDialog : DialogFragment() {
34 | interface OnConfigUpdateListener {
35 | fun onConfigUpdated()
36 | }
37 |
38 | override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
39 | val activity: Activity = requireActivity()
40 | val builder = AlertDialog.Builder(activity)
41 |
42 | val view = layoutInflater.inflate(R.layout.dialog_generation_config, null)
43 | val temperatureEditText = view.findViewById(R.id.temperature_edit_text)
44 | temperatureEditText.setText(getTemperature(activity).toString())
45 | val topKEditText = view.findViewById(R.id.top_k_edit_text)
46 | topKEditText.setText(getTopK(activity).toString())
47 | val maxOutputTokensEditText = view.findViewById(R.id.max_output_tokens_edit_text)
48 | maxOutputTokensEditText.setText(getMaxOutputTokens(activity).toString())
49 |
50 | builder
51 | .setView(view)
52 | .setPositiveButton(R.string.button_save) { _: DialogInterface, _: Int ->
53 | setTemperature(activity, temperatureEditText.text.toString().toFloat())
54 | setTopK(activity, topKEditText.text.toString().toInt())
55 | setMaxOutputTokens(activity, maxOutputTokensEditText.text.toString().toInt())
56 | if (activity is OnConfigUpdateListener) {
57 | (activity as OnConfigUpdateListener).onConfigUpdated()
58 | }
59 | }
60 | .setNegativeButton(R.string.button_cancel, null)
61 | return builder.create()
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/gemini-nano/app/src/main/java/com/google/ai/edge/aicore/demo/GenerationConfigUtils.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Google LLC. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.ai.edge.aicore.demo
18 |
19 | import android.content.Context
20 | import androidx.preference.PreferenceManager
21 |
22 | object GenerationConfigUtils {
23 | @JvmStatic
24 | fun getTemperature(context: Context): Float {
25 | return PreferenceManager.getDefaultSharedPreferences(context)
26 | .getFloat(context.getString(R.string.pref_key_temperature), 0.2f)
27 | }
28 |
29 | @JvmStatic
30 | fun setTemperature(context: Context, temperature: Float) {
31 | PreferenceManager.getDefaultSharedPreferences(context)
32 | .edit()
33 | .putFloat(context.getString(R.string.pref_key_temperature), temperature)
34 | .apply()
35 | }
36 |
37 | @JvmStatic
38 | fun getTopK(context: Context): Int {
39 | return PreferenceManager.getDefaultSharedPreferences(context)
40 | .getInt(context.getString(R.string.pref_key_top_k), 16)
41 | }
42 |
43 | @JvmStatic
44 | fun setTopK(context: Context, topK: Int) {
45 | PreferenceManager.getDefaultSharedPreferences(context)
46 | .edit()
47 | .putInt(context.getString(R.string.pref_key_top_k), topK)
48 | .apply()
49 | }
50 |
51 | @JvmStatic
52 | fun getMaxOutputTokens(context: Context): Int {
53 | return PreferenceManager.getDefaultSharedPreferences(context)
54 | .getInt(context.getString(R.string.pref_key_max_output_tokens), 256)
55 | }
56 |
57 | @JvmStatic
58 | fun setMaxOutputTokens(context: Context, maxTokenCount: Int) {
59 | PreferenceManager.getDefaultSharedPreferences(context)
60 | .edit()
61 | .putInt(context.getString(R.string.pref_key_max_output_tokens), maxTokenCount)
62 | .apply()
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/gemini-nano/app/src/main/java/com/google/ai/edge/aicore/demo/kotlin/MainActivity.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Google LLC. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.ai.edge.aicore.demo.kotlin
18 |
19 | import android.os.Bundle
20 | import android.text.TextUtils
21 | import android.widget.Button
22 | import android.widget.CompoundButton
23 | import android.widget.EditText
24 | import android.widget.Toast
25 | import androidx.appcompat.app.AppCompatActivity
26 | import androidx.lifecycle.lifecycleScope
27 | import androidx.recyclerview.widget.LinearLayoutManager
28 | import androidx.recyclerview.widget.RecyclerView
29 | import com.google.ai.edge.aicore.GenerativeAIException
30 | import com.google.ai.edge.aicore.GenerativeModel
31 | import com.google.ai.edge.aicore.demo.ContentAdapter
32 | import com.google.ai.edge.aicore.demo.GenerationConfigDialog
33 | import com.google.ai.edge.aicore.demo.GenerationConfigUtils
34 | import com.google.ai.edge.aicore.demo.R
35 | import com.google.ai.edge.aicore.generationConfig
36 | import java.util.concurrent.Future
37 | import kotlinx.coroutines.flow.onCompletion
38 | import kotlinx.coroutines.future.future
39 |
40 | /** Demonstrates the AICore SDK usage from Kotlin. */
41 | class MainActivity : AppCompatActivity(), GenerationConfigDialog.OnConfigUpdateListener {
42 |
43 | private var requestEditText: EditText? = null
44 | private var sendButton: Button? = null
45 | private var streamingSwitch: CompoundButton? = null
46 | private var configButton: Button? = null
47 | private var contentRecyclerView: RecyclerView? = null
48 | private var model: GenerativeModel? = null
49 | private var useStreaming = false
50 | private var inGenerating = false
51 | private var generateContentFuture: Future? = null
52 |
53 | private val contentAdapter = ContentAdapter()
54 |
55 | override fun onCreate(savedInstanceState: Bundle?) {
56 | super.onCreate(savedInstanceState)
57 | setContentView(R.layout.activity_main)
58 |
59 | requestEditText = findViewById(R.id.request_edit_text)
60 | sendButton = findViewById(R.id.send_button)
61 | sendButton!!.setOnClickListener {
62 | if (inGenerating) {
63 | generateContentFuture?.cancel(true)
64 | endGeneratingUi()
65 | } else {
66 | val request = requestEditText?.text.toString()
67 | if (TextUtils.isEmpty(request)) {
68 | Toast.makeText(this, R.string.prompt_is_empty, Toast.LENGTH_SHORT).show()
69 | return@setOnClickListener
70 | }
71 |
72 | contentAdapter.addContent(ContentAdapter.VIEW_TYPE_REQUEST, request)
73 | startGeneratingUi()
74 | generateContent(request)
75 | }
76 | inGenerating = !inGenerating
77 | }
78 |
79 | streamingSwitch = findViewById(R.id.streaming_switch)
80 | streamingSwitch!!.setOnCheckedChangeListener { _: CompoundButton, isChecked: Boolean ->
81 | useStreaming = isChecked
82 | }
83 | useStreaming = streamingSwitch!!.isChecked
84 |
85 | configButton = findViewById(R.id.config_button)
86 | configButton!!.setOnClickListener {
87 | GenerationConfigDialog().show(supportFragmentManager, null)
88 | }
89 |
90 | contentRecyclerView = findViewById(R.id.content_recycler_view)
91 | contentRecyclerView!!.layoutManager = LinearLayoutManager(this)
92 | contentRecyclerView!!.adapter = contentAdapter
93 |
94 | initGenerativeModel()
95 | }
96 |
97 | override fun onDestroy() {
98 | super.onDestroy()
99 | model?.close()
100 | }
101 |
102 | private fun initGenerativeModel() {
103 | model =
104 | GenerativeModel(
105 | generationConfig {
106 | context = applicationContext
107 | temperature = GenerationConfigUtils.getTemperature(applicationContext)
108 | topK = GenerationConfigUtils.getTopK(applicationContext)
109 | maxOutputTokens = GenerationConfigUtils.getMaxOutputTokens(applicationContext)
110 | }
111 | )
112 | }
113 |
114 | private fun generateContent(request: String) {
115 | generateContentFuture =
116 | lifecycleScope.future {
117 | try {
118 | if (useStreaming) {
119 | var hasFirstStreamingResult = false
120 | var result = ""
121 | model!!
122 | .generateContentStream(request)
123 | .onCompletion { endGeneratingUi() }
124 | .collect { response ->
125 | run {
126 | result += response.text
127 | if (hasFirstStreamingResult) {
128 | contentAdapter.updateStreamingResponse(result)
129 | } else {
130 | contentAdapter.addContent(ContentAdapter.VIEW_TYPE_RESPONSE, result)
131 | hasFirstStreamingResult = true
132 | }
133 | }
134 | }
135 | } else {
136 | val response = model!!.generateContent(request)
137 | contentAdapter.addContent(ContentAdapter.VIEW_TYPE_RESPONSE, response.text!!)
138 | endGeneratingUi()
139 | }
140 | } catch (e: GenerativeAIException) {
141 | contentAdapter.addContent(ContentAdapter.VIEW_TYPE_RESPONSE_ERROR, e.message!!)
142 | endGeneratingUi()
143 | }
144 | }
145 | }
146 |
147 | private fun startGeneratingUi() {
148 | sendButton?.setText(R.string.button_cancel)
149 | requestEditText?.setText(R.string.empty)
150 | streamingSwitch?.isEnabled = false
151 | configButton?.isEnabled = false
152 | }
153 |
154 | private fun endGeneratingUi() {
155 | sendButton?.setText(R.string.button_send)
156 | streamingSwitch?.isEnabled = true
157 | configButton?.isEnabled = true
158 | contentRecyclerView?.smoothScrollToPosition(contentAdapter.itemCount - 1)
159 | }
160 |
161 | override fun onConfigUpdated() {
162 | model?.close()
163 | initGenerativeModel()
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/gemini-nano/app/src/main/res/drawable/list_item_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
19 |
20 |
24 |
25 |
--------------------------------------------------------------------------------
/gemini-nano/app/src/main/res/drawable/request_item_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
19 |
20 |
24 |
25 |
--------------------------------------------------------------------------------
/gemini-nano/app/src/main/res/drawable/response_item_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
19 |
20 |
24 |
25 |
--------------------------------------------------------------------------------
/gemini-nano/app/src/main/res/layout/activity_entry_choice.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
26 |
27 |
33 |
34 |
43 |
44 |
53 |
54 |
--------------------------------------------------------------------------------
/gemini-nano/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
23 |
24 |
29 |
30 |
37 |
38 |
45 |
46 |
47 |
48 |
53 |
54 |
58 |
59 |
67 |
68 |
73 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/gemini-nano/app/src/main/res/layout/dialog_generation_config.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
22 |
23 |
27 |
28 |
33 |
34 |
35 |
36 |
40 |
41 |
46 |
47 |
48 |
49 |
53 |
54 |
59 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/gemini-nano/app/src/main/res/layout/row_item_request.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
24 |
25 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/gemini-nano/app/src/main/res/layout/row_item_response.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
24 |
25 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/gemini-nano/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 | #FFBB86FC
19 | #FF6200EE
20 | #FF3700B3
21 | #FF03DAC5
22 | #FF018786
23 | #FF000000
24 | #FF4286f4
25 | #FFFFFFFF
26 |
27 |
--------------------------------------------------------------------------------
/gemini-nano/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 | 16dp
20 | 16dp
21 |
22 |
--------------------------------------------------------------------------------
/gemini-nano/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 | Gemini Nano demo
19 | Run the demo written in Kotlin
20 | Run the demo written in Java
21 | Send
22 | Generating...
23 | Streaming
24 | Temperature
25 | TopK
26 | MaxOutputTokens
27 | pk_temp
28 | pk_topk
29 | pk_mot
30 | Change config
31 | Save
32 | Cancel
33 | Model is unavailable yet and downloading in background
34 | Please enter an non empty prompt
35 |
36 |
37 |
--------------------------------------------------------------------------------
/gemini-nano/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
32 |
33 |
--------------------------------------------------------------------------------
/gemini-nano/build.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
18 |
19 | buildscript {
20 | ext.kotlin_version = '1.9.0'
21 |
22 | repositories {
23 | google()
24 | mavenCentral()
25 | }
26 | dependencies {
27 | classpath 'com.android.tools.build:gradle:8.1.4'
28 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
29 |
30 | // NOTE: Do not place your application dependencies here; they belong
31 | // in the individual module build.gradle files
32 | }
33 | }
34 |
35 | allprojects {
36 | repositories {
37 | google()
38 | mavenCentral()
39 | mavenLocal()
40 | }
41 | }
42 |
43 | tasks.register('clean', Delete) {
44 | delete rootProject.buildDir
45 | }
46 |
--------------------------------------------------------------------------------
/gemini-nano/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. For more details, visit
12 | # https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Kotlin code style for this project: "official" or "obsolete":
19 | kotlin.code.style=official
20 | # Enables namespacing of each library's R class so that its R class includes only the
21 | # resources declared in the library itself and none from the library's dependencies,
22 | # thereby reducing the size of the R class for that library
23 | android.nonTransitiveRClass=true
24 |
--------------------------------------------------------------------------------
/gemini-nano/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/ai-samples/2e0c14cccd01b118e7fe41c55551dd310a2d0375/gemini-nano/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gemini-nano/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/gemini-nano/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 | @rem SPDX-License-Identifier: Apache-2.0
17 | @rem
18 |
19 | @if "%DEBUG%"=="" @echo off
20 | @rem ##########################################################################
21 | @rem
22 | @rem Gradle startup script for Windows
23 | @rem
24 | @rem ##########################################################################
25 |
26 | @rem Set local scope for the variables with windows NT shell
27 | if "%OS%"=="Windows_NT" setlocal
28 |
29 | set DIRNAME=%~dp0
30 | if "%DIRNAME%"=="" set DIRNAME=.
31 | @rem This is normally unused
32 | set APP_BASE_NAME=%~n0
33 | set APP_HOME=%DIRNAME%
34 |
35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
37 |
38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
40 |
41 | @rem Find java.exe
42 | if defined JAVA_HOME goto findJavaFromJavaHome
43 |
44 | set JAVA_EXE=java.exe
45 | %JAVA_EXE% -version >NUL 2>&1
46 | if %ERRORLEVEL% equ 0 goto execute
47 |
48 | echo. 1>&2
49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
50 | echo. 1>&2
51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
52 | echo location of your Java installation. 1>&2
53 |
54 | goto fail
55 |
56 | :findJavaFromJavaHome
57 | set JAVA_HOME=%JAVA_HOME:"=%
58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
59 |
60 | if exist "%JAVA_EXE%" goto execute
61 |
62 | echo. 1>&2
63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
64 | echo. 1>&2
65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
66 | echo location of your Java installation. 1>&2
67 |
68 | goto fail
69 |
70 | :execute
71 | @rem Setup the command line
72 |
73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
74 |
75 |
76 | @rem Execute Gradle
77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
78 |
79 | :end
80 | @rem End local scope for the variables with windows NT shell
81 | if %ERRORLEVEL% equ 0 goto mainEnd
82 |
83 | :fail
84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
85 | rem the _cmd.exe /c_ return code!
86 | set EXIT_CODE=%ERRORLEVEL%
87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
89 | exit /b %EXIT_CODE%
90 |
91 | :mainEnd
92 | if "%OS%"=="Windows_NT" endlocal
93 |
94 | :omega
95 |
--------------------------------------------------------------------------------
/gemini-nano/settings.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | rootProject.name = "Gemini Nano demo"
18 | include ':app'
19 |
--------------------------------------------------------------------------------