Skip to content

Commit 4a7ccd5

Browse files
committed
Android GUI: Migrate Rx Java to Kotlin Coroutines
1 parent fabd16d commit 4a7ccd5

6 files changed

Lines changed: 136 additions & 191 deletions

File tree

Source/GUI/Android/app/build.gradle

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ tasks.register('copyLocales', DefaultTask) {
202202
preBuild.dependsOn copyLocales
203203

204204
dependencies {
205-
implementation 'com.google.android.material:material:1.13.0'
205+
implementation 'com.google.android.material:material:1.13.0' // can only upgrade when minSdkVersion >= 23
206206
implementation 'androidx.appcompat:appcompat:1.7.1'
207207
implementation "androidx.core:core-ktx:1.17.0" // can only upgrade when minSdkVersion >= 23
208208
implementation 'androidx.documentfile:documentfile:1.1.0'
@@ -217,9 +217,6 @@ dependencies {
217217
implementation 'androidx.room:room-runtime:2.7.2'
218218
implementation 'androidx.room:room-rxjava2:2.7.2'
219219
ksp 'androidx.room:room-compiler:2.7.2'
220-
//RxJava
221-
implementation 'io.reactivex.rxjava2:rxjava:2.2.21'
222-
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
223220
// Android Lifecycle
224221
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
225222
ksp "androidx.lifecycle:lifecycle-common-java8:2.10.0"
@@ -228,9 +225,7 @@ dependencies {
228225
implementation "com.android.billingclient:billing:8.0.0"
229226
implementation "com.android.billingclient:billing-ktx:8.0.0"
230227
// KotlinX Coroutines
231-
api "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2"
232-
api "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2"
233-
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-rx2:1.10.2"
228+
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.11.0"
234229
// Locales manager
235230
implementation 'com.github.YarikSOffice:lingver:1.3.0'
236231
}

Source/GUI/Android/app/src/main/java/net/mediaarea/mediainfo/ReportDao.kt

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,38 +7,37 @@
77
package net.mediaarea.mediainfo
88

99
import androidx.room.Dao
10-
import androidx.room.Query
11-
import androidx.room.Insert
12-
import androidx.room.Update
1310
import androidx.room.Delete
11+
import androidx.room.Insert
1412
import androidx.room.OnConflictStrategy
15-
16-
import io.reactivex.Flowable
17-
import io.reactivex.Single
13+
import androidx.room.Query
14+
import androidx.room.Update
15+
import kotlinx.coroutines.flow.Flow
1816

1917
@Dao
2018
interface ReportDao {
19+
2120
@Query("SELECT MAX(id) FROM reports")
22-
fun getLastId(): Single<Int>
21+
suspend fun getLastId(): Int?
2322

2423
@Query("SELECT * FROM reports WHERE id = :id")
25-
fun getReport(id: Int): Single<Report>
24+
suspend fun getReport(id: Int): Report
2625

2726
@Query("SELECT * FROM reports ORDER BY id")
28-
fun getAllReports(): Flowable<List<Report>>
27+
fun getAllReports(): Flow<List<Report>>
2928

3029
@Insert(onConflict = OnConflictStrategy.REPLACE)
31-
fun insertReport(report: Report)
30+
suspend fun insertReport(report: Report): Long
3231

3332
@Update(onConflict = OnConflictStrategy.REPLACE)
34-
fun updateReport(report: Report)
33+
suspend fun updateReport(report: Report)
3534

3635
@Delete
37-
fun deleteReport(report: Report)
36+
suspend fun deleteReport(report: Report)
3837

3938
@Query("DELETE FROM reports WHERE id = :id")
40-
fun deleteReport(id: Int)
39+
suspend fun deleteReport(id: Int)
4140

4241
@Query("DELETE FROM reports")
43-
fun deleteAllReports()
42+
suspend fun deleteAllReports()
4443
}

Source/GUI/Android/app/src/main/java/net/mediaarea/mediainfo/ReportDetailActivity.kt

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,19 @@ import android.content.Intent
1313
import android.os.Bundle
1414
import android.os.Build
1515

16-
import android.view.MenuItem
17-
1816
import android.content.res.AssetManager
17+
import android.view.MenuItem
1918
import android.view.View
2019
import androidx.core.view.ViewCompat
2120
import androidx.core.view.WindowCompat
2221
import androidx.core.view.WindowInsetsCompat
2322
import androidx.core.view.updatePadding
24-
23+
import androidx.lifecycle.lifecycleScope
2524
import androidx.viewpager2.widget.ViewPager2
2625

27-
import io.reactivex.disposables.CompositeDisposable
28-
import io.reactivex.schedulers.Schedulers
29-
import io.reactivex.android.schedulers.AndroidSchedulers
26+
import kotlinx.coroutines.flow.collectLatest
27+
import kotlinx.coroutines.launch
28+
3029
import net.mediaarea.mediainfo.databinding.ActivityReportDetailBinding
3130

3231
class ReportDetailActivity : AppCompatActivity(), ReportActivityListener {
@@ -40,7 +39,6 @@ class ReportDetailActivity : AppCompatActivity(), ReportActivityListener {
4039

4140
}
4241
}
43-
private val disposable: CompositeDisposable = CompositeDisposable()
4442
private lateinit var reportModel: ReportViewModel
4543

4644
override fun getReportViewModel(): ReportViewModel {
@@ -96,28 +94,24 @@ class ReportDetailActivity : AppCompatActivity(), ReportActivityListener {
9694
val viewModelFactory = Injection.provideViewModelFactory(this)
9795
reportModel = ViewModelProvider(this, viewModelFactory)[ReportViewModel::class.java]
9896

99-
disposable.add(reportModel.getAllReports()
100-
.subscribeOn(Schedulers.io())
101-
.observeOn(AndroidSchedulers.mainThread())
102-
.subscribe { reports: List<Report> ->
103-
activityReportDetailBinding.pager.registerOnPageChangeCallback(PageChangeListener(reports))
104-
activityReportDetailBinding.pager.adapter = PagerAdapter(this, reports)
97+
lifecycleScope.launch {
98+
reportModel.getAllReports().collectLatest { reports: List<Report> ->
99+
activityReportDetailBinding.pager.registerOnPageChangeCallback(
100+
PageChangeListener(
101+
reports
102+
)
103+
)
104+
activityReportDetailBinding.pager.adapter = PagerAdapter(this@ReportDetailActivity, reports)
105105
val id = intent.getIntExtra(Core.ARG_REPORT_ID, -1)
106-
if (id!=-1) {
106+
if (id != -1) {
107107
val index = reports.indexOfFirst { it.id == id }
108-
if (index!=-1) {
108+
if (index != -1) {
109109
title = reports.elementAt(index).filename
110110
activityReportDetailBinding.pager.setCurrentItem(index, false)
111111
}
112112
}
113-
})
114-
}
115-
116-
override fun onStop() {
117-
super.onStop()
118-
119-
// clear all the subscription
120-
disposable.clear()
113+
}
114+
}
121115
}
122116

123117
override fun finish() {

Source/GUI/Android/app/src/main/java/net/mediaarea/mediainfo/ReportDetailFragment.kt

Lines changed: 39 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import androidx.core.view.MenuProvider
1717
import androidx.documentfile.provider.DocumentFile
1818
import androidx.fragment.app.Fragment
1919
import androidx.lifecycle.Lifecycle
20+
import androidx.lifecycle.lifecycleScope
2021
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
2122

2223
import android.os.Bundle
@@ -25,15 +26,14 @@ import android.content.Context
2526
import android.view.*
2627
import android.widget.Toast
2728

28-
import io.reactivex.android.schedulers.AndroidSchedulers
29-
import io.reactivex.disposables.CompositeDisposable
30-
import io.reactivex.schedulers.Schedulers
29+
import kotlinx.coroutines.Dispatchers
30+
import kotlinx.coroutines.launch
31+
import kotlinx.coroutines.withContext
3132

3233
import net.mediaarea.mediainfo.databinding.ReportDetailBinding
3334

3435

3536
class ReportDetailFragment : Fragment() {
36-
private val disposable: CompositeDisposable = CompositeDisposable()
3737
private lateinit var activityListener: ReportActivityListener
3838
private var sharedPreferences: SharedPreferences? = null
3939
private lateinit var reportDetailBinding: ReportDetailBinding
@@ -52,32 +52,29 @@ class ReportDetailFragment : Fragment() {
5252
return@let
5353
}
5454

55-
disposable
56-
.add(activityListener.getReportViewModel().getReport(it)
57-
.subscribeOn(Schedulers.io())
58-
.observeOn(AndroidSchedulers.mainThread())
59-
.subscribe { report: Report ->
60-
if (report.report.isEmpty()) {
55+
lifecycleScope.launch {
56+
val report = activityListener.getReportViewModel().getReport(it)
57+
if (report.report.isEmpty()) {
58+
onError()
59+
} else {
60+
val currentContext: Context? = context
61+
if (currentContext == null) {
62+
onError()
63+
} else {
64+
val directory = DocumentFile.fromTreeUri(currentContext, uri)
65+
66+
if (directory == null) {
6167
onError()
6268
} else {
63-
val currentContext: Context? = context
64-
if (currentContext == null) {
69+
if (!directory.canWrite()) {
6570
onError()
6671
} else {
67-
val directory = DocumentFile.fromTreeUri(currentContext, uri)
68-
69-
if (directory == null) {
70-
onError()
71-
} else {
72-
if (!directory.canWrite()) {
73-
onError()
74-
} else {
75-
saveReport(directory, report)
76-
}
77-
}
72+
saveReport(directory, report)
7873
}
7974
}
80-
})
75+
}
76+
}
77+
}
8178
}
8279
}
8380

@@ -120,36 +117,30 @@ class ReportDetailFragment : Fragment() {
120117
}
121118
}
122119

123-
override fun onStop() {
124-
super.onStop()
125-
126-
// clear all the subscription
127-
disposable.clear()
128-
}
129-
130120
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
131121
savedInstanceState: Bundle?): View {
132122

133123
//val rootView = inflater.inflate(R.layout.report_detail, container, false)
134124
// Show the report
135125
id?.let { id ->
136-
disposable.add(activityListener.getReportViewModel().getReport(id)
137-
.subscribeOn(Schedulers.io())
138-
.observeOn(AndroidSchedulers.mainThread())
139-
.doOnSuccess {
140-
val report: String = Core.convertReport(it.report, view)
141-
var content = ""
142-
if (view != "HTML") {
143-
content += "<html><head>" +
144-
"<style>:root { color-scheme: var(--color-scheme, light); } @media (prefers-color-scheme: dark) { :root { --color-scheme: dark; } }</style>" +
145-
"</head><body><pre>"
146-
content += report.replace("\t", " ").htmlEncode()
147-
content += "</pre></body></html>"
148-
} else {
149-
content+=report
150-
}
151-
reportDetailBinding.reportDetail.loadDataWithBaseURL(null, content, "text/html", "utf-8", null)
152-
}.subscribe())
126+
lifecycleScope.launch {
127+
val content = withContext(Dispatchers.IO) {
128+
val reportObject = activityListener.getReportViewModel().getReport(id)
129+
val report: String = Core.convertReport(reportObject.report, view)
130+
var content = ""
131+
if (view != "HTML") {
132+
content += "<html><head>" +
133+
"<style>:root { color-scheme: var(--color-scheme, light); } @media (prefers-color-scheme: dark) { :root { --color-scheme: dark; } }</style>" +
134+
"</head><body><pre>"
135+
content += report.replace("\t", " ").htmlEncode()
136+
content += "</pre></body></html>"
137+
} else {
138+
content+=report
139+
}
140+
content
141+
}
142+
reportDetailBinding.reportDetail.loadDataWithBaseURL(null, content, "text/html", "utf-8", null)
143+
}
153144
}
154145

155146
return reportDetailBinding.root

0 commit comments

Comments
 (0)