Skip to content

Commit 2f0abc1

Browse files
step-08: implement Android application
1 parent 805afd3 commit 2f0abc1

File tree

7 files changed

+231
-19
lines changed

7 files changed

+231
-19
lines changed

androidApp/build.gradle.kts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,13 @@ dependencies {
1616
implementation(project(":shared"))
1717
implementation("com.google.android.material:material:1.2.0")
1818
implementation("androidx.appcompat:appcompat:1.2.0")
19-
implementation("androidx.constraintlayout:constraintlayout:1.1.3")
19+
implementation("androidx.constraintlayout:constraintlayout:2.0.0")
20+
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
21+
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.7-1.4-M3")
22+
implementation("androidx.core:core-ktx:1.3.1")
23+
implementation("androidx.recyclerview:recyclerview:1.1.0")
24+
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
25+
implementation("androidx.cardview:cardview:1.0.0")
2026
}
2127
android {
2228
compileSdkVersion(29)
@@ -32,4 +38,13 @@ android {
3238
isMinifyEnabled = false
3339
}
3440
}
35-
}
41+
compileOptions {
42+
sourceCompatibility = JavaVersion.VERSION_1_8
43+
targetCompatibility = JavaVersion.VERSION_1_8
44+
}
45+
46+
kotlinOptions {
47+
jvmTarget = JavaVersion.VERSION_1_8.toString()
48+
}
49+
}
50+
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package com.jetbrains.handson.androidApp
2+
3+
import android.view.LayoutInflater
4+
import android.view.View
5+
import android.view.ViewGroup
6+
import android.widget.TextView
7+
import androidx.core.content.ContextCompat
8+
import androidx.recyclerview.widget.RecyclerView
9+
import com.jetbrains.handson.kmm.shared.entity.RocketLaunch
10+
11+
class LaunchesRvAdapter(var launches: List<RocketLaunch>) : RecyclerView.Adapter<LaunchesRvAdapter.LaunchViewHolder>() {
12+
13+
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LaunchViewHolder {
14+
return LayoutInflater.from(parent.context)
15+
.inflate(R.layout.item_launch, parent, false)
16+
.run(::LaunchViewHolder)
17+
}
18+
19+
override fun getItemCount(): Int = launches.count()
20+
21+
override fun onBindViewHolder(holder: LaunchViewHolder, position: Int) {
22+
holder.bindData(launches[position])
23+
}
24+
25+
inner class LaunchViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
26+
private val missionNameTextView = itemView.findViewById<TextView>(R.id.missionName)
27+
private val launchYearTextView = itemView.findViewById<TextView>(R.id.launchYear)
28+
private val launchSuccessTextView = itemView.findViewById<TextView>(R.id.launchSuccess)
29+
private val missionDetailsTextView = itemView.findViewById<TextView>(R.id.details)
30+
31+
fun bindData(launch: RocketLaunch) {
32+
val ctx = itemView.context
33+
missionNameTextView.text = ctx.getString(R.string.mission_name_field, launch.missionName)
34+
launchYearTextView.text = ctx.getString(R.string.launch_year_field, launch.launchYear.toString())
35+
missionDetailsTextView.text = ctx.getString(R.string.details_field, launch.details ?: "")
36+
val launchSuccess = launch.launchSuccess
37+
if (launchSuccess != null ) {
38+
if (launchSuccess) {
39+
launchSuccessTextView.text = ctx.getString(R.string.successful)
40+
launchSuccessTextView.setTextColor((ContextCompat.getColor(itemView.context, R.color.colorSuccessful)))
41+
} else {
42+
launchSuccessTextView.text = ctx.getString(R.string.unsuccessful)
43+
launchSuccessTextView.setTextColor((ContextCompat.getColor(itemView.context, R.color.colorUnsuccessful)))
44+
}
45+
} else {
46+
launchSuccessTextView.text = ctx.getString(R.string.no_data)
47+
launchSuccessTextView.setTextColor((ContextCompat.getColor(itemView.context, R.color.colorNoData)))
48+
}
49+
}
50+
}
51+
}

androidApp/src/main/java/com/jetbrains/handson/androidApp/MainActivity.kt

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,67 @@ package com.jetbrains.handson.androidApp
22

