0
0
0
share
#Android#thread#kotlin#Coroutines
0 Komentar
Belajar Membuat Aplikasi Jadwal Shalat Android dengan Kotlin Coroutines

Pendahuluan
Di bahasa pemrograman Kotlin, ada salah satu fitur yang menurut penulis cukup bagus yang bernama Kotlin Coroutines. Apa itu Kotlin Coroutines? Berdasarkan situs https://blog.mindorks.com/what-are-coroutines-in-kotlin-bf4fecd476e9 menyebutkan bahwa
Coroutines are a new way of writing asyncrhonous, non-blocking code (and much more)
Jadi, menurut penulis Kotlin Coroutines adalah salah satu metode untuk melakukan operasi asynchronous , namun dengan style atau gaya penulisan kode yang berbentuk synchronous atau dengan kata lain, Kotlin Coroutines lebih mengarah ke multi-thread.
Launch dan Async
Dalam tutorial ini, ada 2 keyword yang akan kita pelajari yaitu, launch dan async. Perbedaannya adalah launch tidak mengembalikan nilai apapun dan async mengembalikan nilai yang bertipe objek Deferred<T>
Mulai Pembuatan Contoh Projek
Jadi, pada tutorial ini saya akan memberikan contoh penggunaan Kotlin Coroutines untuk melakukan pemanggilan request ke API secara asynchronous
Buat Projek Baru
Buka aplikasi Android Studio dan buat projek baru dengan nama projeknya Sholat Yuk dan set min sdk version-nya ke 18. Pada saat pembuatan Activity, ubah nama Activity-nya menjadi SplashScreenActivity dan nama layout-nya menjadi activity_splash_screen
API Key
Pada projek ini, kita akan menggunakan data dari API http://wahidganteng.ga/api/jadwal-sholat. Jadi, silakan lakukan pendaftaran di situs tersebut untuk mendapatkan API Key-nya.

