Pendahuluan
Generative AI adalah salah satu cabang dari artificial intelligence atau kecerdasan buatan yang dapat menghasilkan atau membuat berbagai tipe konten, mulai dari teks, gambar, audio, hingga video. Akhir-akhir ini generative AI semakin populer dengan adanya Chat GPT dan Bard yang dapat menghasilkan berbagai konten berbasis teks dan Dall-E yang menghasilkan konten berbasis gambar. Bahkan Generative AI juga bisa memeberi tahu kita cara membuat aplikasi yang akan kita segera bahas. Tentu masih banyak lagi produk generative AI lainnya di luar sana, tetapi beberapa yang sudah disebutkan adalah yang paling populer.
Namun, selain yang sudah disebutkan, ada model AI baru yang diperkenalkan oleh Google dengan kemampuan generative AI juga. Model tersebut bernama Gemini. Model Gemini yang diperkenalkan oleh Google ini memiliki tiga varian, yaitu Gemini Ultra, Gemini Pro, dan Gemini Nano.
Setiap variannya memiliki peruntukan dan kemampuannya masing-masing. Gemini Ultra adalah model dengan kemampuan paling tinggi untuk tugas yang kompleks, Gemini Pro adalah model terbaik untuk mengerjakan berbagai tugas, dan Gemini Nano adalah model paling ringan dan efisien yang bisa langsung dijalankan pada mobile device.
Nah, dalam artikel ini kita akan belajar cara membuat aplikasi content generator sederhana dengan menggunakan model Gemini Nano.
Persiapan
-
Android Studio
Untuk mengikuti proses pembuatan aplikasi content generator sederhana ini, sebenarnya bisa saja menggunakan Android Studio versi stable terbaru (Hedgehog 2023.1.1). Namun, akan lebih mudah lagi jika kita menggunakan Android studio versi canary (Jellyfish 2023.3.1). Hal ini karena pada versi tersebut sudah tersedia starter project khusus untuk menggunakan Gemini API. Untuk mengunduh Android studio versi Jellyfish, kamu bisa langsung menuju halaman Android Studio Preview Release dan pilih Canary build.
💻 Mulai Belajar Pemrograman
Belajar pemrograman di Dicoding Academy dan mulai perjalanan Anda sebagai developer profesional.
Daftar Sekarang-
Membuat Project Baru
Setelah berhasil mengunduh dan menginstal Android Studio Jellyfish, selanjutnya mari kita membuat project baru.
Buka Android Studio → Pilih New Project → Pilih Gemini API Starter dan klik Next.
Selanjutnya kita tinggal sesuaikan saja kolom yang ingin diubah. Contohnya di bawah ini. Jika sudah, klik Next.
Selanjutnya kita akan diminta Gemini API Key. Untuk mendapatkan API key, kamu bisa klik bagian Generate API Key with Google AI Studio.
Kita akan diarahkan ke halaman dengan tampilan seperti di bawah ini. Setelah itu, generate API key dengan klik Create API key in new Project atau Create API in existing project.
Setelah API key berhasil dibuat, langsung copy kemudian paste di kolom Android Studio sebelumnya.
Terakhir, klik Finish kemudian tunggu proses build project selesai.
-
Menjalankan Project Baru
Secara default, ketika kita membuat aplikasi dengan starter project Gemini API, sudah tersedia layout dan logic yang bisa digunakan untuk merangkum teks yang dimasukkan. Tanpa mengubah satu baris kode pun kita sudah bisa menggunakan kecanggihan AI dari Gemini. Berikut adalah tampilan saat pertama kali dijalankan.
Cara Membuat Aplikasi Content Generator
Starter yang dibuat secara default akan menggunakan Kotlin sebagai salah satu dari macam-macam bahasa pemrograman untuk pengembangan aplikasi mobile. Sementara itu, tampilannya sendiri sudah menggunakan UI tools yang baru untuk Android, yaitu Jetpack Compose.
Pada artikel ini, kita akan mengubah starter project yang hanya bisa merangkum teks input menjadi content generator sederhana dengan beberapa masukan, seperti topik, tone, format, dan panjang konten. Hasil dari aplikasi yang akan kita buat sebagai berikut.
-
Mengubah File SummarizeUiState.kt
Pertama-tama, hal yang kita ubah adalah SummarizeUiState menjadi ContentGeneratorUiState dan menyesuaikan isinya menjadi seperti berikut.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
sealed interface ContentGeneratorUiState { data object Initial : ContentGeneratorUiState data object Loading : ContentGeneratorUiState data class Success( val outputText: String ) : ContentGeneratorUiState data class Error( val errorMessage: String ) : ContentGeneratorUiState } |
ContentGeneratorUiState ini akan kita gunakan untuk mengelola state dari UI yang akan dibuat nanti.
-
Menambahkan File GeneraiEnum.kt
Selanjutnya adalah kita menambahkan file GeneraiEnum.kt dengan isi kode berikut.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
enum class Tone(val prompt: String) { PROFESSIONAL("professional"), CASUAL("casual"), FUNNY("funny"), INFORMATIVE("informative") } enum class Format(val prompt: String) { PARAGRAPH("paragraph"), EMAIL("email draft"), IDEAS("list of ideas"), BLOG_POST("blog post") } enum class Length(val prompt: String) { SHORT("short"), MEDIUM("medium"), LONG("long"), } |
Dalam file GeneraiEnum.kt di atas ada beberapa enum class, seperti Tone, Format, dan Length yang akan kita gunakan sebagai input untuk mengatur konten yang akan dihasilkan.
-
Mengubah File SummarizeViewModel.kt
Selanjutnya, kita mengubah SummarizeViewModel.kt menjadi ContentGeneratorViewModel.kt. Kemudian, sesuaikan isinya menjadi seperti di bawah ini.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
class ContentGeneratorViewModel( private val generativeModel: GenerativeModel ) : ViewModel() { private val _uiState: MutableStateFlow<ContentGeneratorUiState> = MutableStateFlow(ContentGeneratorUiState.Initial) val uiState: StateFlow<ContentGeneratorUiState> = _uiState.asStateFlow() fun generateContent( topic: String, tone: Tone, format: Format, length: Length, ) { _uiState.value = ContentGeneratorUiState.Loading val prompt = """ Compose a ${length.prompt} ${format.prompt} addressing $topic, employing a ${tone.prompt} style """.trimIndent() viewModelScope.launch { try { val response = generativeModel.generateContent(prompt) response.text?.let { outputContent -> _uiState.value = ContentGeneratorUiState.Success(outputContent) } } catch (e: Exception) { _uiState.value = ContentGeneratorUiState.Error(e.localizedMessage ?: "") } } } } |
Kode pada ViewModel di atas adalah salah satu bagian paling penting karena kita menggunakan kelas GenerativeModel yang disediakan oleh library Gemini. Pada ViewModel ini juga, ada fungsi generateContent() yang kita buat dengan parameter topic, tone, format, dan length. Parameter-parameter tersebut kemudian diolah menjadi sebuah prompt yang kemudian menjadi argumen untuk model Gemini.
-
Mengubah File MainActivity.kt
Terakhir, kita sesuaikan kode pada MainActivity untuk membuat tampilan seperti yang sudah disiapkan. Pada MainActivity ini, ada beberapa composable function yang akan disesuaikan sebagai berikut.
- Menyiapkan resource strings.xml.
Sebelum melakukan perubahan pada MainActivity.kt, pertama-tama kita siapkan resource strings.xml terlebih dulu agar pengelolaan teks yang kita gunakan lebih mudah dan konsisten.
1 2 3 4 5 6 7 8 9 10 |
<resources> <string name="app_name">Generai</string> <string name="topic_label">Topic</string> <string name="topic_hint">Enter your topic</string> <string name="tone_label">Tone</string> <string name="format_label">Format</string> <string name="length_label">Length</string> <string name="result_label">Result</string> <string name="generate_content_label">Generate Content</string> </resources> |
- Menambahkan composable function ContentGenerator.
Di MainActivity, pertama kita menambahkan composable function baru untuk layout form utama kita dengan kode seperti di bawah ini.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
@Composable fun ContentGenerator( modifier: Modifier = Modifier, onGenerateContentClicked: (String, Format, Tone, Length) -> Unit = { _: String, _: Format, _: Tone, _: Length -> } ) { var topic by remember { mutableStateOf("") } var selectedFormat by remember { mutableStateOf(Format.PARAGRAPH) } var selectedTone by remember { mutableStateOf(Tone.CASUAL) } var selectedLength by remember { mutableStateOf(Length.MEDIUM) } Column( modifier = modifier .fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(8.dp) ) { TextField( value = topic, maxLines = 10, label = { Text(text = stringResource(R.string.topic_label)) }, placeholder = { Text(stringResource(R.string.topic_hint)) }, onValueChange = { topic = it }, modifier = Modifier .fillMaxWidth() .height(100.dp) ) Text(text = stringResource(R.string.tone_label), style = Typography.titleMedium) Row( modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceAround ) { Tone.entries.forEach { tone -> Column(horizontalAlignment = Alignment.CenterHorizontally) { RadioButton( selected = (tone == selectedTone), onClick = { selectedTone = tone } ) Text( text = tone.name, style = MaterialTheme.typography.bodyMedium, modifier = Modifier.padding(start = 8.dp) ) } } } Text(text = stringResource(R.string.format_label), style = Typography.titleMedium) Row( modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceAround ) { Format.entries.forEach { format -> Column(horizontalAlignment = Alignment.CenterHorizontally) { RadioButton( selected = (format == selectedFormat), onClick = { selectedFormat = format } ) Text( text = format.name.replace("_", " "), style = MaterialTheme.typography.bodyMedium, modifier = Modifier.padding(start = 8.dp) ) } } } Text(text = stringResource(R.string.length_label), style = Typography.titleMedium) Row( modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceAround ) { Length.entries.forEach { length -> Column(horizontalAlignment = Alignment.CenterHorizontally) { RadioButton( selected = (length == selectedLength), onClick = { selectedLength = length } ) Text( text = length.name, style = MaterialTheme.typography.bodyMedium, modifier = Modifier.padding(start = 8.dp) ) } } } Button( onClick = { onGenerateContentClicked(topic, selectedFormat, selectedTone, selectedLength) }, modifier = Modifier .padding(all = 4.dp) .fillMaxWidth() .height(55.dp) ) { Text(stringResource(R.string.generate_content_label)) } Spacer(modifier = Modifier.height(8.dp)) Text(text = stringResource(R.string.result_label), style = Typography.titleMedium) } } |
Pada pembuatan composable function baru di atas pastikan untuk melakukan import dari resource yang diperlukan agar tidak terjadi error. Misalnya untuk komponen RadioButton kita perlu import dari material3 atau androidx.compose.material3.RadioButton.
- SummarizeScreen menjadi ContentGeneratorScreen.
Selanjutnya, kita ubah composable SummarizeScreen yang sudah ada sebelumnya menjadi ContentGeneratorScreen. Kemudian kita panggil composable ContentGenerator yang sudah dibuat sebelumnya.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
@OptIn(ExperimentalMaterial3Api::class) @Composable fun ContentGeneratorScreen( uiState: ContentGeneratorUiState = ContentGeneratorUiState.Initial, onGenerateContentClicked: (String, Format, Tone, Length) -> Unit = { _: String, _: Format, _: Tone, _: Length -> } ) { Column( modifier = Modifier .fillMaxSize() .padding(all = 8.dp) .verticalScroll(rememberScrollState()) ) { TopAppBar(title = { Text(text = "Generai") }) ContentGenerator( onGenerateContentClicked = { inputText, format, tone, length -> onGenerateContentClicked(inputText, format, tone, length) }) when (uiState) { ContentGeneratorUiState.Initial -> { // Nothing is shown } ContentGeneratorUiState.Loading -> { Box( contentAlignment = Alignment.Center, modifier = Modifier .padding(all = 8.dp) .align(Alignment.CenterHorizontally) ) { CircularProgressIndicator() } } is ContentGeneratorUiState.Success -> { Row(modifier = Modifier.padding(all = 8.dp)) { Icon( Icons.Outlined.Person, contentDescription = "Person Icon" ) Text( text = uiState.outputText, modifier = Modifier.padding(horizontal = 8.dp) ) } } is ContentGeneratorUiState.Error -> { Text( text = uiState.errorMessage, color = Color.Red, modifier = Modifier.padding(all = 8.dp) ) } } } } |
- SummarizeRoute menjadi ContentGeneratorRoute.
Setelah mengubah SummarizeScreen, lalu kita ubah SummarizeRoute menjadi ContentGeneratorRoute dengan kode seperti di bawah ini.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
@Composable internal fun ContentGeneratorRoute( contentGeneratorViewModel: ContentGeneratorViewModel = viewModel() ) { val contentGeneratorUiState by contentGeneratorViewModel.uiState.collectAsState() ContentGeneratorScreen(contentGeneratorUiState, onGenerateContentClicked = { inputText, format, tone, length -> contentGeneratorViewModel.generateContent( topic = inputText, format = format, tone = tone, length = length ) }) } |
- Mengubah class MainActivity
Terakhir, ubah isi dari class MainActivity itu sendiri. Pada bagian ini kita hanya mengubah pemanggil route dari SummarizeRoute menjadi ContentGeneratorRoute dengan parameter object dari ContentGeneratorViewModel.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { GeneraiTheme { // A surface container using the 'background' color from the theme Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background, ) { val generativeModel = GenerativeModel( modelName = "gemini-pro", apiKey = BuildConfig.apiKey ) val viewModel = ContentGeneratorViewModel(generativeModel) ContentGeneratorRoute(viewModel) } } } } } |
Menjalankan Aplikasi
Yeay, akhirnya selesai juga. Sekarang saatnya kita coba jalankan project yang kita sudah buat di atas. Jika semuanya lancar, seharusnya tampilannya akan sama persis seperti di bawah ini.
Mari kita coba aplikasi ini dengan membuat konten paragraf pendek bertopik “Gemini AI” dan memiliki tone informatif. Setelah kita klik Generate Content, hasilnya akan seperti di bawah ini.
Dengan percobaan di atas, itu sama saja dengan menjalankan Gemini AI dengan sebuah prompt “Compose a short paragraph addressing Gemini AI, employing a informative style”.
Penutup
Akhirnya, kita telah menyelesaikan langkah-langkah untuk membuat aplikasi Generative AI sederhana menggunakan model Gemini di Android Studio. Dari desain antarmuka hingga integrasi model Generative AI, kita telah memahami esensi proses pengembangan aplikasi yang menerapkan kecerdasan buatan.
Untuk pengembangan selanjutnya, kamu bisa eksplorasi konsep-konsep atau contoh-contoh yang telah disediakan oleh tim Android. Gemini sendiri tidak hanya bisa menerima input teks, tetapi bisa mengenali gambar juga. Jadi, kamu bisa coba sekreatif mungkin untuk memanfaatkan model Gemini ini.
Oh iya, jika kamu ingin melihat kodenya secara keseluruhan atau bahkan kontribusi untuk improvement, silakan cek langsung di repository GitHub.
Sekian dulu artikel dari kami, semoga bisa membantu kamu yang sedang belajar. Sampai jumpa dalam artikel selanjutnya!