diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt index 1f21a3261..c62c6c12a 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt +++ b/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() } val chapterCache = remember { Injekt.get() } - 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( diff --git a/core/src/main/java/eu/kanade/tachiyomi/util/storage/DiskUtil.kt b/core/src/main/java/eu/kanade/tachiyomi/util/storage/DiskUtil.kt index acddaf0bd..5ea61b5a5 100644 --- a/core/src/main/java/eu/kanade/tachiyomi/util/storage/DiskUtil.kt +++ b/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. */ diff --git a/core/src/main/java/tachiyomi/core/preference/Preference.kt b/core/src/main/java/tachiyomi/core/preference/Preference.kt index bd95798ef..1cc0d3d3c 100644 --- a/core/src/main/java/tachiyomi/core/preference/Preference.kt +++ b/core/src/main/java/tachiyomi/core/preference/Preference.kt @@ -35,7 +35,7 @@ interface Preference { /** * 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) diff --git a/i18n/src/main/res/values/strings.xml b/i18n/src/main/res/values/strings.xml index 6e33f1f7f..11df5d701 100644 --- a/i18n/src/main/res/values/strings.xml +++ b/i18n/src/main/res/values/strings.xml @@ -506,6 +506,13 @@ Canceled restore You should keep copies of backups in other places as well. Backups may contain sensitive data including any stored passwords; be careful if sharing. Last automatically backed up: %s + Data + Storage usage + Clear chapter cache + Used: %1$s + Cache cleared. %1$d files have been deleted + Error occurred while clearing + Clear chapter cache on app launch Syncing library @@ -521,12 +528,6 @@ Reset default user agent string Requires app restart to take effect Cookies cleared - Data - Clear chapter cache - Used: %1$s - Cache cleared. %1$d files have been deleted - Error occurred while clearing - Clear chapter cache on app launch Invalidate downloads index Force app to recheck downloaded chapters Downloads index invalidated