Setup build.gradle
Buka file build.gradle pada level module app dan ubah menjadi seperti berikut.
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 26
defaultConfig {
applicationId "com.codepolitan.sholatyuk"
minSdkVersion 18
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
flavorDimensions "development"
productFlavors {
development {
applicationIdSuffix ".development"
buildConfigField("String", "API_KEY", "\"Your API Key\"")
buildConfigField("String", "BASE_URL", "\"http://wahidganteng.ga/process/api/\"")
dimension "development"
}
}
}
kotlin {
experimental {
coroutines "enable"
}
}
dependencies {
/** Library from directory libs */
implementation fileTree(include: ['*.jar'], dir: 'libs')
/** Library from Standard Android */
implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
implementation 'com.android.support:design:26.1.0'
/** Library support for Kotlin Coroutines */
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.19.3'
/** An HTTP+HTTP/2 client for Android and Java applications */
implementation 'com.squareup.okhttp3:okhttp:3.9.0'
/** A Java serialization and deserialization library to convert Objects into JSON */
implementation 'com.google.code.gson:gson:2.8.2'
/** Testing Framework */
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}
Pada file tersebut, kita ada menambahkan API Key dan Base Url dari API Server yang akan kita pakai. Sengaja kita buat API Key dan Base Url-nya di file build.gradle agar kita lebih mudah melakukan pemanggilannya. Kemudian, pada file build.gradle kita juga aktifkan fitur Kotlin Coroutines yang ditandai dengan kode berikut
kotlin {
experimental {
coroutines "enable"
}
}
dan tambahkan dependency si Kotlin Coroutines-nya
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.19.3'
Selain itu, kita juga ada menggunakan beberapa fitur lainnya seperti Android Kotlin Extensions dan beberapa dependency lainnya yang kita butuhkan pada pembuatan projek kali ini.
Setup colors.xml
Buka file colors.xml dan ubah menjadi seperti berikut
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#512da8</color>
<color name="colorPrimaryDark">#140078</color>
<color name="colorPrimaryLight">#8559da</color>
<color name="colorSecondary">#ea80fc</color>
<color name="colorSecondaryDark">#b64fc8</color>
<color name="colorSecondaryLight">#ffb2ff</color>
<color name="colorAccent">#ea80fc</color>
</resources>
Setup strings.xml
Buka file strings.xml dan ubah menjadi seperti berikut
<resources>
<string name="app_name">Sholat Yuk</string>
<string name="image_view_background_splash_screen">image view background splash screen</string>
<string name="shalat">Shalat</string>
<string name="yuk">Yuk</string>
<string name="image_view_location_city">image view location city</string>
<string name="location_city">Location City</string>
<string name="medan">Medan</string>
<string name="submit">Submit</string>
<string name="choose_location">Choose location</string>
<string name="please_wait">Please wait</string>
<string name="indonesia">Indonesia</string>
<string name="mon_august_29_2015">Mon, August 29, 2015</string>
<string name="please_choose_location">Please, choose location</string>
<string name="shubuh">Shubuh</string>
<string name="esimated_time_5_hours">Esimated time: 5 hours</string>
<string name="_00">:00</string>
<string name="_2">2</string>
<string name="image_view_icon_circle">image view icon circle</string>
<string name="am">am</string>
<string name="pm">pm</string>
</resources>
Buat file class Android.kt
Silakan buat package baru dengan nama experimental dan buat file baru dengan nama Android.kt pada package tersebut. Dan isi dengan source code berikut.
package com.codepolitan.sholatyuk.experimental
import android.os.Handler
import android.os.Looper
import kotlin.coroutines.experimental.AbstractCoroutineContextElement
import kotlin.coroutines.experimental.Continuation
import kotlin.coroutines.experimental.ContinuationInterceptor
/**
* Created by yudisetiawan on 12/23/17.
*/
private class AndroidContinuation<T>(val cont: Continuation<T>) : Continuation<T> by cont {
override fun resume(value: T) {
if (Looper.myLooper() == Looper.getMainLooper()) {
cont.resume(value)
} else {
Handler(Looper.getMainLooper()).post {
cont.resume(value)
}
}
}
override fun resumeWithException(exception: Throwable) {
if (Looper.myLooper() == Looper.getMainLooper()) {
cont.resumeWithException(exception)
} else {
Handler(Looper.getMainLooper()).post {
cont.resumeWithException(exception)
}
}
}
}
object Android: AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor {
override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> =
AndroidContinuation(continuation)
}
Jadi, kegunaan file ini lebih mengarah ke arah penggunaan Thread. Terlihat dari kode Looper.myLooper()
dimana, jika di UI Thread maka, akan diarahkan ke UI Thread dan jika tidak maka akan di arahkan ke Background Thread.
Buat file object ShalatClient.kt
Buat package baru dengan nama network dan didalamnya buat lagi package baru dengan nama api. Dan didalamnya, buat file baru dengan nama ShalatClient. Selanjutnya, isi dengan source code berikut.
package com.codepolitan.sholatyuk.network.api
import com.codepolitan.sholatyuk.BuildConfig
import com.codepolitan.sholatyuk.model.DataJadwalSholat
import com.codepolitan.sholatyuk.model.DataKota
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.Deferred
import kotlinx.coroutines.experimental.async
import okhttp3.OkHttpClient
import okhttp3.Request
import java.text.SimpleDateFormat
import java.util.*
/**
* Created by yudisetiawan on 12/23/17.
*/
object ShalatClient {
private val TAG = javaClass.simpleName
val okhttpClient = OkHttpClient()
/**
* @description Get City Data from API
* @return {Deffered<DataKota>} Return value DataKota from API
*/
fun getCityData(): Deferred<DataKota> {
return async(CommonPool) {
val request = Request.Builder()
.url(BuildConfig.BASE_URL + BuildConfig.API_KEY + "/jadwal-sholat/get-kota")
.build()
val response = okhttpClient.newCall(request).execute()
val dataKota = object : TypeToken<DataKota>() {}.type
Gson().fromJson<DataKota>(response.body()!!.string(), dataKota)
}
}
/**
* @description Get Prayer Schedule Data from API
* @param {Int} id Unique id for each city
* @return {Deferred<DataJadwalSholat>} Return value DataJadwalSholat from API
*/
fun getPrayerScheduleData(id: Int): Deferred<DataJadwalSholat> {
return async(CommonPool) {
var url = BuildConfig.BASE_URL + BuildConfig.API_KEY + "/jadwal-sholat"
val strDate = SimpleDateFormat("yyyy-MM-dd", Locale.US).format(Date())
url += "?idk=$id"
url += url.let {
val strMonth = strDate.substring(5, 5 + 2)
val strYear = strDate.substring(0, 0 + 4)
"&bln=$strMonth&thn=$strYear"
}
val request = Request.Builder()
.url(url)
.build()
val response = okhttpClient.newCall(request).execute()
val dataPrayerSchedule = object : TypeToken<DataJadwalSholat>() {}.type
Gson().fromJson<DataJadwalSholat>(response.body()!!.string(), dataPrayerSchedule)
}
}
}
Pada file tersebut, kita buat 2 buat function yang bernama getCityData untuk mengambil data list kota dari API dan satu lagi function bernama getPrayerScheduleData untuk mengambil data list jadwal sholat dari API. Bisa Anda lihat pada kode diatas, kita menggunakan async
karena, kita ingin mengembalikan nilai atau data dari respon si OkHttp di dalam execute block async
Buat file data class DataKota.kt
Buat package baru dengan nama model dan buat file baru dengan nama DataKota.kt dan isi dengan source code berikut.
package com.codepolitan.sholatyuk.model
import android.os.Parcel
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
/**
* Created by yudisetiawan on 12/23/17.
*/
data class DataKota(
@SerializedName("status") val status: String,
@SerializedName("msg") val msg: String,
@SerializedName("count") val count: Int,
@SerializedName("data") val data: List<Data>
)
data class Data(
@SerializedName("id") val id: String,
@SerializedName("nama_kota") val namaKota: String
) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readString(),
parcel.readString())
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(id)
parcel.writeString(namaKota)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<Data> {
override fun createFromParcel(parcel: Parcel): Data {
return Data(parcel)
}
override fun newArray(size: Int): Array<Data?> {
return arrayOfNulls(size)
}
}
}
Buat file data class DataJadwalSholat.kt
Buat file baru dengan nama DataJadwalSholat.kt pada package model dan isi dengan source code berikut.
package com.codepolitan.sholatyuk.model
import android.os.Parcel
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
/**
* Created by yudisetiawan on 12/24/17.
*/
data class DataJadwalSholat(
@SerializedName("status") val status: String,
@SerializedName("msg") val msg: String,
@SerializedName("count") val count: Int,
@SerializedName("data") val data: List<DataSholat>
) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readString(),
parcel.readString(),
parcel.readInt(),
parcel.createTypedArrayList(DataSholat))
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(status)
parcel.writeString(msg)
parcel.writeInt(count)
parcel.writeTypedList(data)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<DataJadwalSholat> {
override fun createFromParcel(parcel: Parcel): DataJadwalSholat {
return DataJadwalSholat(parcel)
}
override fun newArray(size: Int): Array<DataJadwalSholat?> {
return arrayOfNulls(size)
}
}
}
data class DataSholat(
@SerializedName("tanggal") val tanggal: String,
@SerializedName("shubuh") val shubuh: String,
@SerializedName("dzuhur") val dzuhur: String,
@SerializedName("ashr") val ashr: String,
@SerializedName("maghrib") val maghrib: String,
@SerializedName("isya") val isya: String
) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readString(),
parcel.readString(),
parcel.readString(),
parcel.readString(),
parcel.readString(),
parcel.readString())
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(tanggal)
parcel.writeString(shubuh)
parcel.writeString(dzuhur)
parcel.writeString(ashr)
parcel.writeString(maghrib)
parcel.writeString(isya)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<DataSholat> {
override fun createFromParcel(parcel: Parcel): DataSholat {
return DataSholat(parcel)
}
override fun newArray(size: Int): Array<DataSholat?> {
return arrayOfNulls(size)
}
}
}
Catatan: File data class hanya berfungsi sebagai class model atau POJO kalau di Java.
Persiapan file drawable
Untuk semua file drawable pada projek kali ini bisa Anda unduh di sini
Buat file DatabaseHelper
Buat package baru dengan nama db dan buat file class baru didalamnya dengan nama DatabaseHelper.kt dan isi dengan source code berikut.
package com.codepolitan.sholatyuk.db
import android.content.ContentValues
import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
import com.codepolitan.sholatyuk.model.Data
import java.sql.SQLException
/**
* Created by yudisetiawan on 12/23/17.
*/
class DatabaseHelper(context: Context?, name: String?, factory: SQLiteDatabase.CursorFactory?, version: Int) : SQLiteOpenHelper(context, name, factory, version) {
private val TAG = javaClass.simpleName
companion object {
val DATABASE_NAME = "shalatyuk.db"
val DATABASE_VERSION = 1
}
private val CITY_TABLE_NAME = "kota"
private val CITY_TABLE_NAME_COLUMN_ID = "id"
private val CITY_TABLE_NAME_COLUMN_CITY_NAME = "name"
private val CITY_TABLE_SELECT = "select * from $CITY_TABLE_NAME"
private val CITY_TABLE_CREATE = "create table if not exists $CITY_TABLE_NAME " +
"($CITY_TABLE_NAME_COLUMN_ID integer primary key," +
"$CITY_TABLE_NAME_COLUMN_CITY_NAME text " +
")"
private val CITY_TABLE_DROP = "drop table if exists $CITY_TABLE_NAME"
/**
* @description For create database
* @param sqliteDatabase {SQLiteDatabase} object SQLiteDatabase
*/
override fun onCreate(sqliteDatabase: SQLiteDatabase?) {
try {
sqliteDatabase?.execSQL(CITY_TABLE_CREATE)
} catch (sqle: SQLException) {
sqle.printStackTrace()
}
}
/**
* @description For update database
* @param sqliteDatabase {SQLiteDatabase} object SQLiteDatabase
* @param oldVersion {Int} old version SQLitedatabase
* @param newVersion {Int} new version SQLiteDatabase
*/
override fun onUpgrade(sqliteDatabase: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
try {
sqliteDatabase?.execSQL(CITY_TABLE_DROP)
sqliteDatabase?.execSQL(CITY_TABLE_CREATE)
} catch (sqle: SQLException) {
sqle.printStackTrace()
}
}
/**
* @description Insert data city to city_table
* @param data {Data} value of Data city
* @return {Long} return value row ID if new inserted or -1 if an error occurred
*/
fun insertDataCity(data: Data): Long {
try {
val sqliteDatabase = writableDatabase
val contentValue = ContentValues()
contentValue.put(CITY_TABLE_NAME_COLUMN_ID, data.id)
contentValue.put(CITY_TABLE_NAME_COLUMN_CITY_NAME, data.namaKota)
return sqliteDatabase.insert(
CITY_TABLE_NAME,
null,
contentValue
)
} catch (e: Exception) {
throw e
}
}
/**
* @description Insert list data city to city_table
* @param listData {List<Data>} values of Data city
* @return {Int} return value 1 if success or something else
*/
fun insertDataCity(listData: List<Data>): Int {
try {
val sqliteDatabase = writableDatabase
val queryInsert = "insert into $CITY_TABLE_NAME " +
"($CITY_TABLE_NAME_COLUMN_ID, $CITY_TABLE_NAME_COLUMN_CITY_NAME) " +
"values " +
"(?, ?)"
sqliteDatabase.beginTransaction()
val sqliteStatement = sqliteDatabase.compileStatement(queryInsert)
for (data in listData) {
sqliteStatement.bindString(1, data.id)
sqliteStatement.bindString(2, data.namaKota)
sqliteStatement.execute()
sqliteStatement.clearBindings()
}
sqliteDatabase.setTransactionSuccessful()
sqliteDatabase.endTransaction()
return 1
} catch (e: Exception) {
throw e
}
}
/**
* @description Delete data city in city_table by id
* @param id {Int} ID of city
* @return {Int} the number of row
*/
fun deleteDataCityById(id: Int): Int {
try {
val sqliteDatabase = writableDatabase
return sqliteDatabase.delete(
CITY_TABLE_NAME,
"$CITY_TABLE_NAME_COLUMN_ID = ?",
Array(1, { id.toString() })
)
} catch (e: Exception) {
throw e
}
}
/**
* @description Delete all data city in city_table
* @return {Int} return the number of row affected
*/
fun deleteDataCity(): Int {
try {
val sqliteDatabase = writableDatabase
return sqliteDatabase.delete(
CITY_TABLE_NAME,
null,
null
)
} catch (e: Exception) {
throw e
}
}
/**
* @description count item data city in city_table
* @return {Int} return count value item data city in city_table
*/
fun countDataCity(): Int {
val itemCount: Int
try {
val sqliteDatabase = writableDatabase
val cursor = sqliteDatabase.rawQuery(
CITY_TABLE_SELECT,
null,
null
)
itemCount = cursor.count
cursor.close()
} catch (e: Exception) {
e.printStackTrace()
throw e
}
return itemCount
}
/**
* @description Get data city in city_table
* @return {List<Data>} return list data city from city_table
*/
fun getDataCity(): List<Data> {
val listDataCity = ArrayList<Data>()
try {
val sqliteDatabase = writableDatabase
val cursor = sqliteDatabase.rawQuery(
CITY_TABLE_SELECT,
null,
null
)
if (cursor.count > 0) {
while (cursor.moveToNext()) {
val dataKota = Data(
id = cursor.getInt(cursor.getColumnIndex(CITY_TABLE_NAME_COLUMN_ID)).toString(),
namaKota = cursor.getString(cursor.getColumnIndex(CITY_TABLE_NAME_COLUMN_CITY_NAME))
)
listDataCity.add(dataKota)
}
}
cursor.close()
} catch (e: Exception) {
e.printStackTrace()
}
return listDataCity
}
}
Pada file DatabaseHelper.kt kita ada membuat beberapa function database seperti, memasukkan data ke database, menghitung jumlah data di database, dan menghapus data di database.
Ubah layout activity_splash_screen.xml
Buka file activity_splash_screen.xml dan isi dengan source code berikut.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.codepolitan.sholatyuk.ui.splashscreen.SplashScreenActivity">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="@string/image_view_background_splash_screen"
android:scaleType="centerCrop"
android:src="@drawable/img_backgound_splash_screen" />
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0.7"
android:background="#000000" />
<ProgressBar
android:id="@+id/progress_bar_loading_activity_splash_screen"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/progress_bar_loading_activity_splash_screen"
android:layout_marginBottom="8dp"
android:gravity="center_vertical"
>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:fontFamily="sans-serif-condensed"
android:gravity="end"
android:text="@string/shalat"
android:textColor="@android:color/white"
android:textSize="28sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:fontFamily="sans-serif-condensed"
android:gravity="start"
android:text="@string/yuk"
android:textColor="@color/colorAccent"
android:textSize="18sp" />
</LinearLayout>
</RelativeLayout>
Ubah file SplashScreenActivity
Buka file SplashScreenActivity.kt dan isi dengan source code berikut.
package com.codepolitan.sholatyuk.ui.splashscreen
import android.content.Intent
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import com.codepolitan.sholatyuk.R
import com.codepolitan.sholatyuk.db.DatabaseHelper
import com.codepolitan.sholatyuk.experimental.Android
import com.codepolitan.sholatyuk.network.api.ShalatClient
import com.codepolitan.sholatyuk.ui.home.HomeActivity
import kotlinx.coroutines.experimental.launch
class SplashScreenActivity : AppCompatActivity() {
private val TAG = javaClass.simpleName
private val databaseHelper by lazy {
DatabaseHelper(
context = this@SplashScreenActivity,
name = DatabaseHelper.DATABASE_NAME,
factory = null,
version = DatabaseHelper.DATABASE_VERSION
)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_splash_screen)
doLoadData()
}
/**
* @description Load data from API and save it to local database
*/
private fun doLoadData() {
launch(Android) {
val itemCountDataCityLocal = databaseHelper.countDataCity()
val resultDataKota = ShalatClient.getCityData().await()
val intentHomeActivity = Intent(this@SplashScreenActivity, HomeActivity::class.java)
intentHomeActivity.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
if (itemCountDataCityLocal == resultDataKota.count) {
startActivity(intentHomeActivity)
} else {
databaseHelper.deleteDataCity()
databaseHelper.insertDataCity(resultDataKota.data)
startActivity(intentHomeActivity)
}
}
}
}
Pada file tersebut, kita ada melakukan request data list kota dari API dan kemudian, menyimpannya ke database lokal dimana, fungsi ini bisa Anda lihat di fun doLoadData()
. Di dalam function doLoadData ada melakukan pengambilan data dari API Server. ShalatCient.getCityData().await()
ini merupakan fungsi Kotlin Coroutines untuk melakukan pengambilan data dari API Server secara asyncrhonous namun, style atau gaya penulisan kode-nya seolah-olah synchronous. Keyword await()
berfungsi untuk menahan jalannya alur program sampai proses pemanggilannya selesai dan setelah selesai mengambil data dari API Server, baru dilanjutkan ke proses dibawahnya. Kita menggunakan launch
karena, kita tidak ada melakukan pengembalian nilai ketika Kotlin Coroutines dijalankan.
Ubah layout activity_home.xml
Buat activity baru dan beri nama activity-nya HomeActivity.kt dan layout-nya activity_home.xml. Ubah file activity_home.xml menjadi seperti berikut.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#eeeeee"
tools:context="com.codepolitan.sholatyuk.ui.home.HomeActivity">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar_activity_home"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
<RelativeLayout
android:id="@+id/relative_layout_container_contet_activity_home"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/toolbar_activity_home"
android:layout_marginTop="@dimen/activity_vertical_margin"
android:layout_marginEnd="@dimen/activity_horizontal_margin"
android:layout_marginBottom="0dp"
android:layout_marginStart="@dimen/activity_horizontal_margin"
android:background="@drawable/background_white_rouned"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin">
<ImageView
android:id="@+id/image_view_location_city_activity_home"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/activity_horizontal_margin"
android:contentDescription="@string/image_view_location_city"
android:src="@drawable/ic_location_city_black_24dp" />
<TextView
android:id="@+id/text_view_label_location_city_activity_home"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toEndOf="@+id/image_view_location_city_activity_home"
android:text="@string/location_city"
android:textColor="@android:color/darker_gray" />
<TextView
android:id="@+id/text_view_value_location_city_activity_home"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/text_view_label_location_city_activity_home"
android:layout_toEndOf="@+id/image_view_location_city_activity_home"
android:text="@string/choose_location"
android:textSize="16sp" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/button_submit_activity_home"
android:text="@string/submit"
android:textColor="@android:color/white"
android:textAllCaps="false"
android:layout_below="@+id/text_view_value_location_city_activity_home"
android:layout_marginTop="20dp"
android:background="@drawable/background_button_color_accent_rounded" />
</RelativeLayout>
</RelativeLayout>
Ubah file HomeActivity
Buka file HomeActivity dan isi dengan source code berikut.
package com.codepolitan.sholatyuk.ui.home
import android.app.Activity
import android.app.ProgressDialog
import android.content.Intent
import android.os.Bundle
import android.support.design.widget.Snackbar
import android.support.v7.app.AppCompatActivity
import android.view.View
import com.codepolitan.sholatyuk.R
import com.codepolitan.sholatyuk.experimental.Android
import com.codepolitan.sholatyuk.model.Data
import com.codepolitan.sholatyuk.network.api.ShalatClient
import com.codepolitan.sholatyuk.ui.city.CityActivity
import com.codepolitan.sholatyuk.ui.schedule.PrayerScheduleActivity
import kotlinx.android.synthetic.main.activity_home.*
import kotlinx.coroutines.experimental.launch
class HomeActivity : AppCompatActivity(), View.OnClickListener {
private val TAG = javaClass.simpleName
private val REQUEST_CODE_CITY = 210
private var dataCity: Data? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_home)
initListener()
initToolbar()
}
/**
* @description Initialize toolbar
*/
private fun initToolbar() {
setSupportActionBar(toolbar_activity_home)
supportActionBar?.title = getString(R.string.app_name)
}
/**
* @description Initialize all listener view
*/
private fun initListener() {
text_view_value_location_city_activity_home.setOnClickListener(this)
button_submit_activity_home.setOnClickListener(this)
}
/**
* @description override listener on click
* @param view {View} view for on click
*/
override fun onClick(view: View?) {
view?.id.let {
when (it) {
R.id.text_view_value_location_city_activity_home -> {
startActivityForResult(Intent(this@HomeActivity, CityActivity::class.java), REQUEST_CODE_CITY)
}
R.id.button_submit_activity_home -> {
if (text_view_value_location_city_activity_home.text.equals(getString(R.string.choose_location))) {
Snackbar.make(
findViewById(android.R.id.content),
getString(R.string.please_choose_location),
Snackbar.LENGTH_SHORT
).show()
} else {
doLoadDataPrayerSchedule()
}
}
else -> {
/** nothing to do in here */
}
}
}
}
/**
* @description Load data prayer schedule
*/
private fun doLoadDataPrayerSchedule() {
val progressDialog = ProgressDialog(this)
progressDialog.let {
it.setCancelable(false)
it.setMessage(getString(R.string.please_wait))
it.show()
}
launch(Android) {
val resultDataPrayerSchedule = ShalatClient
.getPrayerScheduleData(id = dataCity!!.id.toInt())
.await()
progressDialog.let {
if (it.isShowing) {
it.dismiss()
}
}
val intentPrayerScheduleActivity = Intent(this@HomeActivity, PrayerScheduleActivity::class.java)
intentPrayerScheduleActivity.putExtra("dataPrayer", resultDataPrayerSchedule)
intentPrayerScheduleActivity.putExtra("locationCity", dataCity!!.namaKota)
startActivity(intentPrayerScheduleActivity)
}
}
/**
* @description Activity result from another activity
* @param requestCode {Int} request code when start other activity
* @param resultCode {Int} result code when back to this activity
* @param data {Intent} Intent data from another activity
*/
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when (resultCode) {
Activity.RESULT_OK -> {
when (requestCode) {
REQUEST_CODE_CITY -> {
val bundle = data?.extras
val dataCity = bundle?.get("data") as Data
this@HomeActivity.dataCity = dataCity.copy()
text_view_value_location_city_activity_home.text = dataCity.namaKota
}
else -> {
/** nothing to do in here */
}
}
}
else -> {
/** nothing to do in here */
}
}
super.onActivityResult(requestCode, resultCode, data)
}
}
Pada file HomeActivity, kita ada memberikan OnClick Listener pada TextView value choose location dan Button submit. OnClick Listener kita berikan pada TextView value choose location untuk kita arahkan ke CityActivity dan ketika di activity tersebut si user akan memilih lokasi kota-nya. Ketika si user telah memilih lokasi kotanya maka, app secara otomatis akan kembali ke HomeActivity dimana, data yang dipilih si user dari CityActivity akan ditampung di function onActivityResult di HomeActivity. Kemudian, Button submit kita beri OnClick Listener untuk mengirimkan data lokasi kota si user ke PrayerScheduleActivity yang mana, sebelumnya akan divalidasi terlebih dahulu apakah data lokasi kota si user valid ataut tidak. Jika tidak valid maka, app akan memunculkan Snackbar message dan jika valid maka, app akan berpindah ke PrayerScheduleActivity.
Ubah layout activity_city
Buat activity baru dengan nama activity CityActivity dan layout-nya activity_city. Buka file activity_city dan isi dengan source code berikut.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.codepolitan.sholatyuk.ui.city.CityActivity">
<android.support.v7.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:id="@+id/toolbar_activity_city"
android:background="@color/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/toolbar_activity_city"
android:paddingEnd="@dimen/activity_horizontal_margin"
android:paddingStart="@dimen/activity_horizontal_margin"
android:id="@+id/recycler_view_data_city_activity_city" />
</RelativeLayout>
Ubah file CityActivity
Buka file CityActivity dan isi dengan source code berikut.
package com.codepolitan.sholatyuk.ui.city
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.DividerItemDecoration
import android.support.v7.widget.LinearLayoutManager
import android.view.MenuItem
import com.codepolitan.sholatyuk.R
import com.codepolitan.sholatyuk.db.DatabaseHelper
import com.codepolitan.sholatyuk.model.Data
import com.codepolitan.sholatyuk.ui.city.adapter.AdapterDataCity
import kotlinx.android.synthetic.main.activity_city.*
class CityActivity : AppCompatActivity() {
private val TAG = javaClass.simpleName
private val databaseHelper by lazy {
DatabaseHelper(
context = this@CityActivity,
name = DatabaseHelper.DATABASE_NAME,
factory = null,
version = DatabaseHelper.DATABASE_VERSION
)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_city)
initToolbar()
doLoadData()
}
/**
* @desription Load data from database local and setup adapter recyclerview
*/
private fun doLoadData() {
val listDataCity = databaseHelper.getDataCity()
recycler_view_data_city_activity_city.let {
val adapterDataCity = AdapterDataCity(
listDataCity = listDataCity,
listenerAdapterDataCity = object : AdapterDataCity.ListenerAdapterDataCity {
override fun clickItem(data: Data) {
val intent = Intent()
intent.putExtra("data", data)
setResult(Activity.RESULT_OK, intent)
finish()
}
}
)
val dividerItemDecoration = DividerItemDecoration(this, DividerItemDecoration.VERTICAL)
it.addItemDecoration(dividerItemDecoration)
it.layoutManager = LinearLayoutManager(this)
it.adapter = adapterDataCity
}
}
/**
* @description Initialize toolbar
*/
private fun initToolbar() {
setSupportActionBar(toolbar_activity_city)
supportActionBar?.let {
it.setDisplayHomeAsUpEnabled(true)
it.title = getString(R.string.choose_location)
}
}
/**
* @description On options item selected in toolbar
* @param item {MenuItem} item selected in toolbar
* @return {Boolean} return true or false when selected item in toolbar
*/
override fun onOptionsItemSelected(item: MenuItem?): Boolean =
item?.itemId.let {
return when (it) {
android.R.id.home -> {
onBackPressed()
true
}
else -> {
super.onOptionsItemSelected(item)
}
}
}
}
Pada file ini, kita hanya melakukan setup adapter for RecyclerView karena, CityActivity hanya berfungsi untuk si user memilih lokasi kota-nya saja. Jadi, tidak begitu susah logic di file ini.
Buat file layout item_data_city
Buat file item_data_city dan isi dengan source code berikut.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/relative_layout_container_item_data_city"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingTop="@dimen/activity_vertical_margin">
<TextView
android:id="@+id/text_view_city_name_item_data_city"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp" />
</RelativeLayout>
File ini nanti akan kita gunakan sebagai item layout untuk si RecyclerView di CityActivity
Buat file AdapterDataCity
Buat file AdapterDataCity dan isi dengan source code berikut.
package com.codepolitan.sholatyuk.ui.city.adapter
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.codepolitan.sholatyuk.R
import com.codepolitan.sholatyuk.model.Data
import kotlinx.android.synthetic.main.item_data_city.view.*
/**
* Created by yudisetiawan on 12/24/17.
*/
class AdapterDataCity(val listDataCity: List<Data>, val listenerAdapterDataCity: ListenerAdapterDataCity) : RecyclerView.Adapter<AdapterDataCity.ViewHolderItemDataCity>() {
private val TAG = javaClass.simpleName
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ViewHolderItemDataCity =
ViewHolderItemDataCity(
LayoutInflater.from(parent?.context).inflate(
R.layout.item_data_city, null
)
)
override fun onBindViewHolder(holder: ViewHolderItemDataCity?, position: Int) {
holder?.itemView?.let {
it.text_view_city_name_item_data_city.text = listDataCity[position].namaKota
}
}
override fun getItemCount(): Int = listDataCity.size
inner class ViewHolderItemDataCity(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener {
init {
itemView.text_view_city_name_item_data_city.setOnClickListener(this)
itemView.relative_layout_container_item_data_city.setOnClickListener(this)
}
override fun onClick(view: View?) {
view?.id.let {
when (it) {
R.id.relative_layout_container_item_data_city,
R.id.text_view_city_name_item_data_city -> {
listenerAdapterDataCity.clickItem(listDataCity[adapterPosition])
}
else -> {
/** nothing to do in here */
}
}
}
}
}
interface ListenerAdapterDataCity {
fun clickItem(data: Data)
}
}
Pada file adapter ini, kita hanya melakukan set text ke TextView city name dan buat interface si adapter agar bisa mengirimkan data kota yang dipilih si user lewat fun clickItem(data: Data)
Buat file activity_prayer_schedule
Buat activity baru dengan nama activity-nya PrayerScheduleActivity dan layout-nya activity_prayer_schedule. Selanjutnya, buka file activity_prayer_schedule dan isi dengan source code berikut.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.codepolitan.sholatyuk.ui.schedule.PrayerScheduleActivity">
<LinearLayout
android:id="@+id/linear_layout_container_content_activity_prayer_schedule"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1.5">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/img_mountain" />
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0.5"
android:background="#000000" />
<TextView
android:id="@+id/text_view_location_city_activity_prayer_schedule"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_marginTop="8dp"
android:alpha="0.7"
android:fontFamily="sans-serif-condensed"
android:text="@string/location_city"
android:textColor="@android:color/white"
android:textSize="24sp" />
<ImageView
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_above="@+id/text_view_location_city_activity_prayer_schedule"
android:layout_centerHorizontal="true"
android:alpha="0.8"
android:src="@drawable/ic_location_on_white_24dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/text_view_location_city_activity_prayer_schedule"
android:layout_centerHorizontal="true"
android:alpha="0.6"
android:fontFamily="sans-serif-condensed"
android:text="@string/indonesia"
android:textColor="#f5f5f5"
android:textSize="14sp" />
</RelativeLayout>
<TextView
android:id="@+id/text_view_date_activity_prayer_schedule"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#e0e0e0"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingEnd="@dimen/activity_horizontal_margin"
android:paddingStart="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:text="@string/mon_august_29_2015"
android:textAllCaps="true"
android:textColor="#757575" />
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view_data_prayer_schedule_activity_prayer_schedule"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar_activity_prayer_schedule"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@android:color/transparent"
android:theme="@style/ThemeOverlay.AppCompat.Dark"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
</RelativeLayout>
Ubah file PrayerScheduleActivity
Buka file PrayerScheduleActivity dan isi dengan source code berikut.
package com.codepolitan.sholatyuk.ui.schedule
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.LinearLayoutManager
import android.view.MenuItem
import com.codepolitan.sholatyuk.R
import com.codepolitan.sholatyuk.model.DataJadwalSholat
import com.codepolitan.sholatyuk.ui.schedule.adapter.AdapterPrayerSchedule
import kotlinx.android.synthetic.main.activity_prayer_schedule.*
import java.text.SimpleDateFormat
import java.util.*
import kotlin.collections.ArrayList
class PrayerScheduleActivity : AppCompatActivity() {
private val TAG = javaClass.simpleName
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_prayer_schedule)
initToolbar()
doLoadData()
}
/**
* @description Setup data prayer schedule and setup adapter recyclerview
*/
private fun doLoadData() {
val bundle = intent.extras
val dataPrayer = bundle.get("dataPrayer") as DataJadwalSholat
val locationCity = bundle.getString("locationCity")
text_view_location_city_activity_prayer_schedule.text = locationCity
text_view_date_activity_prayer_schedule.text = SimpleDateFormat("HH:mm EEE, MMMM d, yyyy", Locale.US).format(Date())
val listPrayTime = ArrayList<String>()
val listPrayName = ArrayList<String>()
val dateNow = SimpleDateFormat("yyyy-MM-dd", Locale.US).format(Date()).let {
it.substring(it.length - 2, it.length)
}
for (prayerItem in dataPrayer.data) {
if (prayerItem.tanggal == dateNow) {
listPrayTime.let {
it.add(prayerItem.shubuh)
it.add(prayerItem.dzuhur)
it.add(prayerItem.ashr)
it.add(prayerItem.maghrib)
it.add(prayerItem.isya)
}
listPrayName.let {
it.add("shubuh")
it.add("dzuhur")
it.add("ashr")
it.add("maghrib")
it.add("isya")
}
break
}
}
recycler_view_data_prayer_schedule_activity_prayer_schedule.let {
val adapterPrayerSchedule = AdapterPrayerSchedule(
this,
listPrayTime,
listPrayName
)
it.layoutManager = LinearLayoutManager(this)
it.adapter = adapterPrayerSchedule
}
}
/**
* @description Initialize toolbar
*/
private fun initToolbar() {
setSupportActionBar(toolbar_activity_prayer_schedule)
supportActionBar?.let {
it.title = ""
it.setDisplayHomeAsUpEnabled(true)
}
}
/**
* @description On options item selected
* @param item {MenuItem} menu item in toolbar
* @return {Boolean} return true or false when selected menu item
*/
override fun onOptionsItemSelected(item: MenuItem?): Boolean =
item?.itemId.let {
return when (it) {
android.R.id.home -> {
onBackPressed()
true
}
else -> {
super.onOptionsItemSelected(item)
}
}
}
}
Pada file ini, kita hanya menyajikan data yang dikirim dari activity sebelumnya yaitu, HomeActivity dimana, HomeActivity ada mengirimkan data jadwal sholat ketika, button submit di tap oleh user. Dan di PrayerScheduleActivity menerima data tersebut dan melakukan setup adapter recyclerview dan kemudian, menampilkannya.
Buat file AdapterPrayerSchedule
Buat file class baru dengan nama AdapterPrayerSchedule dan isi dengan source code berikut.
package com.codepolitan.sholatyuk.ui.schedule.adapter
import android.content.Context
import android.support.v4.content.ContextCompat
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.codepolitan.sholatyuk.R
import kotlinx.android.synthetic.main.item_prayer_schedule.view.*
import java.text.SimpleDateFormat
import java.util.*
/**
* Created by yudisetiawan on 12/24/17.
*/
class AdapterPrayerSchedule(val context: Context, val listPrayTime: List<String>, val listPrayName: List<String>) : RecyclerView.Adapter<AdapterPrayerSchedule.ViewHolderItemPrayerSchedule>() {
private val TAG = javaClass.simpleName
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): AdapterPrayerSchedule.ViewHolderItemPrayerSchedule =
ViewHolderItemPrayerSchedule(
LayoutInflater.from(parent?.context)
.inflate(R.layout.item_prayer_schedule, null)
)
override fun onBindViewHolder(holder: AdapterPrayerSchedule.ViewHolderItemPrayerSchedule?, position: Int) {
holder?.itemView?.let {
val prayerName = listPrayName[position]
val prayerTime = listPrayTime[position]
val timeNow = SimpleDateFormat("HH:mm", Locale.US).format(Date())
val timeHourNow = timeNow.split(":")[0].toInt()
val timeMinuteNow = timeNow.split(":")[1].toInt()
val timeHourPrayer = prayerTime.split(":")[0].toInt()
val timeMinutePrayer = prayerTime.split(":")[1].toInt()
var estimatedHour: Int
estimatedHour = if (timeHourNow < timeHourPrayer) {
timeHourPrayer - timeHourNow
} else {
0
}
val estimatedMinute: Int
estimatedMinute = if (timeMinuteNow < timeMinutePrayer && timeHourNow < timeHourPrayer) {
timeMinutePrayer - timeMinuteNow
} else {
if (estimatedHour == 0) {
0
} else {
estimatedHour -= 1
60 - timeMinuteNow + timeMinutePrayer
}
}
it.text_view_estimated_time_item_prayer_schedule.let {
if (estimatedHour == 0 && estimatedMinute == 0) {
it.text = "Esimated time: skip"
} else {
it.text = "Estimated time: $estimatedHour Hours $estimatedMinute Minutes"
}
}
val hour = prayerTime.split(":")[0].toInt().let {
if (it > 12) {
it - 12
} else {
it
}
}
val minute = prayerTime.split(":")[1].toInt()
it.text_view_hour_item_prayer_schedule.text = hour.toString()
it.text_view_minute_item_prayer_schedule.let {
if (minute < 10) {
it.text = ":0$minute"
} else {
it.text = ":$minute"
}
}
it.text_view_prayer_name_item_prayer_schedule.text = prayerName.capitalize()
when (prayerName) {
"shubuh" -> {
it.image_view_circle_item_prayer_schedule
.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_circle_blue))
it.text_view_format_hour_item_prayer_schedule.text = context.getString(R.string.am)
}
"dzuhur" -> {
it.image_view_circle_item_prayer_schedule
.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_circle_pink))
it.text_view_format_hour_item_prayer_schedule.text = context.getString(R.string.pm)
}
"ashr" -> {
it.image_view_circle_item_prayer_schedule
.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_circle_green))
it.text_view_format_hour_item_prayer_schedule.text = context.getString(R.string.pm)
}
"maghrib" -> {
it.image_view_circle_item_prayer_schedule
.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_circle_lime))
it.text_view_format_hour_item_prayer_schedule.text = context.getString(R.string.pm)
}
"isya" -> {
it.image_view_circle_item_prayer_schedule
.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_circle_brown))
it.text_view_format_hour_item_prayer_schedule.text = context.getString(R.string.pm)
}
else -> {
/** nothing to do in here */
}
}
}
}
override fun getItemCount(): Int = listPrayName.size
inner class ViewHolderItemPrayerSchedule(itemView: View?) : RecyclerView.ViewHolder(itemView)
}
File AdapterPrayerSchedule berfungsi sebagai adapter untuk si RecyclerView di PrayerScheduleActivity dimana, pada file adapter ini di fun onBindViewHolder() ada sedikit logic-nya dimana, app akan menampilkan waktu tersisa atau estimated time dari jadwal shalat berdasarkan waktunya sekarang dan jika sudah lewat maka, app akan menampilkan skip
dan jika belum lewat jadwalnya maka, app akan menampilkan sisa waktunya menuju jadwal shalat. Selanjutnya, si app juga menggunakan format am atau pm jadi, logic-nya itu hanya melakukan pengecekan jika value hour lebih besari dari 12 maka, format-nya pm dan jika tidak maka, format-nya am.
Output
Berikut adalah output dari program yang kita buat pada tutorial ini.



Projek lengkapnya bisa Anda lihat di https://github.com/CoderJava/SholatYuk
Kesimpulan
Jadi, Kotlin Coroutines sebenarnya berfungsi untuk melakukan execute sesuatu secara asynchronous dan penggunaan multi-thread.
0
0
0
share