Introducción
Gemini, el último modelo de lenguaje de Google, ha revolucionado el mundo de la inteligencia artificial. Su integración en aplicaciones Android puede abrir un abanico de posibilidades, desde chatbots inteligentes hasta asistentes virtuales personalizados. En este tutorial, te guiaremos a través del proceso de integración de Gemini en tu proyecto Android Studio, utilizando Jetpack Compose y basándonos en las mejores prácticas implementadas en el template de Koala.
¿Por qué usar Gemini y Jetpack Compose?
Gemini: Ofrece capacidades avanzadas de generación de texto, traducción de idiomas, escritura de diferentes tipos de contenido creativo y más.
Jetpack Compose: Es el toolkit de UI moderno de Android que simplifica la creación de interfaces de usuario nativas, declarativas y con menos código.
Koala: Un template de proyecto Android que proporciona una base sólida para el desarrollo de aplicaciones modernas, incluyendo la integración de IA.
Preparación del Entorno
-
Instalar Android Studio: Asegúrate de tener instalada la última versión de Android Studio que soporte Jetpack Compose.
Crear un nuevo proyecto: Inicia un nuevo proyecto de Android utilizando el template de Koala (si está disponible) o configura un proyecto existente.
-
Agregar las dependencias necesarias:
- Gemini SDK: Consulta la documentación oficial de Gemini para obtener las instrucciones específicas sobre cómo agregar la dependencia del SDK a tu proyecto.
- Dependencias de Jetpack Compose: Asegúrate de tener las últimas versiones de las dependencias de Jetpack Compose en tu archivo build.gradle.
- Creación de la Interfaz de Usuario
- Diseñar la pantalla: Utiliza Jetpack Compose para crear una interfaz de usuario intuitiva que permita al usuario interactuar con Gemini. Esto puede incluir un campo de texto para las entradas del usuario y una zona para mostrar las respuestas de Gemini.
- Implementar la lógica: Crea una función para enviar las consultas del usuario al modelo de Gemini. Utiliza las respuestas de Gemini para actualizar la interfaz de usuario. Considera implementar un historial de conversaciones para que el usuario pueda revisar interacciones anteriores.
- Inicializar el cliente de Gemini: Sigue las instrucciones de la documentación oficial de Gemini para inicializar el cliente en tu aplicación.
- Hacer solicitudes a la API: Utiliza el cliente para enviar las consultas del usuario al modelo de Gemini y recibir las respuestas.
- Manejar las respuestas: Procesa las respuestas de Gemini y actualiza la interfaz de usuario en consecuencia.
Integración con Gemini
Dependencias
// En Gradle Scripts haga esto.
//local.properties(SDK Location)
#Copie Aqui su Api Key de Geminis
apikey = AIza_yaWZhYO_KcTgsLQFu98
En el archivo libs.version.toml
Haga adiciona lo siguient
[versions]
...
...
...
...
lifecycleViewmodelCompose = "2.8.4"
generativeai = "0.9.0"
googleAndroidLibrariesMapsplatformSecretsGradlePlugin = "2.0.1"
[libraries]
...
...
...
...
androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycleViewmodelCompose" }
generativeai = { group = "com.google.ai.client.generativeai", name = "generativeai", version.ref = "generativeai" }
[plugins]
...
...
google-android-libraries-mapsplatform-secrets-gradle-plugin = { id = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin", version.ref = "googleAndroidLibrariesMapsplatformSecretsGradlePlugin" }
Build.gradle.kts (Project:NameProje)
En este archivo incluya lo siente>
alias(libs.plugins.google.android.libraries.mapsplatform.secrets.gradle.plugin) apply false
Build.gradle.kts (Module :app)
En este archivo inclusa lo siente
plugins {
...
...
alias(libs.plugins.google.android.libraries.mapsplatform.secrets.gradle.plugin)
}
android {
...
...
...
buildFeatures {
compose = true
buildConfig = true
}
}
dependencies {
...
...
...
implementation(libs.androidx.lifecycle.viewmodel.compose)
implementation(libs.generativeai)
}
Sincronice al proyecto
Ejemplo de Código en Main Activity
class MainActivity : ComponentActivity() {
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
MyDrinksTheme {
Scaffold() {
DrinkScreen()
}
}
}
}
}
Create a file DrinkScreen and fill with this content
val images = arrayOf(
// Image generated using Gemini from the prompt "cupcake image"
R.drawable.ron,
// Image generated using Gemini from the prompt "cookies images"
R.drawable.wine,
// Image generated using Gemini from the prompt "cake images"
R.drawable.beer,
)
val imageDescriptions = arrayOf(
R.string.run,
R.string.wine,
R.string.beer,
)
@Composable
fun DrinkScreen(
drinkingViewModel: DrinkViewModel = viewModel()
) {
val selectedImage = remember { mutableIntStateOf(0) }
val placeholderPrompt = stringResource(R.string.prompt_placeholder)
val placeholderResult = stringResource(R.string.results_placeholder)
var prompt by rememberSaveable { mutableStateOf(placeholderPrompt) }
var result by rememberSaveable { mutableStateOf(placeholderResult) }
val uiState by drinkingViewModel.uiState.collectAsState()
val context = LocalContext.current
Column(
modifier = Modifier.fillMaxSize()
) {
Text(
text = stringResource(R.string.drink_title),
style = MaterialTheme.typography.titleLarge,
modifier = Modifier.padding(16.dp)
)
LazyRow(
modifier = Modifier.fillMaxWidth()
) {
itemsIndexed(images) { index, image ->
var imageModifier = Modifier
.padding(start = 8.dp, end = 8.dp)
.requiredSize(200.dp)
.clickable {
selectedImage.intValue = index
}
if (index == selectedImage.intValue) {
imageModifier =
imageModifier.border(BorderStroke(4.dp, MaterialTheme.colorScheme.primary))
}
Image(
painter = painterResource(image),
contentDescription = stringResource(imageDescriptions[index]),
modifier = imageModifier
)
}
}
Row(
modifier = Modifier.padding(all = 16.dp)
) {
TextField(
value = prompt,
label = { Text(stringResource(R.string.label_prompt)) },
onValueChange = { prompt = it },
modifier = Modifier
.weight(0.8f)
.padding(end = 16.dp)
.align(Alignment.CenterVertically)
)
Button(
onClick = {
val bitmap = BitmapFactory.decodeResource(
context.resources,
images[selectedImage.intValue]
)
drinkingViewModel.sendPrompt(bitmap, prompt)
},
enabled = prompt.isNotEmpty(),
modifier = Modifier
.align(Alignment.CenterVertically)
) {
Text(text = stringResource(R.string.action_go))
}
}
if (uiState is UiState.Loading) {
CircularProgressIndicator(modifier = Modifier.align(Alignment.CenterHorizontally))
} else {
var textColor = MaterialTheme.colorScheme.onSurface
if (uiState is UiState.Error) {
textColor = MaterialTheme.colorScheme.error
result = (uiState as UiState.Error).errorMessage
} else if (uiState is UiState.Success) {
textColor = MaterialTheme.colorScheme.onSurface
result = (uiState as UiState.Success).outputText
}
val scrollState = rememberScrollState()
Text(
text = result,
textAlign = TextAlign.Start,
color = textColor,
modifier = Modifier
.align(Alignment.CenterHorizontally)
.padding(16.dp)
.fillMaxSize()
.verticalScroll(scrollState)
)
}
}
}
Create a file name DrinkViewModel a fill with this content
class DrinkViewModel : ViewModel() {
private val _uiState: MutableStateFlow =
MutableStateFlow(UiState.Initial)
val uiState: StateFlow =
_uiState.asStateFlow()
private val generativeModel = GenerativeModel(
modelName = "gemini-1.5-flash",
apiKey = BuildConfig.apikey
)
fun sendPrompt(
bitmap: Bitmap,
prompt: String
) {
_uiState.value = UiState.Loading
viewModelScope.launch(Dispatchers.IO) {
try {
val response = generativeModel.generateContent(
content {
image(bitmap)
text(prompt)
}
)
response.text?.let { outputContent ->
_uiState.value = UiState.Success(outputContent)
}
} catch (e: Exception) {
_uiState.value = UiState.Error(e.localizedMessage ?: "")
}
}
}
}
Create a interface name UiState a fill with this content
/**
* A sealed hierarchy describing the state of the text generation.
*/
sealed interface UiState {
/**
* Empty state when the screen is first shown
*/
object Initial : UiState
/**
* Still loading
*/
object Loading : UiState
/**
* Text has been generated
*/
data class Success(val outputText: String) : UiState
/**
* There was an error generating text
*/
data class Error(val errorMessage: String) : UiState
}
Update the Strings value in res with this content
MyDrinks
Prompt
Go
Drinking with Gemini
Provide a recipe for the bottle in the image
(Results will appear here)
Run
Wine
Beer
In res/drawable package copy 3 images: ron.png, wine.pne, beer.png
Comments