diff --git a/firebase-ai/README.md b/firebase-ai/README.md index 5ae40900b..c6cd38bb9 100644 --- a/firebase-ai/README.md +++ b/firebase-ai/README.md @@ -5,6 +5,8 @@ generative AI models (like Gemini) to build AI-powered features and applications For more information about Firebase AI Logic, visit the [documentation](http://firebase.google.com/docs/ai-logic). +Screenshot + ## Setup & Configuration ### Prerequisites @@ -25,33 +27,40 @@ To try out this sample app, you need to use latest stable version of Android Stu You can find the implementation for each feature by clicking on the links below: -### Text / Chat -- [Travel tips](app/src/main/java/com/google/firebase/quickstart/ai/feature/text/TravelTipsViewModel.kt): The user wants the model to help a new traveler with travel tips -- [Chatbot recommendations for courses](app/src/main/java/com/google/firebase/quickstart/ai/feature/text/CourseRecommendationsViewModel.kt): A chatbot suggests courses for a performing arts program. -- [Weather Chat](app/src/main/java/com/google/firebase/quickstart/ai/feature/text/WeatherChatViewModel.kt): Use function calling to get the weather conditions for a specific US city on a specific date. -- [Grounding with Google Search](app/src/main/java/com/google/firebase/quickstart/ai/feature/text/GoogleSearchGroundingViewModel.kt): Use Grounding with Google Search to get responses based on up-to-date information from the web. -- [Thinking](app/src/main/java/com/google/firebase/quickstart/ai/feature/text/ThinkingChatViewModel.kt): Gemini 2.5 Flash with dynamic thinking -- [Server Prompt Templates - Gemini](app/src/main/java/com/google/firebase/quickstart/ai/feature/text/ServerPromptTemplateViewModel.kt): Generate an invoice using server prompt templates. +### Gemini 3 +- [Translate text](app/src/main/java/com/google/firebase/quickstart/ai/feature/text/TranslationViewModel.kt): Use Gemini 3.1 Flash-Lite to translate text +- [SVG Generator](app/src/main/java/com/google/firebase/quickstart/ai/feature/text/SvgViewModel.kt): Use Gemini 3.5 Flash to create SVG illustrations +- [Gemini 3.1 Flash Image (Nano Banana 2)](app/src/main/java/com/google/firebase/quickstart/ai/feature/text/NanoBanana2ViewModel.kt): Generate and/or edit images using Nano Banana 2 preview +- [Gemini 3 Pro Image (Nano Banana Pro)](app/src/main/java/com/google/firebase/quickstart/ai/feature/text/NanoBananaProViewModel.kt): Generate and/or edit images using Nano Banana Pro preview -### Image analysis / generation -- [Gemini 2.5 Flash Image (aka nanobanana)](app/src/main/java/com/google/firebase/quickstart/ai/feature/text/ImageGenerationViewModel.kt): Generate and/or edit images using Gemini 2.5 Flash Image aka nanobanana -- [SVG Generator](app/src/main/java/com/google/firebase/quickstart/ai/feature/text/SvgViewModel.kt): Use Gemini 3 Flash preview to create SVG illustrations -- [Blog post creator (Vertex AI)](app/src/main/java/com/google/firebase/quickstart/ai/feature/text/ImageBlogCreatorViewModel.kt): Create a blog post from an image file stored in Cloud Storage. +### Nano Banana +- [Gemini 3.1 Flash Image (Nano Banana 2)](app/src/main/java/com/google/firebase/quickstart/ai/feature/text/NanoBanana2ViewModel.kt): Generate and/or edit images using Nano Banana 2 preview +- [Gemini 3 Pro Image (Nano Banana Pro)](app/src/main/java/com/google/firebase/quickstart/ai/feature/text/NanoBananaProViewModel.kt): Generate and/or edit images using Nano Banana Pro preview +- [Gemini 2.5 Flash Image (Nano Banana)](app/src/main/java/com/google/firebase/quickstart/ai/feature/text/NanoBananaViewModel.kt): Generate and/or edit images using Nano Banana (GA) -### Audio analysis -- [Audio Summarization](app/src/main/java/com/google/firebase/quickstart/ai/feature/text/AudioSummarizationViewModel.kt): Summarize an audio file +### Multimodal understanding +- [Audio Summarization](app/src/main/java/com/google/firebase/quickstart/ai/feature/text/AudioSummarizationViewModel.kt): Use Gemini 3.1 Flash Lite to summarize an audio file +- [Summarize video](app/src/main/java/com/google/firebase/quickstart/ai/feature/text/VideoSummarizationViewModel.kt): Summarize a video and extract important dialogue. - [Translation from audio (Vertex AI)](app/src/main/java/com/google/firebase/quickstart/ai/feature/text/AudioTranslationViewModel.kt): Translate an audio file stored in Cloud Storage - -### Video analysis +- [Blog post creator (Vertex AI)](app/src/main/java/com/google/firebase/quickstart/ai/feature/text/ImageBlogCreatorViewModel.kt): Create a blog post from an image file stored in Cloud Storage. +- [Document comparison (Vertex AI)](app/src/main/java/com/google/firebase/quickstart/ai/feature/text/DocumentComparisonViewModel.kt): Compare the contents of 2 documents stored in Cloud Storage. - [Hashtags for a video (Vertex AI)](app/src/main/java/com/google/firebase/quickstart/ai/feature/text/VideoHashtagGeneratorViewModel.kt): Generate hashtags for a video ad stored in Cloud Storage -- [Summarize video](app/src/main/java/com/google/firebase/quickstart/ai/feature/text/VideoSummarizationViewModel.kt): Summarize a video and extract important dialogue. -### Live API (Real-time bidrectional streaming) -- [ForecastTalk](app/src/main/java/com/google/firebase/quickstart/ai/feature/live/StreamAudioViewModel.kt): Use bidirectional streaming to get information about weather conditions -- [Gemini Live (Video input)](app/src/main/java/com/google/firebase/quickstart/ai/feature/live/StreamVideoViewModel.kt): Use bidirectional streaming to chat with Gemini using your phone's camera +### Tools and function calling +- [Grounding with Google Search](app/src/main/java/com/google/firebase/quickstart/ai/feature/text/GoogleSearchGroundingViewModel.kt): Use Grounding with Google Search to get responses based on up-to-date information from the web. +- [Weather Chat](app/src/main/java/com/google/firebase/quickstart/ai/feature/text/WeatherChatViewModel.kt): Use function calling to get the weather conditions for a specific US city on a specific date. +- [Gemini Live (audio input)](app/src/main/java/com/google/firebase/quickstart/ai/feature/live/StreamAudioViewModel.kt): Use bidirectional streaming to get information about weather conditions for a specific US city on a specific date +- [Gemini Live (video input)](app/src/main/java/com/google/firebase/quickstart/ai/feature/live/StreamVideoViewModel.kt): Use bidirectional streaming to chat with Gemini using your phone's camera + +### Live API streaming +- [Gemini Live (audio input)](app/src/main/java/com/google/firebase/quickstart/ai/feature/live/StreamAudioViewModel.kt): Use bidirectional streaming to get information about weather conditions for a specific US city on a specific date +- [Gemini Live (video input)](app/src/main/java/com/google/firebase/quickstart/ai/feature/live/StreamVideoViewModel.kt): Use bidirectional streaming to chat with Gemini using your phone's camera + +### Server prompt templates +- [Server Prompt Templates - Gemini](app/src/main/java/com/google/firebase/quickstart/ai/feature/text/ServerPromptTemplateViewModel.kt): Generate an invoice using server prompt templates. -### Document (PDFs) analysis -- [Document comparison (Vertex AI)](app/src/main/java/com/google/firebase/quickstart/ai/feature/text/DocumentComparisonViewModel.kt): Compare the contents of 2 documents in Cloud Storage. +### Hybrid inference +- [Hybrid Receipt Scanner](app/src/main/java/com/google/firebase/quickstart/ai/feature/hybrid/HybridInferenceViewModel.kt): Use hybrid inference to scan receipts and extract expense data on-device whenever possible. ## All samples diff --git a/firebase-ai/app/src/main/java/com/google/firebase/quickstart/ai/feature/text/ThinkingChatViewModel.kt b/firebase-ai/app/src/main/java/com/google/firebase/quickstart/ai/feature/text/NanoBanana2ViewModel.kt similarity index 51% rename from firebase-ai/app/src/main/java/com/google/firebase/quickstart/ai/feature/text/ThinkingChatViewModel.kt rename to firebase-ai/app/src/main/java/com/google/firebase/quickstart/ai/feature/text/NanoBanana2ViewModel.kt index 8b59f9e2a..a768a5ea6 100644 --- a/firebase-ai/app/src/main/java/com/google/firebase/quickstart/ai/feature/text/ThinkingChatViewModel.kt +++ b/firebase-ai/app/src/main/java/com/google/firebase/quickstart/ai/feature/text/NanoBanana2ViewModel.kt @@ -7,16 +7,25 @@ import com.google.firebase.ai.Chat import com.google.firebase.ai.ai import com.google.firebase.ai.type.Content import com.google.firebase.ai.type.GenerativeBackend +import com.google.firebase.ai.type.ResponseModality import com.google.firebase.ai.type.generationConfig -import com.google.firebase.ai.type.thinkingConfig import com.google.firebase.quickstart.ai.ui.UiChatMessage @Serializable -object ThinkingChatRoute +object NanoBanana2Route -class ThinkingChatViewModel : ChatViewModel() { +class NanoBanana2ViewModel : ChatViewModel() { - override val initialPrompt: String = "Analogize photosynthesis and growing up." + override val initialPrompt: String = """ + A photo of a glossy magazine cover, the minimal blue cover + has the large bold words Nano Banana. The text is in a serif + font and fills the view. No other text. In front of the text + there is a portrait of a person in a sleek and minimal dress. + She is playfully holding the number 2, which is the focal point. + Put the issue number and "Feb 2026" date in the corner along with + a barcode. The magazine is on a shelf against an orange plastered + wall, within a designer store. + """.trimIndent() private val chat: Chat @@ -24,12 +33,9 @@ class ThinkingChatViewModel : ChatViewModel() { val generativeModel = Firebase.ai( backend = GenerativeBackend.googleAI() ).generativeModel( - modelName = "gemini-2.5-flash", + modelName = "gemini-3.1-flash-image-preview", generationConfig = generationConfig { - thinkingConfig = thinkingConfig { - includeThoughts = true - thinkingBudget = -1 // Dynamic Thinking - } + responseModalities = listOf(ResponseModality.TEXT, ResponseModality.IMAGE) } ) chat = generativeModel.startChat() diff --git a/firebase-ai/app/src/main/java/com/google/firebase/quickstart/ai/feature/text/NanoBananaProViewModel.kt b/firebase-ai/app/src/main/java/com/google/firebase/quickstart/ai/feature/text/NanoBananaProViewModel.kt new file mode 100644 index 000000000..3fbe9aad6 --- /dev/null +++ b/firebase-ai/app/src/main/java/com/google/firebase/quickstart/ai/feature/text/NanoBananaProViewModel.kt @@ -0,0 +1,51 @@ +package com.google.firebase.quickstart.ai.feature.text + +import kotlinx.serialization.Serializable + +import com.google.firebase.Firebase +import com.google.firebase.ai.Chat +import com.google.firebase.ai.ai +import com.google.firebase.ai.type.Content +import com.google.firebase.ai.type.GenerativeBackend +import com.google.firebase.ai.type.ResponseModality +import com.google.firebase.ai.type.generationConfig +import com.google.firebase.quickstart.ai.ui.UiChatMessage + +@Serializable +object NanoBananaProRoute + +class NanoBananaProViewModel : ChatViewModel() { + + override val initialPrompt: String = """ + Present a clear, 45° top-down isometric miniature 3D cartoon + scene of London, featuring its most iconic landmarks and + architectural elements. Use soft, refined textures with + realistic PBR materials and gentle, lifelike lighting and shadows. + Integrate the current weather conditions directly into the city + environment to create an immersive atmospheric mood. Use a clean, + minimalistic composition with a soft, solid-colored background. + At the top-center, place the title "London" in large bold text, + a prominent weather icon beneath it, then the date (small text) + and temperature (medium text). All text must be centered with + consistent spacing, and may subtly overlap the tops of the buildings. + """.trimIndent() + + private val chat: Chat + + init { + val generativeModel = Firebase.ai( + backend = GenerativeBackend.googleAI() + ).generativeModel( + modelName = "gemini-3-pro-image-preview", + generationConfig = generationConfig { + responseModalities = listOf(ResponseModality.TEXT, ResponseModality.IMAGE) + } + ) + chat = generativeModel.startChat() + } + + override suspend fun performSendMessage(prompt: Content, currentMessages: List) { + val response = chat.sendMessage(prompt) + validateAndDisplayResponse(response, currentMessages) + } +} diff --git a/firebase-ai/app/src/main/java/com/google/firebase/quickstart/ai/feature/text/ImageGenerationViewModel.kt b/firebase-ai/app/src/main/java/com/google/firebase/quickstart/ai/feature/text/NanoBananaViewModel.kt similarity index 94% rename from firebase-ai/app/src/main/java/com/google/firebase/quickstart/ai/feature/text/ImageGenerationViewModel.kt rename to firebase-ai/app/src/main/java/com/google/firebase/quickstart/ai/feature/text/NanoBananaViewModel.kt index 09c080215..b5b9ddf9a 100644 --- a/firebase-ai/app/src/main/java/com/google/firebase/quickstart/ai/feature/text/ImageGenerationViewModel.kt +++ b/firebase-ai/app/src/main/java/com/google/firebase/quickstart/ai/feature/text/NanoBananaViewModel.kt @@ -12,9 +12,9 @@ import com.google.firebase.ai.type.generationConfig import com.google.firebase.quickstart.ai.ui.UiChatMessage @Serializable -object ImageGenerationRoute +object NanoBananaRoute -class ImageGenerationViewModel : ChatViewModel() { +class NanoBananaViewModel : ChatViewModel() { override val initialPrompt: String = """ Hi, can you create a 3d rendered image of a pig diff --git a/firebase-ai/app/src/main/java/com/google/firebase/quickstart/ai/feature/text/SvgViewModel.kt b/firebase-ai/app/src/main/java/com/google/firebase/quickstart/ai/feature/text/SvgViewModel.kt index bc6b4d7e2..130fa7e9b 100644 --- a/firebase-ai/app/src/main/java/com/google/firebase/quickstart/ai/feature/text/SvgViewModel.kt +++ b/firebase-ai/app/src/main/java/com/google/firebase/quickstart/ai/feature/text/SvgViewModel.kt @@ -8,6 +8,7 @@ import com.google.firebase.Firebase import com.google.firebase.ai.GenerativeModel import com.google.firebase.ai.ai import com.google.firebase.ai.type.GenerativeBackend +import com.google.firebase.ai.type.ThinkingLevel import com.google.firebase.ai.type.content import com.google.firebase.ai.type.generationConfig import com.google.firebase.ai.type.thinkingConfig @@ -31,7 +32,7 @@ class SvgViewModel : ViewModel() { generativeModel = Firebase.ai( backend = GenerativeBackend.googleAI() ).generativeModel( - modelName = "gemini-3-flash-preview", + modelName = "gemini-3.5-flash", systemInstruction = content { text( """ @@ -45,7 +46,7 @@ class SvgViewModel : ViewModel() { }, generationConfig = generationConfig { thinkingConfig { - thinkingBudget = -1 + thinkingLevel = ThinkingLevel.HIGH } } ) @@ -58,6 +59,10 @@ class SvgViewModel : ViewModel() { try { val response = generativeModel.generateContent(prompt) val newSvg = response.text + // Remove the ```xml [...] ``` around the svg + ?.removePrefix("```xml") + ?.removeSuffix("```") + ?.trimIndent() if (newSvg != null) { _uiState.value = SvgUiState.Success(listOf(newSvg) + currentSvgs) } else { diff --git a/firebase-ai/app/src/main/java/com/google/firebase/quickstart/ai/ui/navigation/FirebaseAISamples.kt b/firebase-ai/app/src/main/java/com/google/firebase/quickstart/ai/ui/navigation/FirebaseAISamples.kt index af633b814..66adb0c14 100644 --- a/firebase-ai/app/src/main/java/com/google/firebase/quickstart/ai/ui/navigation/FirebaseAISamples.kt +++ b/firebase-ai/app/src/main/java/com/google/firebase/quickstart/ai/ui/navigation/FirebaseAISamples.kt @@ -1,35 +1,33 @@ package com.google.firebase.quickstart.ai.ui.navigation -import com.google.firebase.quickstart.ai.feature.live.StreamAudioViewModel -import com.google.firebase.quickstart.ai.feature.live.StreamVideoViewModel import com.google.firebase.quickstart.ai.feature.hybrid.HybridInferenceRoute import com.google.firebase.quickstart.ai.feature.hybrid.HybridInferenceViewModel +import com.google.firebase.quickstart.ai.feature.live.StreamAudioViewModel import com.google.firebase.quickstart.ai.feature.live.StreamRealtimeAudioRoute import com.google.firebase.quickstart.ai.feature.live.StreamRealtimeVideoRoute +import com.google.firebase.quickstart.ai.feature.live.StreamVideoViewModel import com.google.firebase.quickstart.ai.feature.text.AudioSummarizationRoute import com.google.firebase.quickstart.ai.feature.text.AudioSummarizationViewModel import com.google.firebase.quickstart.ai.feature.text.AudioTranslationRoute import com.google.firebase.quickstart.ai.feature.text.AudioTranslationViewModel -import com.google.firebase.quickstart.ai.feature.text.CourseRecommendationsRoute -import com.google.firebase.quickstart.ai.feature.text.CourseRecommendationsViewModel import com.google.firebase.quickstart.ai.feature.text.DocumentComparisonRoute import com.google.firebase.quickstart.ai.feature.text.DocumentComparisonViewModel import com.google.firebase.quickstart.ai.feature.text.GoogleSearchGroundingRoute import com.google.firebase.quickstart.ai.feature.text.GoogleSearchGroundingViewModel import com.google.firebase.quickstart.ai.feature.text.ImageBlogCreatorRoute import com.google.firebase.quickstart.ai.feature.text.ImageBlogCreatorViewModel -import com.google.firebase.quickstart.ai.feature.text.ImageGenerationRoute -import com.google.firebase.quickstart.ai.feature.text.ImageGenerationViewModel +import com.google.firebase.quickstart.ai.feature.text.NanoBanana2Route +import com.google.firebase.quickstart.ai.feature.text.NanoBanana2ViewModel +import com.google.firebase.quickstart.ai.feature.text.NanoBananaProRoute +import com.google.firebase.quickstart.ai.feature.text.NanoBananaProViewModel +import com.google.firebase.quickstart.ai.feature.text.NanoBananaRoute +import com.google.firebase.quickstart.ai.feature.text.NanoBananaViewModel import com.google.firebase.quickstart.ai.feature.text.ServerPromptTemplateRoute import com.google.firebase.quickstart.ai.feature.text.ServerPromptTemplateViewModel import com.google.firebase.quickstart.ai.feature.text.SvgRoute import com.google.firebase.quickstart.ai.feature.text.SvgViewModel -import com.google.firebase.quickstart.ai.feature.text.ThinkingChatRoute -import com.google.firebase.quickstart.ai.feature.text.ThinkingChatViewModel import com.google.firebase.quickstart.ai.feature.text.TranslationRoute import com.google.firebase.quickstart.ai.feature.text.TranslationViewModel -import com.google.firebase.quickstart.ai.feature.text.TravelTipsRoute -import com.google.firebase.quickstart.ai.feature.text.TravelTipsViewModel import com.google.firebase.quickstart.ai.feature.text.VideoHashtagGeneratorRoute import com.google.firebase.quickstart.ai.feature.text.VideoHashtagGeneratorViewModel import com.google.firebase.quickstart.ai.feature.text.VideoSummarizationRoute @@ -44,24 +42,15 @@ val FIREBASE_AI_SAMPLES = listOf( route = TranslationRoute, screenType = ScreenType.CHAT, viewModelClass = TranslationViewModel::class, - categories = listOf(Category.TEXT) + categories = listOf(Category.GEMINI3) ), Sample( - title = "Travel tips", - description = "The user wants the model to help a new traveler" + - " with travel tips", - route = TravelTipsRoute, - screenType = ScreenType.CHAT, - viewModelClass = TravelTipsViewModel::class, - categories = listOf(Category.TEXT), - ), - Sample( - title = "Chatbot recommendations for courses", - description = "A chatbot suggests courses for a performing arts program.", - route = CourseRecommendationsRoute, - screenType = ScreenType.CHAT, - viewModelClass = CourseRecommendationsViewModel::class, - categories = listOf(Category.TEXT), + title = "SVG Generator", + description = "Use Gemini 3.5 Flash to create SVG illustrations", + route = SvgRoute, + screenType = ScreenType.SVG, + viewModelClass = SvgViewModel::class, + categories = listOf(Category.GEMINI3) ), Sample( title = "Audio Summarization", @@ -69,7 +58,15 @@ val FIREBASE_AI_SAMPLES = listOf( route = AudioSummarizationRoute, screenType = ScreenType.CHAT, viewModelClass = AudioSummarizationViewModel::class, - categories = listOf(Category.AUDIO), + categories = listOf(Category.MULTIMODAL_UNDERSTANDING), + ), + Sample( + title = "Summarize video", + description = "Summarize a video and extract important dialogue.", + route = VideoSummarizationRoute, + screenType = ScreenType.CHAT, + viewModelClass = VideoSummarizationViewModel::class, + categories = listOf(Category.MULTIMODAL_UNDERSTANDING) ), Sample( title = "Translation from audio (Vertex AI)", @@ -77,7 +74,7 @@ val FIREBASE_AI_SAMPLES = listOf( route = AudioTranslationRoute, screenType = ScreenType.CHAT, viewModelClass = AudioTranslationViewModel::class, - categories = listOf(Category.AUDIO) + categories = listOf(Category.MULTIMODAL_UNDERSTANDING) ), Sample( title = "Blog post creator (Vertex AI)", @@ -85,24 +82,39 @@ val FIREBASE_AI_SAMPLES = listOf( route = ImageBlogCreatorRoute, screenType = ScreenType.CHAT, viewModelClass = ImageBlogCreatorViewModel::class, - categories = listOf(Category.IMAGE) + categories = listOf(Category.MULTIMODAL_UNDERSTANDING) + ), + Sample( + title = "Gemini 3.1 Flash Image (Nano Banana 2)", + description = "Generate and/or edit images using Nano Banana 2 preview", + route = NanoBanana2Route, + screenType = ScreenType.CHAT, + viewModelClass = NanoBanana2ViewModel::class, + categories = listOf(Category.NANO_BANANA, Category.GEMINI3) ), Sample( - title = "Gemini 2.5 Flash Image (aka nanobanana)", - description = "Generate and/or edit images using Gemini 2.5 Flash Image aka nanobanana", - route = ImageGenerationRoute, + title = "Gemini 3 Pro Image (Nano Banana Pro)", + description = "Generate and/or edit images using Nano Banana Pro preview", + route = NanoBananaProRoute, screenType = ScreenType.CHAT, - viewModelClass = ImageGenerationViewModel::class, - categories = listOf(Category.IMAGE) + viewModelClass = NanoBananaProViewModel::class, + categories = listOf(Category.NANO_BANANA, Category.GEMINI3) + ), + Sample( + title = "Gemini 2.5 Flash Image (Nano Banana)", + description = "Generate and/or edit images using Nano Banana (GA)", + route = NanoBananaRoute, + screenType = ScreenType.CHAT, + viewModelClass = NanoBananaViewModel::class, + categories = listOf(Category.NANO_BANANA) ), Sample( title = "Document comparison (Vertex AI)", - description = "Compare the contents of 2 documents." + - " Only supported by the Vertex AI Gemini API because the documents are stored in Cloud Storage", + description = "Compare the contents of 2 documents stored in Cloud Storage", route = DocumentComparisonRoute, screenType = ScreenType.CHAT, viewModelClass = DocumentComparisonViewModel::class, - categories = listOf(Category.DOCUMENT) + categories = listOf(Category.MULTIMODAL_UNDERSTANDING) ), Sample( title = "Hashtags for a video (Vertex AI)", @@ -110,51 +122,43 @@ val FIREBASE_AI_SAMPLES = listOf( route = VideoHashtagGeneratorRoute, screenType = ScreenType.CHAT, viewModelClass = VideoHashtagGeneratorViewModel::class, - categories = listOf(Category.VIDEO) + categories = listOf(Category.MULTIMODAL_UNDERSTANDING) ), Sample( - title = "Summarize video", - description = "Summarize a video and extract important dialogue.", - route = VideoSummarizationRoute, + title = "Grounding with Google Search", + description = "Use Grounding with Google Search to get responses based on up-to-date information from the" + + " web.", + route = GoogleSearchGroundingRoute, screenType = ScreenType.CHAT, - viewModelClass = VideoSummarizationViewModel::class, - categories = listOf(Category.VIDEO) + viewModelClass = GoogleSearchGroundingViewModel::class, + categories = listOf(Category.TOOLS_FC) ), Sample( - title = "ForecastTalk", + title = "Weather Chat", + description = "Use function calling to get the weather conditions" + + " for a specific US city on a specific date.", + route = WeatherChatRoute, + screenType = ScreenType.CHAT, + viewModelClass = WeatherChatViewModel::class, + categories = listOf(Category.TOOLS_FC) + ), + Sample( + title = "Gemini Live (audio input)", description = "Use bidirectional streaming to get information about" + " weather conditions for a specific US city on a specific date", route = StreamRealtimeAudioRoute, screenType = ScreenType.BIDI, viewModelClass = StreamAudioViewModel::class, - categories = listOf(Category.LIVE_API, Category.AUDIO, Category.FUNCTION_CALLING) + categories = listOf(Category.LIVE_API, Category.TOOLS_FC) ), Sample( - title = "Gemini Live (Video input)", + title = "Gemini Live (video input)", description = "Use bidirectional streaming to chat with Gemini using your" + " phone's camera", route = StreamRealtimeVideoRoute, screenType = ScreenType.BIDI_VIDEO, viewModelClass = StreamVideoViewModel::class, - categories = listOf(Category.LIVE_API, Category.VIDEO, Category.FUNCTION_CALLING) - ), - Sample( - title = "Weather Chat", - description = "Use function calling to get the weather conditions" + - " for a specific US city on a specific date.", - route = WeatherChatRoute, - screenType = ScreenType.CHAT, - viewModelClass = WeatherChatViewModel::class, - categories = listOf(Category.TEXT, Category.FUNCTION_CALLING) - ), - Sample( - title = "Grounding with Google Search", - description = "Use Grounding with Google Search to get responses based on up-to-date information from the" + - " web.", - route = GoogleSearchGroundingRoute, - screenType = ScreenType.CHAT, - viewModelClass = GoogleSearchGroundingViewModel::class, - categories = listOf(Category.TEXT) + categories = listOf(Category.LIVE_API, Category.TOOLS_FC) ), Sample( title = "Server Prompt Templates - Gemini", @@ -163,23 +167,7 @@ val FIREBASE_AI_SAMPLES = listOf( route = ServerPromptTemplateRoute, screenType = ScreenType.SERVER_PROMPT, viewModelClass = ServerPromptTemplateViewModel::class, - categories = listOf(Category.TEXT), - ), - Sample( - title = "Thinking", - description = "Gemini 2.5 Flash with dynamic thinking", - route = ThinkingChatRoute, - screenType = ScreenType.CHAT, - viewModelClass = ThinkingChatViewModel::class, - categories = listOf(Category.TEXT) - ), - Sample( - title = "SVG Generator", - description = "Use Gemini 3 Flash preview to create SVG illustrations", - route = SvgRoute, - screenType = ScreenType.SVG, - viewModelClass = SvgViewModel::class, - categories = listOf(Category.IMAGE, Category.TEXT) + categories = listOf(Category.SERVER_PROMPTS), ), Sample( title = "Hybrid Receipt Scanner", @@ -187,6 +175,7 @@ val FIREBASE_AI_SAMPLES = listOf( route = HybridInferenceRoute, screenType = ScreenType.HYBRID, viewModelClass = HybridInferenceViewModel::class, - categories = listOf(Category.TEXT, Category.IMAGE, Category.HYBRID) + categories = listOf(Category.HYBRID) + ) ) diff --git a/firebase-ai/app/src/main/java/com/google/firebase/quickstart/ai/ui/navigation/MainMenuScreen.kt b/firebase-ai/app/src/main/java/com/google/firebase/quickstart/ai/ui/navigation/MainMenuScreen.kt index 74a9f9689..c34d0f89e 100644 --- a/firebase-ai/app/src/main/java/com/google/firebase/quickstart/ai/ui/navigation/MainMenuScreen.kt +++ b/firebase-ai/app/src/main/java/com/google/firebase/quickstart/ai/ui/navigation/MainMenuScreen.kt @@ -35,7 +35,6 @@ fun MainMenuScreen( onSampleClicked: (Sample) -> Unit ) { MenuScreen( - filterTitle = "Filter by use case:", filters = Category.entries.toList(), samples = FIREBASE_AI_SAMPLES, onSampleClicked = { @@ -46,7 +45,6 @@ fun MainMenuScreen( @Composable fun MenuScreen( - filterTitle: String, filters: List, samples: List, onSampleClicked: (sample: Sample) -> Unit = {} @@ -54,11 +52,11 @@ fun MenuScreen( Column( modifier = Modifier .fillMaxSize() - .padding(16.dp) ) { var selectedCategory by rememberSaveable { mutableStateOf(filters.first()) } - Text(text = filterTitle, style = MaterialTheme.typography.titleLarge) - LazyRow { + LazyRow( + modifier = Modifier.padding(vertical = 8.dp) + ) { items(filters) { capability -> FilterChip( onClick = { selectedCategory = capability }, @@ -77,21 +75,16 @@ fun MenuScreen( } else { null }, - modifier = Modifier.padding(end = 8.dp) + modifier = Modifier.padding(horizontal = 4.dp) ) } } - Text( - text = "Samples", - style = MaterialTheme.typography.titleLarge, - modifier = Modifier.padding(top = 16.dp) - ) val filteredSamples = samples.filter { it.categories.contains(selectedCategory) } LazyVerticalGrid( columns = GridCells.Adaptive(MIN_CARD_SIZE), - modifier = Modifier + modifier = Modifier.padding(horizontal = 16.dp) ) { items(filteredSamples) { sample -> SampleItem(sample.title, sample.description, onItemClicked = { diff --git a/firebase-ai/app/src/main/java/com/google/firebase/quickstart/ai/ui/navigation/Sample.kt b/firebase-ai/app/src/main/java/com/google/firebase/quickstart/ai/ui/navigation/Sample.kt index af631a417..d800f0184 100644 --- a/firebase-ai/app/src/main/java/com/google/firebase/quickstart/ai/ui/navigation/Sample.kt +++ b/firebase-ai/app/src/main/java/com/google/firebase/quickstart/ai/ui/navigation/Sample.kt @@ -6,13 +6,12 @@ import kotlin.reflect.KClass enum class Category( val label: String ) { - TEXT("Text"), - IMAGE("Image"), - VIDEO("Video"), - AUDIO("Audio"), - DOCUMENT("Document"), - FUNCTION_CALLING("Function calling"), - LIVE_API("Live API Streaming"), + GEMINI3("Gemini 3"), + NANO_BANANA("Nano Banana"), + MULTIMODAL_UNDERSTANDING("Multimodal understanding"), + TOOLS_FC("Tools and function calling"), + LIVE_API("Live API streaming"), + SERVER_PROMPTS("Server prompt templates"), HYBRID("Hybrid inference") } diff --git a/firebase-ai/docs/result.png b/firebase-ai/docs/result.png new file mode 100644 index 000000000..0e47c5158 Binary files /dev/null and b/firebase-ai/docs/result.png differ