Tugas PPB 10 Membuat Aplikasi Dessert Clicker

TUGAS PPB 10

Membuat Aplikasi Dessert Clicker
Nama: Fayyadh Hafizh
NRP : 5025201164
Kelas: PPB I
Link Github: Tugas 10

Halo temen-temen semuanya! Pada kesempatan kali ini, kita akan mencoba membuat sebuah aplikasi yang bernama Dessert Clicket. Aplikasi ini dibuat dengan tujuan untuk mempelajari lifecycle dari sebuah Activity. Activity sendiri merupakan proses yang dijalankan dalam bentuk aplikasi. Pada implementasinya, kita akan melihat bagaimana metode-metode dari lifecycle yang ada di dalam sebuah rangkaian proses jalannya aplikasi. Secara umum, terdapat 7 metode dari lifecycle yang ada, yaitu onCreate(), onStart(), onRestart(), onResume(), onPause(), onStop(), dan onDestroyed(). Masing-masing dari metode memiliki fungsinya sendiri. Detail dari penjelasan setiap metode akan dijelaskan dibawah ini.
  • onCreate()


    onCreate() merupakan metode paling awal dimana metode ini akan dipanggil oleh sistem ketika sebuah aplikasi dijalankan oleh pengguna. Metode onCreate() akan menjalankan aplikasi di belakang layar dan mempersiapkan tampilan beserta logic dari aplikasi yang dijalankan. 
  • onStart()
    Metode onStart() merupakan metode lanjutan dari metode onCreate() dimana setelah pemanggilan onStart(), tampilan aplikasi akan diperlihatkan kepada pengguna dan menjadi interaktif. Proses ini berlangsung dengan cepat sehingga pengguna mungkin saja tidak sempat untuk berinteraksi dengan aplikasi. Alih-alih berhenti pada lifecycle ini, pemanggilan metode onResume() dilakukan setelah metode onStart() selesai dilakukan.
  • onResume()


    Metode ini merupakan metode yang paling lama dibandingkan dengan metode lainnya karena metode ini merupakan proses yang memungkinkan pengguna untuk berinteraksi dengan aplikasi mulai dari menambahkan data, menampilkan data, dan lain-lain. Pada state ini, aplikasi akan dijadikan sebagai fokus sehingga interaksi antar aplikasi dan pengguna dapat berlangsung. Proses ini berlangsung selama tidak gangguan dari aplikasi lain atau pengguna melakukan perubahan state pada aplikasi. 
  • onPause()


    Ketika terdapat gangguan dari aplikasi lain seperti aplikasi telpon ataupun aplikasi yang dapat merubah fokus perangkat ke aplikasi lain dalam mode multi-aplikasi, maka pemanggilan metode onPause() akan dilakukan. Pada state ini, aplikasi akan masuk ke mode tidak fokus walaupun tampilan aplikasi masih berjalan.
  • onStop()
    Jika aplikasi ditutup dan berjalan pada pada background, maka metode onStop() dipanggil untuk menutup tampilan dari aplikasi tersebut. Urutan dari pemanggilan metode lifecycle ketika aplikasi dikeluarkan dimulai dari onPause() kemudian dilanjutkan dengan onStop(). Pada state ini, pengguna tidak dapat melihat tampilan maupun berinteraksi dengan aplikasi.
  • onDestroyed()


    Jika aplikasi dihapus dari background proses, maka pemanggilan metode onDestroyed() dilakukan agar aplikasi dihapus dari memori untuk mengurangi beban pada memori. Metode onDestroyed() juga dapat dipanggil ketika konfigurasi dari perangkat maupun aplikasi berubah seperti rotasi terhadap orientasi layar yang menyebabkan pemanggilan metode onPause(), onStop(), dan onDestroyed() secara berurutan diikuti oleh metode onCreate(), onStart(), dan onResume(). Jika dilihat dari pemanggilan metode onDestroyed(), maka seharusnya semua data yang ditampilkan pada aplikasi akan menghilang. Tetapi, dengan menggunakan fungsi rememberSaveable memungkinan setiap Composable atau Component menyimpan state dari setiap data yang ada agar dapat ditampilkan metode onDestroyed() dipanggilan.
  • onRestart()

    Metode onRestart() mirip dengan metode onStart() dimana aplikasi akan diperlihatkan sekaligus inisialisasi UI lainnya. Perbedaan dari keduanya adalah pemanggilan dengan melihat state sebelumnya. Jika state sebelumnya adalah ON_CREATE, maka yang dipanggil adalah metode onStart() sedangkan pemanggilan metode onRestart() dilakukan jika state sebelumnya adalah ON_STOP.