33
import androidx.appcompat.app.AppCompatActivity
44
import android.os.Bundle
5-
import android.widget.TextView
5+
import android.widget.FrameLayout
6+
import android.widget.Toast
7+
import androidx.recyclerview.widget.LinearLayoutManager
8+
import androidx.recyclerview.widget.RecyclerView
9+
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
10+
import com.jetbrains.handson.kmm.shared.SpaceXSDK
11+
import com.jetbrains.handson.kmm.shared.cache.DatabaseDriverFactory
12+
import kotlinx.coroutines.MainScope
13+
import kotlinx.coroutines.launch
14+
import androidx.core.view.isVisible
15+
import kotlinx.coroutines.cancel
616

717

818
class MainActivity : AppCompatActivity() {
19+
private val mainScope = MainScope()
20+
21+
private lateinit var launchesRecyclerView: RecyclerView
22+
private lateinit var progressBarView: FrameLayout
23+
private lateinit var swipeRefreshLayout: SwipeRefreshLayout
24+
25+
private val sdk = SpaceXSDK(DatabaseDriverFactory(this))
26+
27+
private val launchesRvAdapter = LaunchesRvAdapter(listOf())
28+
929
override fun onCreate(savedInstanceState: Bundle?) {
1030
super.onCreate(savedInstanceState)
31+
title = "SpaceX Launches"
1132
setContentView(R.layout.activity_main)
33+
34+
launchesRecyclerView = findViewById(R.id.launchesListRv)
35+
progressBarView = findViewById(R.id.progressBar)
36+
swipeRefreshLayout = findViewById(R.id.swipeContainer)
37+
38+
launchesRecyclerView.adapter = launchesRvAdapter
39+
launchesRecyclerView.layoutManager = LinearLayoutManager(this)
40+
41+
swipeRefreshLayout.setOnRefreshListener {
42+
swipeRefreshLayout.isRefreshing = false
43+
displayLaunches(true)
44+
}
45+
46+
displayLaunches(false)
47+
}
48+
49+
override fun onDestroy() {
50+
super.onDestroy()
51+
mainScope.cancel()
52+
}
53+
54+
private fun displayLaunches(needReload: Boolean) {
55+
progressBarView.isVisible = true
56+
mainScope.launch {
57+
kotlin.runCatching {
58+
sdk.getLaunches(needReload)
59+
}.onSuccess {
60+
launchesRvAdapter.launches = it
61+
launchesRvAdapter.notifyDataSetChanged()
62+
}.onFailure {
63+
Toast.makeText(this@MainActivity, it.localizedMessage, Toast.LENGTH_SHORT).show()
64+
}
65+
progressBarView.isVisible = false
66+
}
1267
}
13-
}
68+
}
Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,41 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<androidx.constraintlayout.widget.ConstraintLayout
33
xmlns:android="http://schemas.android.com/apk/res/android"
4-
xmlns:tools="http://schemas.android.com/tools"
54
xmlns:app="http://schemas.android.com/apk/res-auto"
65
android:layout_width="match_parent"
7-
android:layout_height="match_parent"
8-
android:id="@+id/main_view"
9-
tools:context=".MainActivity">
6+
android:layout_height="match_parent" >
107

11-
<TextView
12-
android:layout_width="wrap_content"
13-
android:layout_height="wrap_content"
14-
android:text="Hello World!"
15-
android:id="@+id/text_view"
8+
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
9+
android:id="@+id/swipeContainer"
10+
android:layout_width="match_parent"
11+
android:layout_height="match_parent"
1612
app:layout_constraintBottom_toBottomOf="parent"
17-
app:layout_constraintLeft_toLeftOf="parent"
18-
app:layout_constraintRight_toRightOf="parent"
19-
app:layout_constraintTop_toTopOf="parent"/>
13+
app:layout_constraintEnd_toEndOf="parent"
14+
app:layout_constraintStart_toStartOf="parent"
15+
app:layout_constraintTop_toTopOf="parent">
16+
17+
<androidx.recyclerview.widget.RecyclerView
18+
android:id="@+id/launchesListRv"
19+
android:layout_width="match_parent"
20+
android:layout_height="match_parent" />
21+
22+
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
23+
24+
<FrameLayout
25+
android:id="@+id/progressBar"
26+
android:layout_width="0dp"
27+
android:layout_height="0dp"
28+
android:background="#fff"
29+
app:layout_constraintBottom_toBottomOf="parent"
30+
app:layout_constraintEnd_toEndOf="parent"
31+
app:layout_constraintStart_toStartOf="parent"
32+
app:layout_constraintTop_toTopOf="parent">
33+
34+
<ProgressBar
35+
android:layout_width="wrap_content"
36+
android:layout_height="wrap_content"
37+
android:layout_gravity="center" />
38+
39+
</FrameLayout>
2040

