@@ -13,20 +13,21 @@ import android.content.Intent
1313import android.os.Bundle
1414import android.os.Build
1515
16- import android.view.MenuItem
17-
1816import android.content.res.AssetManager
17+ import android.view.MenuItem
1918import android.view.View
2019import androidx.core.view.ViewCompat
2120import androidx.core.view.WindowCompat
2221import androidx.core.view.WindowInsetsCompat
2322import androidx.core.view.updatePadding
24-
23+ import androidx.lifecycle.Lifecycle
24+ import androidx.lifecycle.lifecycleScope
25+ import androidx.lifecycle.repeatOnLifecycle
2526import androidx.viewpager2.widget.ViewPager2
2627
27- import io.reactivex.disposables.CompositeDisposable
28- import io.reactivex.schedulers.Schedulers
29- import io.reactivex.android.schedulers.AndroidSchedulers
28+ import kotlinx.coroutines.flow.collectLatest
29+ import kotlinx.coroutines.launch
30+
3031import net.mediaarea.mediainfo.databinding.ActivityReportDetailBinding
3132
3233class ReportDetailActivity : AppCompatActivity (), ReportActivityListener {
@@ -35,12 +36,14 @@ class ReportDetailActivity : AppCompatActivity(), ReportActivityListener {
3536 private inner class PageChangeListener (private val reports : List <Report >) : ViewPager2.OnPageChangeCallback() {
3637 override fun onPageSelected (position : Int ) {
3738 super .onPageSelected(position)
38- title = reports.elementAt(position).filename
39- intent.putExtra(Core .ARG_REPORT_ID , reports.elementAt(position).id)
4039
40+ // CRITICAL BOUNDS CHECK: Verify position is valid inside the current data allocation range
41+ if (position >= 0 && position < reports.size) {
42+ title = reports.elementAt(position).filename
43+ intent.putExtra(Core .ARG_REPORT_ID , reports.elementAt(position).id)
44+ }
4145 }
4246 }
43- private val disposable: CompositeDisposable = CompositeDisposable ()
4447 private lateinit var reportModel: ReportViewModel
4548
4649 override fun getReportViewModel (): ReportViewModel {
@@ -96,28 +99,54 @@ class ReportDetailActivity : AppCompatActivity(), ReportActivityListener {
9699 val viewModelFactory = Injection .provideViewModelFactory(this )
97100 reportModel = ViewModelProvider (this , viewModelFactory)[ReportViewModel ::class .java]
98101
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)
105- val id = intent.getIntExtra(Core .ARG_REPORT_ID , - 1 )
106- if (id!= - 1 ) {
107- val index = reports.indexOfFirst { it.id == id }
108- if (index!= - 1 ) {
109- title = reports.elementAt(index).filename
110- activityReportDetailBinding.pager.setCurrentItem(index, false )
102+ // From Google Gemini 3.5 Flash
103+ // 1. Initialize the adapter ONCE
104+ val pagerAdapter = PagerAdapter (this )
105+ activityReportDetailBinding.pager.adapter = pagerAdapter
106+ // 2. Track your active listener reference locally
107+ var pageChangeListener: ViewPager2 .OnPageChangeCallback ? = null
108+ // 3. Collect data changes safely inside the single lifecycle coroutine
109+ lifecycleScope.launch {
110+ repeatOnLifecycle(Lifecycle .State .STARTED ) {
111+ // We use a flag to track if we have already run the initial intent-navigation jump
112+ var isInitialSetupDone = false
113+ reportModel.getAllReports().collectLatest { reports: List <Report > ->
114+ // A. Update the listener with the fresh data list safely without adding a new callback instance
115+ pageChangeListener?.let { oldListener ->
116+ activityReportDetailBinding.pager.unregisterOnPageChangeCallback(oldListener)
117+ }
118+ // CRITICAL: Handle empty state gracefully before configuring UI listeners
119+ if (reports.isEmpty()) {
120+ title = getString(R .string.app_name)
121+ pagerAdapter.submitList(emptyList())
122+ pageChangeListener = null
123+ return @collectLatest // Short-circuit out of this emission safely
124+ }
125+ pageChangeListener = PageChangeListener (reports)
126+ activityReportDetailBinding.pager.registerOnPageChangeCallback(pageChangeListener!! )
127+ // B. Push the new items cleanly into your existing adapter
128+ pagerAdapter.submitList(reports)
129+ // C. Handle the intent navigation token EXACTLY ONCE on initial load
130+ if (! isInitialSetupDone) {
131+ val targetId = intent.getIntExtra(Core .ARG_REPORT_ID , - 1 )
132+ if (targetId != - 1 ) {
133+ val index = reports.indexOfFirst { it.id == targetId }
134+ if (index != - 1 ) {
135+ title = reports[index].filename
136+ activityReportDetailBinding.pager.setCurrentItem(index, false )
137+ }
138+ }
139+ isInitialSetupDone = true // Lock it so subsequent DB updates don't snap the user back
140+ } else {
141+ // Update the title dynamically based on the current visible item during background updates
142+ val currentIndex = activityReportDetailBinding.pager.currentItem
143+ if (currentIndex in reports.indices) {
144+ title = reports[currentIndex].filename
145+ }
111146 }
112147 }
113- })
114- }
115-
116- override fun onStop () {
117- super .onStop()
118-
119- // clear all the subscription
120- disposable.clear()
148+ }
149+ }
121150 }
122151
123152 override fun finish () {
0 commit comments