瀏覽代碼

Add basic storage usage info to "Data and storage" settings screen

arkon 1 年之前
父節點
當前提交
cb8ea5eab0

+ 39 - 4
app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt

@@ -4,12 +4,15 @@ import android.content.ActivityNotFoundException
 import android.content.Context
 import android.content.Intent
 import android.net.Uri
+import android.os.Environment
+import android.text.format.Formatter
 import android.widget.Toast
 import androidx.activity.compose.rememberLauncherForActivityResult
 import androidx.activity.result.contract.ActivityResultContracts
 import androidx.annotation.StringRes
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.lazy.rememberLazyListState
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.verticalScroll
@@ -32,6 +35,8 @@ import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.res.stringResource
 import eu.kanade.presentation.more.settings.Preference
+import eu.kanade.presentation.more.settings.widget.BasePreferenceWidget
+import eu.kanade.presentation.more.settings.widget.PrefsHorizontalPadding
 import eu.kanade.presentation.permissions.PermissionRequestHelper
 import eu.kanade.presentation.util.relativeTimeSpanString
 import eu.kanade.tachiyomi.R
@@ -41,6 +46,7 @@ import eu.kanade.tachiyomi.data.backup.BackupFileValidator
 import eu.kanade.tachiyomi.data.backup.BackupRestoreJob
 import eu.kanade.tachiyomi.data.backup.models.Backup
 import eu.kanade.tachiyomi.data.cache.ChapterCache
+import eu.kanade.tachiyomi.util.storage.DiskUtil
 import eu.kanade.tachiyomi.util.system.DeviceUtil
 import eu.kanade.tachiyomi.util.system.copyToClipboard
 import eu.kanade.tachiyomi.util.system.toast
@@ -373,22 +379,24 @@ object SettingsDataScreen : SearchableSettings {
         val libraryPreferences = remember { Injekt.get<LibraryPreferences>() }
 
         val chapterCache = remember { Injekt.get<ChapterCache>() }
-        var readableSizeSema by remember { mutableIntStateOf(0) }
-        val readableSize = remember(readableSizeSema) { chapterCache.readableSize }
+        var cacheReadableSizeSema by remember { mutableIntStateOf(0) }
+        val cacheReadableSize = remember(cacheReadableSizeSema) { chapterCache.readableSize }
 
         return Preference.PreferenceGroup(
             title = stringResource(R.string.label_data),
             preferenceItems = listOf(
+                getStorageInfoPref(cacheReadableSize),
+
                 Preference.PreferenceItem.TextPreference(
                     title = stringResource(R.string.pref_clear_chapter_cache),
-                    subtitle = stringResource(R.string.used_cache, readableSize),
+                    subtitle = stringResource(R.string.used_cache, cacheReadableSize),
                     onClick = {
                         scope.launchNonCancellable {
                             try {
                                 val deletedFiles = chapterCache.clear()
                                 withUIContext {
                                     context.toast(context.getString(R.string.cache_deleted, deletedFiles))
-                                    readableSizeSema++
+                                    cacheReadableSizeSema++
                                 }
                             } catch (e: Throwable) {
                                 logcat(LogPriority.ERROR, e)
@@ -404,6 +412,33 @@ object SettingsDataScreen : SearchableSettings {
             ),
         )
     }
+
+    @Composable
+    fun getStorageInfoPref(
+        chapterCacheReadableSize: String,
+    ): Preference.PreferenceItem.CustomPreference {
+        val context = LocalContext.current
+        val available = remember {
+            Formatter.formatFileSize(context, DiskUtil.getAvailableStorageSpace(Environment.getDataDirectory()))
+        }
+        val total = remember {
+            Formatter.formatFileSize(context, DiskUtil.getTotalStorageSpace(Environment.getDataDirectory()))
+        }
+
+        return Preference.PreferenceItem.CustomPreference(
+            title = stringResource(R.string.pref_storage_usage),
+        ) {
+            BasePreferenceWidget(
+                title = stringResource(R.string.pref_storage_usage),
+                subcomponent = {
+                    // TODO: downloads, SD cards, bar representation?, i18n
+                    Box(modifier = Modifier.padding(horizontal = PrefsHorizontalPadding)) {
+                        Text(text = "Available: $available / $total (chapter cache: $chapterCacheReadableSize)")
+                    }
+                },
+            )
+        }
+    }
 }
 
 private data class MissingRestoreComponents(

+ 24 - 0
core/src/main/java/eu/kanade/tachiyomi/util/storage/DiskUtil.kt

@@ -28,6 +28,30 @@ object DiskUtil {
         return size
     }
 
+    /**
+     * Gets the total space for the disk that a file path points to, in bytes.
+     */
+    fun getTotalStorageSpace(file: File): Long {
+        return try {
+            val stat = StatFs(file.absolutePath)
+            stat.blockCountLong * stat.blockSizeLong
+        } catch (_: Exception) {
+            -1L
+        }
+    }
+
+    /**
+     * Gets the available space for the disk that a file path points to, in bytes.
+     */
+    fun getAvailableStorageSpace(file: File): Long {
+        return try {
+            val stat = StatFs(file.absolutePath)
+            stat.availableBlocksLong * stat.blockSizeLong
+        } catch (_: Exception) {
+            -1L
+        }
+    }
+
     /**
      * Gets the available space for the disk that a file path points to, in bytes.
      */

+ 1 - 1
core/src/main/java/tachiyomi/core/preference/Preference.kt

@@ -35,7 +35,7 @@ interface Preference<T> {
 
         /**
          * A preference used for internal app state that isn't really a user preference
-         * and therefore should not be inplaces like backips.
+         * and therefore should not be in places like backups.
          */
         fun isAppState(key: String): Boolean {
             return key.startsWith(APP_STATE_PREFIX)

+ 7 - 6
i18n/src/main/res/values/strings.xml

@@ -506,6 +506,13 @@
     <string name="restoring_backup_canceled">Canceled restore</string>
     <string name="backup_info">You should keep copies of backups in other places as well. Backups may contain sensitive data including any stored passwords; be careful if sharing.</string>
     <string name="last_auto_backup_info">Last automatically backed up: %s</string>
+    <string name="label_data">Data</string>
+    <string name="pref_storage_usage">Storage usage</string>
+    <string name="pref_clear_chapter_cache">Clear chapter cache</string>
+    <string name="used_cache">Used: %1$s</string>
+    <string name="cache_deleted">Cache cleared. %1$d files have been deleted</string>
+    <string name="cache_delete_error">Error occurred while clearing</string>
+    <string name="pref_auto_clear_chapter_cache">Clear chapter cache on app launch</string>
 
     <!-- Sync section -->
     <string name="syncing_library">Syncing library</string>
@@ -521,12 +528,6 @@
     <string name="pref_reset_user_agent_string">Reset default user agent string</string>
     <string name="requires_app_restart">Requires app restart to take effect</string>
     <string name="cookies_cleared">Cookies cleared</string>
-    <string name="label_data">Data</string>
-    <string name="pref_clear_chapter_cache">Clear chapter cache</string>
-    <string name="used_cache">Used: %1$s</string>
-    <string name="cache_deleted">Cache cleared. %1$d files have been deleted</string>
-    <string name="cache_delete_error">Error occurred while clearing</string>
-    <string name="pref_auto_clear_chapter_cache">Clear chapter cache on app launch</string>
     <string name="pref_invalidate_download_cache">Invalidate downloads index</string>
     <string name="pref_invalidate_download_cache_summary">Force app to recheck downloaded chapters</string>
     <string name="download_cache_invalidated">Downloads index invalidated</string>