2141
</androidx.constraintlayout.widget.ConstraintLayout>
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:app="http://schemas.android.com/apk/res-auto"
4+
xmlns:card_view="http://schemas.android.com/tools"
5+
android:layout_width="match_parent"
6+
android:layout_height="wrap_content"
7+
android:layout_marginHorizontal="16dp"
8+
android:layout_marginVertical="8dp"
9+
card_view:cardCornerRadius="8dp">
10+
11+
<androidx.constraintlayout.widget.ConstraintLayout
12+
android:layout_width="match_parent"
13+
android:layout_height="wrap_content"
14+
android:paddingBottom="16dp">
15+
16+
<TextView
17+
android:id="@+id/missionName"
18+
android:layout_width="0dp"
19+
android:layout_height="wrap_content"
20+
android:layout_margin="8dp"
21+
app:layout_constraintEnd_toEndOf="parent"
22+
app:layout_constraintStart_toStartOf="parent"
23+
app:layout_constraintTop_toTopOf="parent" />
24+
25+
<TextView
26+
android:id="@+id/launchSuccess"
27+
android:layout_width="0dp"
28+
android:layout_height="wrap_content"
29+
android:layout_margin="8dp"
30+
app:layout_constraintEnd_toEndOf="parent"
31+
app:layout_constraintStart_toStartOf="parent"
32+
app:layout_constraintTop_toBottomOf="@+id/missionName" />
33+
34+
<TextView
35+
android:id="@+id/launchYear"
36+
android:layout_width="0dp"
37+
android:layout_height="wrap_content"
38+
android:layout_margin="8dp"
39+
app:layout_constraintEnd_toEndOf="parent"
40+
app:layout_constraintStart_toStartOf="parent"
41+
app:layout_constraintTop_toBottomOf="@+id/launchSuccess" />
42+
43+
<TextView
44+
android:id="@+id/details"
45+
android:layout_width="0dp"
46+
android:layout_height="wrap_content"
47+
android:layout_margin="8dp"
48+
app:layout_constraintEnd_toEndOf="parent"
49+
app:layout_constraintStart_toStartOf="parent"
50+
app:layout_constraintTop_toBottomOf="@+id/launchYear" />
51+
52+
</androidx.constraintlayout.widget.ConstraintLayout>
53+
54+
</androidx.cardview.widget.CardView>
Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<resources>
3-
<color name="colorPrimary">#6200EE</color>
4-
<color name="colorPrimaryDark">#3700B3</color>
5-
<color name="colorAccent">#03DAC5</color>
3+
<color name="colorPrimary">#37474f</color>
4+
<color name="colorPrimaryDark">#102027</color>
5+
<color name="colorAccent">#62727b</color>
6+
7+
<color name="colorSuccessful">#4BB543</color>
8+
<color name="colorUnsuccessful">#FC100D</color>
9+
<color name="colorNoData">#615F5F</color>
610
</resources>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<resources>
3+
<string name="app_name">KmmApp</string>
4+
5+
<string name="successful">Successful</string>
6+
<string name="unsuccessful">Unsuccessful</string>
7+
<string name="no_data">No data</string>
8+
9+
<string name="launch_year_field">Launch year: %s</string>
10+
<string name="mission_name_field">Launch name: %s</string>
11+
<string name="launch_success_field">Launch success: %s</string>
12+
<string name="details_field">Launch details: %s</string>
13+
</resources>

0 commit comments

Comments
 (0)