Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added ai-catalog/app/src/main/res/drawable/bg.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 4 additions & 5 deletions ai-catalog/gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ hilt = "2.56.2"
hiltNavigationCompose = "1.2.0"
ksp = "2.1.0-1.0.29"
runtimeLivedata = "1.7.6"
material3Android = "1.3.1"
media3 = "1.6.1"
firebaseCommonKtx = "21.0.0"
uiToolingPreviewAndroid = "1.8.1"
Expand All @@ -31,8 +30,9 @@ uiToolingPreview = "1.8.3"
uiTooling = "1.8.3"
firebaseAi = "16.2.0"
lifecycleViewmodelAndroid = "2.8.7"
material3 = "1.3.2"
material3 = "1.5.0-alpha01"
uiTextGoogleFonts = "1.8.1"
runtime = "1.8.3"

[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
Expand All @@ -58,8 +58,7 @@ androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-toolin
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
androidx-ui-google-fonts = { group = "androidx.compose.ui", name = "ui-text-google-fonts", version.ref = "uiTextGoogleFonts" }
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
androidx-material3-android = { group = "androidx.compose.material3", name = "material3-android", version.ref = "material3Android" }
androidx-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" }
androidx-material-icons-extended = { module = "androidx.compose.material:material-icons-extended" }
androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" }
androidx-navigation-runtime-ktx = { group = "androidx.navigation", name = "navigation-runtime-ktx", version.ref = "navigationRuntimeKtx" }
Expand All @@ -77,7 +76,7 @@ ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview
ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling", version.ref = "uiTooling" }
google-firebase-ai = { group = "com.google.firebase", name = "firebase-ai", version.ref = "firebaseAi" }
androidx-lifecycle-viewmodel-android = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-android", version.ref = "lifecycleViewmodelAndroid" }
material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" }
androidx-runtime = { group = "androidx.compose.runtime", name = "runtime", version.ref = "runtime" }

[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
Expand Down
2 changes: 1 addition & 1 deletion ai-catalog/samples/gemini-chatbot/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,13 @@ dependencies {
implementation(libs.androidx.appcompat)
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.material.icons.extended)
implementation(libs.androidx.material3)
implementation(platform(libs.firebase.bom))
implementation(libs.firebase.ai)
implementation(libs.hilt.android)
implementation(libs.hilt.navigation.compose)
implementation(libs.androidx.runtime.livedata)
implementation(libs.androidx.lifecycle.runtime.compose)
implementation(libs.androidx.material3.android)
ksp(libs.hilt.compiler)

testImplementation(libs.junit)
Expand Down
3 changes: 1 addition & 2 deletions ai-catalog/samples/gemini-live-todo/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,11 @@ dependencies {
implementation(platform(libs.firebase.bom))
implementation(libs.google.firebase.ai)
implementation(libs.androidx.lifecycle.viewmodel.android)
implementation(libs.material3)
implementation(libs.androidx.material3)
implementation(libs.hilt.android)
ksp(libs.hilt.compiler)
implementation(libs.hilt.navigation.compose)
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.material3.android)
implementation(libs.kotlinx.serialization.json)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,11 @@ dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.appcompat)
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.material3)
implementation(libs.androidx.material.icons.extended)
implementation(platform(libs.androidx.compose.bom))
implementation(libs.hilt.android)
implementation(libs.hilt.navigation.compose)
implementation(libs.androidx.material3.android)
implementation(libs.firebase.common.ktx)
implementation(libs.androidx.lifecycle.runtime.compose)
implementation(libs.androidx.ui.tooling.preview.android)
Expand Down
2 changes: 2 additions & 0 deletions ai-catalog/samples/imagen/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ dependencies {
debugImplementation(libs.ui.tooling)
ksp(libs.hilt.compiler)

implementation(project(":ui-component"))

testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.material3.Card
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
Expand All @@ -37,24 +36,11 @@ fun GeneratedContent(uiState: ImagenUIState, modifier: Modifier = Modifier) {
) {
when (uiState) {
ImagenUIState.Initial -> {
Text(
text = stringResource(R.string.imagen_placeholder),
modifier = Modifier
.fillMaxSize()
.wrapContentSize(Alignment.Center),
textAlign = TextAlign.Center,
style = MaterialTheme.typography.bodySmall,
)
//
}

ImagenUIState.Loading -> {
Text(
text = stringResource(R.string.generating_label),
modifier = Modifier
.fillMaxSize()
.wrapContentSize(Alignment.Center),
textAlign = TextAlign.Center,
)
//
}

is ImagenUIState.ImageGenerated -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,98 +15,157 @@
*/
package com.android.ai.samples.imagen.ui

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxWidth
import android.graphics.BitmapFactory
import android.widget.Toast
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.ime
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.windowInsetsBottomHeight
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.input.rememberTextFieldState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults.topAppBarColors
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.paint
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ImageShader
import androidx.compose.ui.graphics.ShaderBrush
import androidx.compose.ui.graphics.TileMode
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewScreenSizes
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.ai.samples.imagen.R
import com.android.ai.theme.AISampleCatalogTheme
import com.android.ai.uicomponent.GenerateButton
import com.android.ai.uicomponent.SampleDetailTopAppBar
import com.android.ai.uicomponent.TextInput

@OptIn(ExperimentalMaterial3Api::class)
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun ImagenScreen(viewModel: ImagenViewModel = hiltViewModel()) {
val uiState: ImagenUIState by viewModel.uiState.collectAsStateWithLifecycle()

if (uiState is ImagenUIState.Error) {
Toast.makeText(LocalContext.current, (uiState as ImagenUIState.Error).message, Toast.LENGTH_SHORT).show()
}

ImagenScreen(
uiState = uiState,
onGenerateClick = viewModel::generateImage,
)
}

@Composable
@OptIn(ExperimentalMaterial3Api::class)
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
private fun ImagenScreen(uiState: ImagenUIState, onGenerateClick: (String) -> Unit) {
val isGenerating = uiState is ImagenUIState.Loading

Scaffold(
modifier = Modifier,
containerColor = MaterialTheme.colorScheme.surface,
topBar = {
TopAppBar(
colors = topAppBarColors(
containerColor = MaterialTheme.colorScheme.primaryContainer,
titleContentColor = MaterialTheme.colorScheme.primary,
),
title = {
Text(text = stringResource(R.string.title_image_generation_screen))
},
actions = {
SeeCodeButton()
},
SampleDetailTopAppBar(
sampleName = stringResource(R.string.title_image_generation_screen),
sampleDescription = stringResource(R.string.subtitle_image_generation_screen),
sourceCodeUrl = "https://github.com/android/ai-samples/tree/main/ai-catalog/samples/imagen",
)
},
) { innerPadding ->
Column(

val context = LocalContext.current
val imageBitmap = remember {
val bitmap = BitmapFactory.decodeResource(context.resources, com.android.ai.uicomponent.R.drawable.img_fill)
bitmap.asImageBitmap()
}

val imageShader = remember {
ImageShader(
image = imageBitmap,
tileModeX = TileMode.Repeated,
tileModeY = TileMode.Repeated,
)
}

Box(
Modifier
.verticalScroll(rememberScrollState())
.fillMaxSize()
.padding(innerPadding)
.padding(16.dp)
.padding(innerPadding),
.imePadding()
.border(
1.dp,
MaterialTheme.colorScheme.outline,
shape = RoundedCornerShape(40.dp),
)
.clip(RoundedCornerShape(40.dp))
.background(ShaderBrush(imageShader)),
) {
GeneratedContent(
uiState = uiState,
modifier = Modifier
.fillMaxWidth()
.aspectRatio(1f),
)

Spacer(modifier = Modifier.height(16.dp))
when (uiState) {
is ImagenUIState.ImageGenerated -> Image(
bitmap = uiState.bitmap.asImageBitmap(),
contentDescription = uiState.contentDescription,
contentScale = ContentScale.FillHeight,
modifier = Modifier.fillMaxSize(),
)
ImagenUIState.Loading -> {}
else -> {}
}

GenerationInput(
onGenerateClick = onGenerateClick,
enabled = !isGenerating,
modifier = Modifier.fillMaxWidth(),
)
val textFieldState = rememberTextFieldState()
val keyboardController = LocalSoftwareKeyboardController.current

Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.ime))
TextInput(
state = textFieldState,
placeholder = stringResource(R.string.placeholder_prompt),
primaryButton = {
GenerateButton(
text = "",
icon = painterResource(id = com.android.ai.uicomponent.R.drawable.ic_ai_img),
modifier = Modifier
.width(72.dp)
.height(72.dp),
enabled = !isGenerating,
onClick = {
onGenerateClick(textFieldState.text.toString())
keyboardController?.hide()
},
)
},
modifier = Modifier
.padding(10.dp)
.height(80.dp)
.align(Alignment.BottomCenter),
)
}
}
}

@Preview
@PreviewScreenSizes
@Composable
@OptIn(ExperimentalMaterial3Api::class)
private fun ImagenScreenPreview() {
ImagenScreen(
uiState = ImagenUIState.Initial,
onGenerateClick = {},
)
AISampleCatalogTheme {
ImagenScreen(
uiState = ImagenUIState.Initial,
onGenerateClick = {},
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
<string name="see_code">See Code</string>
<string name="placeholder_prompt">An oil painting of Alcatraz</string>
<string name="title_image_generation_screen">Imagen image generation</string>
<string name="subtitle_image_generation_screen">Generate images with Imagen, Google image generation model</string>
<string name="generate_button">Generate</string>
<string name="generating_label">Generating…</string>
<string name="prompt_label">Prompt</string>
Expand Down
1 change: 1 addition & 0 deletions ai-catalog/ui-component/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ android {
dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.material3)
implementation(libs.androidx.material.icons.extended)
implementation(libs.androidx.activity.compose)
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.lifecycle.runtime.compose)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import android.app.UiModeManager
import android.content.Context
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.ColorScheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.lightColorScheme
Expand Down Expand Up @@ -278,8 +277,9 @@ enum class Contrast { DEFAULT, MEDIUM, HIGH }

@Composable
private fun systemContrast(): Contrast {
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE ||
LocalInspectionMode.current) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE ||
LocalInspectionMode.current
) {
return Contrast.DEFAULT
} else {
val uiModeManager = LocalContext.current.getSystemService(Context.UI_MODE_SERVICE) as UiModeManager
Expand All @@ -299,14 +299,14 @@ fun AISampleCatalogTheme(
contrast: Contrast = systemContrast(),
content: @Composable () -> Unit,
) {
val colorScheme = if(darkTheme) {
when(contrast) {
val colorScheme = if (darkTheme) {
when (contrast) {
Contrast.DEFAULT -> darkScheme
Contrast.MEDIUM -> mediumContrastDarkColorScheme
Contrast.HIGH -> highContrastDarkColorScheme
}
} else {
when(contrast) {
when (contrast) {
Contrast.DEFAULT -> lightScheme
Contrast.MEDIUM -> mediumContrastLightColorScheme
Contrast.HIGH -> highContrastLightColorScheme
Expand Down
Loading