1414 * this program. If not, see <http://www.gnu.org/licenses/>.
1515 */
1616
17- package com.ichi2.anki
17+ package com.ichi2.anki.shareddeck
1818
1919import android.app.DownloadManager
2020import android.content.ActivityNotFoundException
@@ -35,19 +35,26 @@ import androidx.core.content.ContextCompat
3535import androidx.core.content.FileProvider
3636import androidx.core.net.toUri
3737import androidx.fragment.app.Fragment
38+ import androidx.fragment.app.viewModels
39+ import androidx.lifecycle.Lifecycle
40+ import androidx.lifecycle.lifecycleScope
41+ import androidx.lifecycle.repeatOnLifecycle
3842import com.ichi2.anki.CollectionManager.TR
39- import com.ichi2.anki.SharedDecksActivity.Companion.DOWNLOAD_FILE
43+ import com.ichi2.anki.IntentHandler
44+ import com.ichi2.anki.R
4045import com.ichi2.anki.android.AnkiBroadcastReceiver
4146import com.ichi2.anki.common.crashreporting.CrashReportService
4247import com.ichi2.anki.common.utils.android.showThemedToast
4348import com.ichi2.anki.compat.CompatHelper.Companion.getSerializableCompat
4449import com.ichi2.anki.compat.CompatHelper.Companion.registerReceiverCompat
4550import com.ichi2.anki.databinding.FragmentSharedDecksDownloadBinding
51+ import com.ichi2.anki.shareddeck.SharedDecksActivity.Companion.DOWNLOAD_FILE
4652import com.ichi2.anki.snackbar.showSnackbar
4753import com.ichi2.anki.utils.openUrl
4854import com.ichi2.utils.ImportUtils
4955import com.ichi2.utils.create
5056import dev.androidbroadcast.vbpd.viewBinding
57+ import kotlinx.coroutines.launch
5158import timber.log.Timber
5259import java.io.File
5360import java.net.URLConnection
@@ -62,6 +69,7 @@ import kotlin.math.abs
6269 */
6370class SharedDecksDownloadFragment : Fragment (R .layout.fragment_shared_decks_download) {
6471 private val binding by viewBinding(FragmentSharedDecksDownloadBinding ::bind)
72+ private val viewModel: SharedDecksDownloadViewModel by viewModels()
6573
6674 private var downloadId: Long = 0
6775
@@ -145,6 +153,13 @@ class SharedDecksDownloadFragment : Fragment(R.layout.fragment_shared_decks_down
145153 ) {
146154 super .onViewCreated(view, savedInstanceState)
147155
156+ // Single source of truth: render whatever the ViewModel emits.
157+ viewLifecycleOwner.lifecycleScope.launch {
158+ viewLifecycleOwner.repeatOnLifecycle(Lifecycle .State .STARTED ) {
159+ viewModel.uiState.collect(::renderState)
160+ }
161+ }
162+
148163 val fileToBeDownloaded = arguments?.getSerializableCompat<DownloadFile >(DOWNLOAD_FILE )!!
149164 downloadManager = (activity as SharedDecksActivity ).downloadManager
150165
@@ -170,13 +185,26 @@ class SharedDecksDownloadFragment : Fragment(R.layout.fragment_shared_decks_down
170185 binding.tryDownloadAgainButton.setOnClickListener {
171186 Timber .i(" Try again button clicked, retry downloading of deck" )
172187 downloadManager.remove(downloadId)
188+ viewModel.onEvent(SharedDecksDownloadEvent .RetryRequested )
173189 downloadFile(fileToBeDownloaded)
174- binding.cancelDownloadButton.visibility = View .VISIBLE
175- binding.tryDownloadAgainButton.visibility = View .GONE
176- binding.openInWebBrowserButton.visibility = View .GONE
177190 }
178191 }
179192
193+ /* *
194+ * Applies the latest [SharedDecksDownloadUiState] to the XML views.
195+ * The only place that touches `binding.*` for display.
196+ */
197+ private fun renderState (state : SharedDecksDownloadUiState ) {
198+ binding.downloadingTitle.text = state.title
199+ binding.downloadPercentageText.text = state.percentageText
200+ binding.downloadProgressBar.progress = state.progress
201+ binding.checkNetworkInfoText.visibility = if (state.showNetworkError) View .VISIBLE else View .GONE
202+ binding.cancelDownloadButton.visibility = if (state.showCancelButton) View .VISIBLE else View .GONE
203+ binding.importSharedDeckButton.visibility = if (state.showImportButton) View .VISIBLE else View .GONE
204+ binding.tryDownloadAgainButton.visibility = if (state.showTryAgainButton) View .VISIBLE else View .GONE
205+ binding.openInWebBrowserButton.visibility = if (state.showOpenInBrowserButton) View .VISIBLE else View .GONE
206+ }
207+
180208 /* *
181209 * Register broadcast receiver for listening to download completion.
182210 * Set the request for downloading a deck, enqueue it in DownloadManager, store download ID and
@@ -214,7 +242,7 @@ class SharedDecksDownloadFragment : Fragment(R.layout.fragment_shared_decks_down
214242 onBackPressedCallback.isEnabled = isDownloadInProgress
215243 Timber .d(" Download ID -> $downloadId " )
216244 Timber .d(" File name -> $fileName " )
217- binding.downloadingTitle.text = getString(R .string.downloading_file, fileName)
245+ viewModel.onEvent( SharedDecksDownloadEvent . TitleChanged ( getString(R .string.downloading_file, fileName)) )
218246 startDownloadProgressChecker()
219247 }
220248
@@ -326,12 +354,11 @@ class SharedDecksDownloadFragment : Fragment(R.layout.fragment_shared_decks_down
326354
327355 if (isVisible) {
328356 // Setting these since progress checker can stop before progress is updated to represent 100%
329- binding.downloadPercentageText.text = getString(R .string.percentage, DOWNLOAD_COMPLETED_PROGRESS_PERCENTAGE )
330- binding.downloadProgressBar.progress = DOWNLOAD_COMPLETED_PROGRESS_PERCENTAGE .toInt()
331-
332- // Remove cancel button and show import deck button
333- binding.cancelDownloadButton.visibility = View .GONE
334- binding.importSharedDeckButton.visibility = View .VISIBLE
357+ viewModel.onEvent(
358+ SharedDecksDownloadEvent .DownloadCompleted (
359+ percentageText = getString(R .string.percentage, DOWNLOAD_COMPLETED_PROGRESS_PERCENTAGE ),
360+ ),
361+ )
335362 }
336363
337364 Timber .i(" Opening downloaded deck for import" )
@@ -399,8 +426,12 @@ class SharedDecksDownloadFragment : Fragment(R.layout.fragment_shared_decks_down
399426 Timber .d(" Starting download progress checker" )
400427 downloadProgressChecker.run ()
401428 isProgressCheckerRunning = true
402- binding.downloadPercentageText.text = getString(R .string.percentage, DOWNLOAD_STARTED_PROGRESS_PERCENTAGE )
403- binding.downloadProgressBar.progress = DOWNLOAD_STARTED_PROGRESS_PERCENTAGE .toInt()
429+ viewModel.onEvent(
430+ SharedDecksDownloadEvent .ProgressUpdated (
431+ percent = DOWNLOAD_STARTED_PROGRESS_PERCENTAGE .toInt(),
432+ percentageText = getString(R .string.percentage, DOWNLOAD_STARTED_PROGRESS_PERCENTAGE ),
433+ ),
434+ )
404435 }
405436
406437 /* *
@@ -424,8 +455,12 @@ class SharedDecksDownloadFragment : Fragment(R.layout.fragment_shared_decks_down
424455 downloadManager.query(query)
425456 } catch (_: IllegalArgumentException ) {
426457 // 19812: column local_filename is not allowed in queries
427- binding.downloadPercentageText.text = TR .syncDownloadingFromAnkiweb()
428- binding.downloadProgressBar.progress = 0
458+ viewModel.onEvent(
459+ SharedDecksDownloadEvent .ProgressUpdated (
460+ percent = 0 ,
461+ percentageText = TR .syncDownloadingFromAnkiweb(),
462+ ),
463+ )
429464 return
430465 }
431466
@@ -449,8 +484,12 @@ class SharedDecksDownloadFragment : Fragment(R.layout.fragment_shared_decks_down
449484 // Show download progress percentage up to 1 decimal place.
450485 " %.1f" .format(downloadProgress)
451486 }
452- binding.downloadPercentageText.text = getString(R .string.percentage, percentageValue)
453- binding.downloadProgressBar.progress = downloadProgress.toInt()
487+ viewModel.onEvent(
488+ SharedDecksDownloadEvent .ProgressUpdated (
489+ percent = downloadProgress.toInt(),
490+ percentageText = getString(R .string.percentage, percentageValue),
491+ ),
492+ )
454493
455494 val columnIndexForStatus = it.getColumnIndex(DownloadManager .COLUMN_STATUS )
456495 val columnIndexForReason = it.getColumnIndex(DownloadManager .COLUMN_REASON )
@@ -466,13 +505,10 @@ class SharedDecksDownloadFragment : Fragment(R.layout.fragment_shared_decks_down
466505 }
467506
468507 // Display message if download is waiting for network connection
469- if (it.getInt(columnIndexForStatus) == DownloadManager .STATUS_PAUSED &&
470- it.getInt(columnIndexForReason) == DownloadManager .PAUSED_WAITING_FOR_NETWORK
471- ) {
472- binding.checkNetworkInfoText.visibility = View .VISIBLE
473- } else {
474- binding.checkNetworkInfoText.visibility = View .GONE
475- }
508+ val waitingForNetwork =
509+ it.getInt(columnIndexForStatus) == DownloadManager .STATUS_PAUSED &&
510+ it.getInt(columnIndexForReason) == DownloadManager .PAUSED_WAITING_FOR_NETWORK
511+ viewModel.onEvent(SharedDecksDownloadEvent .NetworkErrorChanged (showing = waitingForNetwork))
476512 }
477513 }
478514
@@ -524,12 +560,9 @@ class SharedDecksDownloadFragment : Fragment(R.layout.fragment_shared_decks_down
524560 } else {
525561 Timber .i(" Download failed, update UI and provide option to retry" )
526562 context?.let { showThemedToast(it, R .string.something_wrong, false ) }
527- // Update UI if download could not be successful
528- binding.tryDownloadAgainButton.visibility = View .VISIBLE
529- binding.openInWebBrowserButton.visibility = View .VISIBLE
530- binding.cancelDownloadButton.visibility = View .GONE
531- binding.downloadPercentageText.text = getString(R .string.download_failed)
532- binding.downloadProgressBar.progress = DOWNLOAD_STARTED_PROGRESS_PERCENTAGE .toInt()
563+ viewModel.onEvent(
564+ SharedDecksDownloadEvent .DownloadFailed (failedText = getString(R .string.download_failed)),
565+ )
533566 }
534567 }
535568
0 commit comments