Lifecycle pada aplikasi android meliputi onCreate(), onStart(), onResume(), onPause(), onStop(), onDestroyed(), dan onRestart(). Setiap metode dipanggil sesuai dengan perubahan yang dilakukan oleh pengguna maupun perangkat sehingga manajemen terhadap state aplikasi dapat dilakukan dengan baik. Kode dan hasil implementasi aplikasi dapat dilihat dibawah ini.
  • Kode Implementasi
  • /*
    * Copyright (C) 2023 The Android Open Source Project
    *
    * Licensed under the Apache License, Version 2.0 (the "License");
    * you may not use this file except in compliance with the License.
    * You may obtain a copy of the License at
    *
    * http://www.apache.org/licenses/LICENSE-2.0
    *
    * Unless required by applicable law or agreed to in writing, software
    * distributed under the License is distributed on an "AS IS" BASIS,
    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    * See the License for the specific language governing permissions and
    * limitations under the License.
    */
    package com.example.dessertclicker
    import android.content.ActivityNotFoundException
    import android.content.Context
    import android.content.Intent
    import android.os.Bundle
    import android.util.Log
    import android.widget.Toast
    import androidx.activity.ComponentActivity
    import androidx.activity.compose.setContent
    import androidx.activity.enableEdgeToEdge
    import androidx.annotation.DrawableRes
    import androidx.compose.foundation.Image
    import androidx.compose.foundation.background
    import androidx.compose.foundation.clickable
    import androidx.compose.foundation.layout.Arrangement
    import androidx.compose.foundation.layout.Box
    import androidx.compose.foundation.layout.Column
    import androidx.compose.foundation.layout.Row
    import androidx.compose.foundation.layout.WindowInsets
    import androidx.compose.foundation.layout.asPaddingValues
    import androidx.compose.foundation.layout.calculateEndPadding
    import androidx.compose.foundation.layout.calculateStartPadding
    import androidx.compose.foundation.layout.fillMaxSize
    import androidx.compose.foundation.layout.fillMaxWidth
    import androidx.compose.foundation.layout.height
    import androidx.compose.foundation.layout.padding
    import androidx.compose.foundation.layout.safeDrawing
    import androidx.compose.foundation.layout.statusBarsPadding
    import androidx.compose.foundation.layout.width
    import androidx.compose.material.icons.Icons
    import androidx.compose.material.icons.filled.Share
    import androidx.compose.material3.Scaffold
    import androidx.compose.material3.Icon
    import androidx.compose.material3.IconButton
    import androidx.compose.material3.MaterialTheme
    import androidx.compose.material3.Surface
    import androidx.compose.material3.Text
    import androidx.compose.runtime.Composable
    import androidx.compose.runtime.getValue
    import androidx.compose.runtime.mutableStateOf
    import androidx.compose.runtime.saveable.rememberSaveable
    import androidx.compose.runtime.setValue
    import androidx.compose.ui.Alignment
    import androidx.compose.ui.Modifier
    import androidx.compose.ui.layout.ContentScale
    import androidx.compose.ui.platform.LocalContext
    import androidx.compose.ui.platform.LocalLayoutDirection
    import androidx.compose.ui.res.dimensionResource
    import androidx.compose.ui.res.painterResource
    import androidx.compose.ui.res.stringResource
    import androidx.compose.ui.text.style.TextAlign
    import androidx.compose.ui.tooling.preview.Preview
    import androidx.core.content.ContextCompat
    import com.example.dessertclicker.data.Datasource
    import com.example.dessertclicker.model.Dessert
    import com.example.dessertclicker.ui.theme.DessertClickerTheme
    // Tag for logging
    private const val TAG = "MainActivity"
    class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
    enableEdgeToEdge()
    super.onCreate(savedInstanceState)
    Log.d(TAG, "onCreate Called")
    setContent {
    DessertClickerTheme {
    // A surface container using the 'background' color from the theme
    Surface(
    modifier = Modifier
    .fillMaxSize()
    .statusBarsPadding(),
    ) {
    DessertClickerApp(desserts = Datasource.dessertList)
    }
    }
    }
    }
    override fun onStart() {
    super.onStart()
    Log.d(TAG, "onStart Called")
    }
    override fun onResume() {
    super.onResume()
    Log.d(TAG, "onResume Called")
    }
    override fun onRestart() {
    super.onRestart()
    Log.d(TAG, "onRestart Called")
    }
    override fun onPause() {
    super.onPause()
    Log.d(TAG, "onPause Called")
    }
    override fun onStop() {
    super.onStop()
    Log.d(TAG, "onStop Called")
    }
    override fun onDestroy() {
    super.onDestroy()
    Log.d(TAG, "onDestroy Called")
    }
    }
    /**
    * Determine which dessert to show.
    */
    fun determineDessertToShow(
    desserts: List<Dessert>,
    dessertsSold: Int
    ): Dessert {
    var dessertToShow = desserts.first()
    for (dessert in desserts) {
    if (dessertsSold >= dessert.startProductionAmount) {
    dessertToShow = dessert
    } else {
    // The list of desserts is sorted by startProductionAmount. As you sell more desserts,
    // you'll start producing more expensive desserts as determined by startProductionAmount
    // We know to break as soon as we see a dessert who's "startProductionAmount" is greater
    // than the amount sold.
    break
    }
    }
    return dessertToShow
    }
    /**
    * Share desserts sold information using ACTION_SEND intent
    */
    private fun shareSoldDessertsInformation(intentContext: Context, dessertsSold: Int, revenue: Int) {
    val sendIntent = Intent().apply {
    action = Intent.ACTION_SEND
    putExtra(
    Intent.EXTRA_TEXT,
    intentContext.getString(R.string.share_text, dessertsSold, revenue)
    )
    type = "text/plain"
    }
    val shareIntent = Intent.createChooser(sendIntent, null)
    try {
    ContextCompat.startActivity(intentContext, shareIntent, null)
    } catch (e: ActivityNotFoundException) {
    Toast.makeText(
    intentContext,
    intentContext.getString(R.string.sharing_not_available),
    Toast.LENGTH_LONG
    ).show()
    }
    }
    @Composable
    private fun DessertClickerApp(
    desserts: List<Dessert>
    ) {
    var revenue by rememberSaveable { mutableStateOf(0) }
    var dessertsSold by rememberSaveable { mutableStateOf(0) }
    val currentDessertIndex by rememberSaveable { mutableStateOf(0) }
    var currentDessertPrice by rememberSaveable {
    mutableStateOf(desserts[currentDessertIndex].price)
    }
    var currentDessertImageId by rememberSaveable {
    mutableStateOf(desserts[currentDessertIndex].imageId)
    }
    Scaffold(
    topBar = {
    val intentContext = LocalContext.current
    val layoutDirection = LocalLayoutDirection.current
    DessertClickerAppBar(
    onShareButtonClicked = {
    shareSoldDessertsInformation(
    intentContext = intentContext,
    dessertsSold = dessertsSold,
    revenue = revenue
    )
    },
    modifier = Modifier
    .fillMaxWidth()
    .padding(
    start = WindowInsets.safeDrawing.asPaddingValues()
    .calculateStartPadding(layoutDirection),
    end = WindowInsets.safeDrawing.asPaddingValues()
    .calculateEndPadding(layoutDirection),
    )
    .background(MaterialTheme.colorScheme.primary)
    )
    }
    ) { contentPadding ->
    DessertClickerScreen(
    revenue = revenue,
    dessertsSold = dessertsSold,
    dessertImageId = currentDessertImageId,
    onDessertClicked = {
    // Update the revenue
    revenue += currentDessertPrice
    dessertsSold++
    // Show the next dessert
    val dessertToShow = determineDessertToShow(desserts, dessertsSold)
    currentDessertImageId = dessertToShow.imageId
    currentDessertPrice = dessertToShow.price
    },
    modifier = Modifier.padding(contentPadding)
    )
    }
    }
    @Composable
    private fun DessertClickerAppBar(
    onShareButtonClicked: () -> Unit,
    modifier: Modifier = Modifier
    ) {
    Row(
    modifier = modifier,
    horizontalArrangement = Arrangement.SpaceBetween,
    verticalAlignment = Alignment.CenterVertically,
    ) {
    Text(
    text = stringResource(R.string.app_name),
    modifier = Modifier.padding(start = dimensionResource(R.dimen.padding_medium)),
    color = MaterialTheme.colorScheme.onPrimary,
    style = MaterialTheme.typography.titleLarge,
    )
    IconButton(
    onClick = onShareButtonClicked,
    modifier = Modifier.padding(end = dimensionResource(R.dimen.padding_medium)),
    ) {
    Icon(
    imageVector = Icons.Filled.Share,
    contentDescription = stringResource(R.string.share),
    tint = MaterialTheme.colorScheme.onPrimary
    )
    }
    }
    }
    @Composable
    fun DessertClickerScreen(
    revenue: Int,
    dessertsSold: Int,
    @DrawableRes dessertImageId: Int,
    onDessertClicked: () -> Unit,
    modifier: Modifier = Modifier
    ) {
    Box(modifier = modifier) {
    Image(
    painter = painterResource(R.drawable.bakery_back),
    contentDescription = null,
    contentScale = ContentScale.Crop
    )
    Column {
    Box(
    modifier = Modifier
    .weight(1f)
    .fillMaxWidth(),
    ) {
    Image(
    painter = painterResource(dessertImageId),
    contentDescription = null,
    modifier = Modifier
    .width(dimensionResource(R.dimen.image_size))
    .height(dimensionResource(R.dimen.image_size))
    .align(Alignment.Center)
    .clickable { onDessertClicked() },
    contentScale = ContentScale.Crop,
    )
    }
    TransactionInfo(
    revenue = revenue,
    dessertsSold = dessertsSold,
    modifier = Modifier.background(MaterialTheme.colorScheme.secondaryContainer)
    )
    }
    }
    }
    @Composable
    private fun TransactionInfo(
    revenue: Int,
    dessertsSold: Int,
    modifier: Modifier = Modifier
    ) {
    Column(modifier = modifier) {
    DessertsSoldInfo(
    dessertsSold = dessertsSold,
    modifier = Modifier
    .fillMaxWidth()
    .padding(dimensionResource(R.dimen.padding_medium))
    )
    RevenueInfo(
    revenue = revenue,
    modifier = Modifier
    .fillMaxWidth()
    .padding(dimensionResource(R.dimen.padding_medium))
    )
    }
    }
    @Composable
    private fun RevenueInfo(revenue: Int, modifier: Modifier = Modifier) {
    Row(
    modifier = modifier,
    horizontalArrangement = Arrangement.SpaceBetween,
    ) {
    Text(
    text = stringResource(R.string.total_revenue),
    style = MaterialTheme.typography.headlineMedium,
    color = MaterialTheme.colorScheme.onSecondaryContainer
    )
    Text(
    text = "$${revenue}",
    textAlign = TextAlign.Right,
    style = MaterialTheme.typography.headlineMedium,
    color = MaterialTheme.colorScheme.onSecondaryContainer
    )
    }
    }
    @Composable
    private fun DessertsSoldInfo(dessertsSold: Int, modifier: Modifier = Modifier) {
    Row(
    modifier = modifier,
    horizontalArrangement = Arrangement.SpaceBetween,
    ) {
    Text(
    text = stringResource(R.string.dessert_sold),
    style = MaterialTheme.typography.titleLarge,
    color = MaterialTheme.colorScheme.onSecondaryContainer
    )
    Text(
    text = dessertsSold.toString(),
    style = MaterialTheme.typography.titleLarge,
    color = MaterialTheme.colorScheme.onSecondaryContainer
    )
    }
    }
    @Preview
    @Composable
    fun MyDessertClickerAppPreview() {
    DessertClickerTheme {
    DessertClickerApp(listOf(Dessert(R.drawable.cupcake, 5, 0)))
    }
    }
    view raw MainActivity.kt hosted with ❤ by GitHub

  • Hasil Implementasi


Komentar

Postingan populer dari blog ini

Tugas PPB 12 Membuat Aplikasi Flutter Namer App