Skip to content

Commit b5be3b0

Browse files
criticalAYColby Cabrera
andcommitted
feat(shared-decks): redesign download screen in Compose
Co-Authored-By: Colby Cabrera <colbycabrera.wd@gmail.com>
1 parent 0af4614 commit b5be3b0

11 files changed

Lines changed: 1324 additions & 379 deletions

File tree

.idea/codeStyles/Project.xml

Lines changed: 0 additions & 22 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// SPDX-FileCopyrightText: 2026 Ashish Yadav <mailtoashish693@gmail.com>
2+
// SPDX-License-Identifier: GPL-3.0-or-later
3+
4+
package com.ichi2.anki.shareddeck
5+
6+
import android.text.format.Formatter
7+
import androidx.compose.runtime.Composable
8+
import androidx.compose.runtime.remember
9+
import androidx.compose.ui.platform.LocalConfiguration
10+
import androidx.compose.ui.platform.LocalContext
11+
import androidx.compose.ui.res.stringResource
12+
import com.ichi2.anki.R
13+
14+
@Composable
15+
internal fun rememberDownloadSizeRange(
16+
downloadedBytes: Long,
17+
totalBytes: Long,
18+
): String {
19+
if (totalBytes <= 0 && downloadedBytes <= 0) return ""
20+
21+
val context = LocalContext.current
22+
val configuration = LocalConfiguration.current
23+
24+
val downloadedFmt =
25+
remember(downloadedBytes, configuration) {
26+
Formatter.formatFileSize(context, downloadedBytes)
27+
}
28+
29+
if (totalBytes <= 0) return downloadedFmt
30+
31+
val totalFmt =
32+
remember(totalBytes, configuration) {
33+
Formatter.formatFileSize(context, totalBytes)
34+
}
35+
return stringResource(R.string.progress_amount_bytes, downloadedFmt, totalFmt)
36+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// SPDX-FileCopyrightText: 2026 Colby Cabrera <colbycabrera.wd@gmail.com>
2+
// SPDX-License-Identifier: GPL-3.0-or-later
3+
4+
package com.ichi2.anki.shareddeck
5+
6+
/**
7+
* Calculates download speed using Exponential Moving Average (EMA) to smooth out fluctuations.
8+
*
9+
* @param alpha The smoothing factor. A value between 0 and 1. Higher values give more weight to
10+
* current measurements. Default is 0.5 (averaging current and last speed).
11+
*/
12+
class DownloadSpeedCalculator(
13+
private val alpha: Double = 0.5,
14+
) {
15+
init {
16+
require(alpha in 0.0..1.0) { "Alpha must be between 0.0 and 1.0" }
17+
}
18+
19+
private var lastSmoothedSpeed: Double = 0.0
20+
private var lastBytesDownloaded: Long = 0
21+
private var lastTimeChecked: Long? = null
22+
23+
/**
24+
* Updates the calculation with new data points.
25+
*
26+
* @param downloadedBytes Total bytes downloaded so far.
27+
* @param currentTime Current time in milliseconds.
28+
* @return The smoothed download speed in bytes per second.
29+
*/
30+
fun update(
31+
downloadedBytes: Long,
32+
currentTime: Long,
33+
): Double {
34+
val lastTime = lastTimeChecked
35+
if (lastTime != null && currentTime > lastTime) {
36+
val timeDiff = currentTime - lastTime
37+
val bytesDiff = downloadedBytes - lastBytesDownloaded
38+
39+
if (bytesDiff >= 0) {
40+
val instantSpeed = (bytesDiff * 1000.0) / timeDiff
41+
lastSmoothedSpeed =
42+
if (lastSmoothedSpeed == 0.0 && instantSpeed > 0) {
43+
// Initialize with the first non-zero speed to avoid a slow ramp up from 0.
44+
instantSpeed
45+
} else {
46+
(lastSmoothedSpeed * (1 - alpha)) + (instantSpeed * alpha)
47+
}
48+
}
49+
}
50+
lastBytesDownloaded = downloadedBytes
51+
lastTimeChecked = currentTime
52+
return lastSmoothedSpeed
53+
}
54+
55+
/**
56+
* Resets the internal state of the calculator.
57+
*/
58+
fun reset() {
59+
lastSmoothedSpeed = 0.0
60+
lastBytesDownloaded = 0
61+
lastTimeChecked = null
62+
}
63+
}

AnkiDroid/src/main/java/com/ichi2/anki/shareddeck/SharedDecksActivity.kt

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ package com.ichi2.anki.shareddeck
1818

1919
import android.app.DownloadManager
2020
import android.content.Context
21+
import android.graphics.Color.TRANSPARENT
2122
import android.os.Bundle
23+
import android.util.TypedValue
2224
import android.view.Menu
2325
import android.view.MenuItem
2426
import android.webkit.CookieManager
@@ -225,7 +227,7 @@ class SharedDecksActivity : AnkiActivity(R.layout.activity_shared_decks) {
225227
supportActionBar?.setDisplayHomeAsUpEnabled(true)
226228
supportActionBar?.setDisplayShowHomeEnabled(true)
227229

228-
downloadManager = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
230+
downloadManager = getSystemService(DOWNLOAD_SERVICE) as DownloadManager
229231

230232
binding.webView.settings.javaScriptEnabled = true
231233
binding.webView.loadUrl(resources.getString(R.string.shared_decks_url))
@@ -246,11 +248,24 @@ class SharedDecksActivity : AnkiActivity(R.layout.activity_shared_decks) {
246248
SHARED_DECKS_DOWNLOAD_FRAGMENT,
247249
).addToBackStack(null)
248250
}
251+
supportActionBar?.title = ""
252+
binding.webviewToolbar.setBackgroundColor(TRANSPARENT)
249253
}
250254
}
251255

252256
binding.webView.webViewClient = webViewClient
253257
onBackPressedDispatcher.addCallback(onBackPressedCallback)
258+
259+
supportFragmentManager.addOnBackStackChangedListener {
260+
if (supportFragmentManager.backStackEntryCount == 0) {
261+
// Restore toolbar when returning to the webview
262+
supportActionBar?.setTitle(R.string.download_deck)
263+
264+
val typedValue = TypedValue()
265+
theme.resolveAttribute(R.attr.appBarColor, typedValue, true)
266+
binding.webviewToolbar.setBackgroundColor(typedValue.data)
267+
}
268+
}
254269
}
255270

256271
override fun onCreateOptionsMenu(menu: Menu): Boolean {

0 commit comments

Comments
 (0)