Storage usage preference

pull/440/head
Koitharu 3 years ago
parent 513aa1a285
commit bc273bfb8f
Signed by: Koitharu
GPG Key ID: 676DEE768C17A9D7

@ -13,6 +13,7 @@ disabled_rules = no-wildcard-imports, no-unused-imports
[{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.rng,*.tld,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul}] [{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.rng,*.tld,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul}]
ij_continuation_indent_size = 4 ij_continuation_indent_size = 4
ij_xml_attribute_wrap = on_every_item
[{*.kt,*.kts}] [{*.kt,*.kts}]
ij_kotlin_allow_trailing_comma_on_call_site = true ij_kotlin_allow_trailing_comma_on_call_site = true

@ -69,11 +69,14 @@ fun <I> ActivityResultLauncher<I>.resolve(context: Context, input: I): ResolveIn
return pm.resolveActivity(intent, 0) return pm.resolveActivity(intent, 0)
} }
fun <I> ActivityResultLauncher<I>.tryLaunch(input: I, options: ActivityOptionsCompat? = null): Boolean { fun <I> ActivityResultLauncher<I>.tryLaunch(
return runCatching { input: I,
options: ActivityOptionsCompat? = null,
): Boolean = runCatching {
launch(input, options) launch(input, options)
}.isSuccess }.onFailure { e ->
} e.printStackTraceDebug()
}.isSuccess
fun SharedPreferences.observe() = callbackFlow<String> { fun SharedPreferences.observe() = callbackFlow<String> {
val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, key -> val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, key ->

@ -32,6 +32,7 @@ import org.koitharu.kotatsu.settings.about.AboutSettingsFragment
import org.koitharu.kotatsu.settings.sources.SourceSettingsFragment import org.koitharu.kotatsu.settings.sources.SourceSettingsFragment
import org.koitharu.kotatsu.settings.sources.SourcesListFragment import org.koitharu.kotatsu.settings.sources.SourcesListFragment
import org.koitharu.kotatsu.settings.tracker.TrackerSettingsFragment import org.koitharu.kotatsu.settings.tracker.TrackerSettingsFragment
import org.koitharu.kotatsu.settings.userdata.UserDataSettingsFragment
@AndroidEntryPoint @AndroidEntryPoint
class SettingsActivity : class SettingsActivity :

@ -1,106 +0,0 @@
package org.koitharu.kotatsu.settings.tools
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.CompoundButton
import androidx.core.graphics.Insets
import androidx.core.net.toUri
import androidx.core.view.isVisible
import androidx.core.view.updatePadding
import androidx.fragment.app.viewModels
import dagger.hilt.android.AndroidEntryPoint
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.github.AppVersion
import org.koitharu.kotatsu.core.ui.BaseFragment
import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.core.util.ext.setChecked
import org.koitharu.kotatsu.databinding.FragmentToolsBinding
import org.koitharu.kotatsu.download.ui.list.DownloadsActivity
import org.koitharu.kotatsu.settings.SettingsActivity
import org.koitharu.kotatsu.settings.about.AppUpdateDialog
@AndroidEntryPoint
class ToolsFragment :
BaseFragment<FragmentToolsBinding>(),
CompoundButton.OnCheckedChangeListener,
View.OnClickListener {
private val viewModel by viewModels<ToolsViewModel>()
override fun onCreateViewBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentToolsBinding {
return FragmentToolsBinding.inflate(inflater, container, false)
}
override fun onViewBindingCreated(binding: FragmentToolsBinding, savedInstanceState: Bundle?) {
super.onViewBindingCreated(binding, savedInstanceState)
binding.buttonSettings.setOnClickListener(this)
binding.buttonDownloads.setOnClickListener(this)
binding.cardUpdate.buttonChangelog.setOnClickListener(this)
binding.cardUpdate.buttonDownload.setOnClickListener(this)
binding.switchIncognito.setOnCheckedChangeListener(this)
binding.memoryUsageView.setManageButtonOnClickListener(this)
binding.chart?.setDataChart(
listOf(
Pair(5, "Категория 1"),
Pair(3, "Категория 2"),
Pair(7, "Категория 3"),
)
)
binding.chart?.startAnimation()
viewModel.isIncognitoModeEnabled.observe(viewLifecycleOwner) {
binding.switchIncognito.setChecked(it, false)
}
viewModel.storageUsage.observe(viewLifecycleOwner) {
binding.memoryUsageView.bind(it)
}
viewModel.appUpdate.observe(viewLifecycleOwner, ::onAppUpdateAvailable)
}
override fun onClick(v: View) {
when (v.id) {
R.id.button_settings -> startActivity(SettingsActivity.newIntent(v.context))
R.id.button_manage -> startActivity(SettingsActivity.newHistorySettingsIntent(v.context))
R.id.button_downloads -> startActivity(DownloadsActivity.newIntent(v.context))
R.id.button_download -> {
val url = viewModel.appUpdate.value?.apkUrl ?: return
val intent = Intent(Intent.ACTION_VIEW)
intent.data = url.toUri()
startActivity(Intent.createChooser(intent, getString(R.string.open_in_browser)))
}
R.id.button_changelog -> {
val version = viewModel.appUpdate.value ?: return
AppUpdateDialog(v.context).show(version)
}
}
}
override fun onCheckedChanged(button: CompoundButton?, isChecked: Boolean) {
viewModel.toggleIncognitoMode(isChecked)
}
override fun onWindowInsetsChanged(insets: Insets) {
requireViewBinding().root.updatePadding(
bottom = insets.bottom,
)
}
private fun onAppUpdateAvailable(version: AppVersion?) {
if (version == null) {
requireViewBinding().cardUpdate.root.isVisible = false
return
}
requireViewBinding().cardUpdate.textSecondary.text = getString(R.string.new_version_s, version.name)
requireViewBinding().cardUpdate.root.isVisible = true
}
companion object {
fun newInstance() = ToolsFragment()
}
}

@ -1,68 +0,0 @@
package org.koitharu.kotatsu.settings.tools
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.plus
import org.koitharu.kotatsu.core.github.AppUpdateRepository
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.prefs.observeAsStateFlow
import org.koitharu.kotatsu.core.ui.BaseViewModel
import org.koitharu.kotatsu.local.data.CacheDir
import org.koitharu.kotatsu.local.data.LocalStorageManager
import org.koitharu.kotatsu.settings.tools.model.StorageUsage
import javax.inject.Inject
@HiltViewModel
class ToolsViewModel @Inject constructor(
private val storageManager: LocalStorageManager,
private val settings: AppSettings,
appUpdateRepository: AppUpdateRepository,
) : BaseViewModel() {
val appUpdate = appUpdateRepository.observeAvailableUpdate()
val storageUsage: StateFlow<StorageUsage?> = flow {
emit(collectStorageUsage())
}.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, null)
val isIncognitoModeEnabled = settings.observeAsStateFlow(
scope = viewModelScope + Dispatchers.Default,
key = AppSettings.KEY_INCOGNITO_MODE,
valueProducer = { isIncognitoModeEnabled },
)
fun toggleIncognitoMode(isEnabled: Boolean) {
settings.isIncognitoModeEnabled = isEnabled
}
private suspend fun collectStorageUsage(): StorageUsage {
val pagesCacheSize = storageManager.computeCacheSize(CacheDir.PAGES)
val otherCacheSize = storageManager.computeCacheSize() - pagesCacheSize
val storageSize = storageManager.computeStorageSize()
val availableSpace = storageManager.computeAvailableSize()
val totalBytes = pagesCacheSize + otherCacheSize + storageSize + availableSpace
return StorageUsage(
savedManga = StorageUsage.Item(
bytes = storageSize,
percent = (storageSize.toDouble() / totalBytes).toFloat(),
),
pagesCache = StorageUsage.Item(
bytes = pagesCacheSize,
percent = (pagesCacheSize.toDouble() / totalBytes).toFloat(),
),
otherCache = StorageUsage.Item(
bytes = otherCacheSize,
percent = (otherCacheSize.toDouble() / totalBytes).toFloat(),
),
available = StorageUsage.Item(
bytes = availableSpace,
percent = (availableSpace.toDouble() / totalBytes).toFloat(),
),
)
}
}

@ -1,14 +0,0 @@
package org.koitharu.kotatsu.settings.tools.model
class StorageUsage(
val savedManga: Item,
val pagesCache: Item,
val otherCache: Item,
val available: Item,
) {
class Item(
val bytes: Long,
val percent: Float,
)
}

@ -0,0 +1,51 @@
package org.koitharu.kotatsu.settings.userdata
class StorageUsage(
val savedManga: Item,
val pagesCache: Item,
val otherCache: Item,
val available: Item,
) {
class Item(
val bytes: Long,
val percent: Float,
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Item
if (bytes != other.bytes) return false
return percent == other.percent
}
override fun hashCode(): Int {
var result = bytes.hashCode()
result = 31 * result + percent.hashCode()
return result
}
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as StorageUsage
if (savedManga != other.savedManga) return false
if (pagesCache != other.pagesCache) return false
if (otherCache != other.otherCache) return false
return available == other.available
}
override fun hashCode(): Int {
var result = savedManga.hashCode()
result = 31 * result + pagesCache.hashCode()
result = 31 * result + otherCache.hashCode()
result = 31 * result + available.hashCode()
return result
}
}

@ -1,48 +1,53 @@
package org.koitharu.kotatsu.settings.tools.views package org.koitharu.kotatsu.settings.userdata
import android.content.Context import android.content.Context
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.graphics.Color
import android.util.AttributeSet import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.LinearLayout
import androidx.annotation.AttrRes import androidx.annotation.AttrRes
import androidx.annotation.ColorInt import androidx.annotation.ColorInt
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.core.content.ContextCompat
import androidx.core.graphics.ColorUtils import androidx.core.graphics.ColorUtils
import androidx.core.widget.TextViewCompat import androidx.core.widget.TextViewCompat
import androidx.preference.Preference
import androidx.preference.PreferenceViewHolder
import com.google.android.material.color.MaterialColors import com.google.android.material.color.MaterialColors
import okio.ByteString.Companion.decodeHex import kotlinx.coroutines.flow.FlowCollector
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.ui.widgets.SegmentedBarView import org.koitharu.kotatsu.core.ui.widgets.SegmentedBarView
import org.koitharu.kotatsu.core.util.FileSize import org.koitharu.kotatsu.core.util.FileSize
import org.koitharu.kotatsu.core.util.ext.getThemeColor import org.koitharu.kotatsu.core.util.ext.getThemeColor
import org.koitharu.kotatsu.databinding.LayoutMemoryUsageBinding import org.koitharu.kotatsu.databinding.PreferenceMemoryUsageBinding
import org.koitharu.kotatsu.settings.tools.model.StorageUsage import com.google.android.material.R as materialR
class StorageUsagePreference @JvmOverloads constructor(
class MemoryUsageView @JvmOverloads constructor(
context: Context, context: Context,
attrs: AttributeSet? = null attrs: AttributeSet? = null,
) : LinearLayout(context, attrs) { ) : Preference(context, attrs), FlowCollector<StorageUsage?> {
private val binding = LayoutMemoryUsageBinding.inflate(LayoutInflater.from(context), this)
private val labelPattern = context.getString(R.string.memory_usage_pattern) private val labelPattern = context.getString(R.string.memory_usage_pattern)
private var usage: StorageUsage? = null
init { init {
orientation = VERTICAL layoutResource = R.layout.preference_memory_usage
bind(null) isSelectable = false
} isPersistent = false
fun setManageButtonOnClickListener(listener: OnClickListener?) {
binding.buttonManage.setOnClickListener(listener)
} }
fun bind(usage: StorageUsage?) { override fun onBindViewHolder(holder: PreferenceViewHolder) {
val storageSegment = SegmentedBarView.Segment(usage?.savedManga?.percent ?: 0f, segmentColor(com.google.android.material.R.attr.colorPrimary)) super.onBindViewHolder(holder)
val pagesSegment = SegmentedBarView.Segment(usage?.pagesCache?.percent ?: 0f, segmentColor(com.google.android.material.R.attr.colorOnPrimaryContainer)) val binding = PreferenceMemoryUsageBinding.bind(holder.itemView)
val otherSegment = SegmentedBarView.Segment(usage?.otherCache?.percent ?: 0f, segmentColor(com.google.android.material.R.attr.colorTertiary)) val storageSegment = SegmentedBarView.Segment(
usage?.savedManga?.percent ?: 0f,
segmentColor(materialR.attr.colorPrimary),
)
val pagesSegment = SegmentedBarView.Segment(
usage?.pagesCache?.percent ?: 0f,
segmentColor(materialR.attr.colorSecondary),
)
val otherSegment = SegmentedBarView.Segment(
usage?.otherCache?.percent ?: 0f,
segmentColor(materialR.attr.colorTertiary),
)
with(binding) { with(binding) {
bar.animateSegments(listOf(storageSegment, pagesSegment, otherSegment).filter { it.percent > 0f }) bar.animateSegments(listOf(storageSegment, pagesSegment, otherSegment).filter { it.percent > 0f })
@ -57,6 +62,11 @@ class MemoryUsageView @JvmOverloads constructor(
} }
} }
override suspend fun emit(value: StorageUsage?) {
usage = value
notifyChanged()
}
private fun formatLabel( private fun formatLabel(
item: StorageUsage.Item?, item: StorageUsage.Item?,
@StringRes labelResId: Int, @StringRes labelResId: Int,
@ -91,7 +101,7 @@ class MemoryUsageView @JvmOverloads constructor(
val colorHex = String.format("%06x", context.getThemeColor(resId)) val colorHex = String.format("%06x", context.getThemeColor(resId))
val hue = getHue(colorHex) val hue = getHue(colorHex)
val color = ColorUtils.HSLToColor(floatArrayOf(hue, 0.5f, 0.5f)) val color = ColorUtils.HSLToColor(floatArrayOf(hue, 0.5f, 0.5f))
val backgroundColor = context.getThemeColor(com.google.android.material.R.attr.colorSurfaceContainerHigh) val backgroundColor = context.getThemeColor(materialR.attr.colorSurfaceContainerHigh)
return MaterialColors.harmonize(color, backgroundColor) return MaterialColors.harmonize(color, backgroundColor)
} }
} }

@ -1,6 +1,5 @@
package org.koitharu.kotatsu.settings package org.koitharu.kotatsu.settings.userdata
import android.content.ActivityNotFoundException
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
import android.net.Uri import android.net.Uri
@ -28,7 +27,7 @@ import org.koitharu.kotatsu.core.ui.util.ReversibleActionObserver
import org.koitharu.kotatsu.core.util.FileSize import org.koitharu.kotatsu.core.util.FileSize
import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.core.util.ext.observeEvent import org.koitharu.kotatsu.core.util.ext.observeEvent
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug import org.koitharu.kotatsu.core.util.ext.tryLaunch
import org.koitharu.kotatsu.local.data.CacheDir import org.koitharu.kotatsu.local.data.CacheDir
import org.koitharu.kotatsu.settings.backup.BackupDialogFragment import org.koitharu.kotatsu.settings.backup.BackupDialogFragment
import org.koitharu.kotatsu.settings.backup.RestoreDialogFragment import org.koitharu.kotatsu.settings.backup.RestoreDialogFragment
@ -76,6 +75,9 @@ class UserDataSettingsFragment : BasePreferenceFragment(R.string.data_and_privac
pref.summary = pref.context.resources.getQuantityString(R.plurals.items, it, it) pref.summary = pref.context.resources.getQuantityString(R.plurals.items, it, it)
} }
} }
findPreference<StorageUsagePreference>("storage_usage")?.let { pref ->
viewModel.storageUsage.observe(viewLifecycleOwner, pref)
}
viewModel.loadingKeys.observe(viewLifecycleOwner) { keys -> viewModel.loadingKeys.observe(viewLifecycleOwner) { keys ->
preferenceScreen.forEach { pref -> preferenceScreen.forEach { pref ->
pref.isEnabled = pref.key !in keys pref.isEnabled = pref.key !in keys
@ -129,10 +131,7 @@ class UserDataSettingsFragment : BasePreferenceFragment(R.string.data_and_privac
} }
AppSettings.KEY_RESTORE -> { AppSettings.KEY_RESTORE -> {
try { if (!backupSelectCall.tryLaunch(arrayOf("*/*"))) {
backupSelectCall.launch(arrayOf("*/*"))
} catch (e: ActivityNotFoundException) {
e.printStackTraceDebug()
Snackbar.make( Snackbar.make(
listView, R.string.operation_not_supported, Snackbar.LENGTH_SHORT, listView, R.string.operation_not_supported, Snackbar.LENGTH_SHORT,
).show() ).show()

@ -1,7 +1,9 @@
package org.koitharu.kotatsu.settings package org.koitharu.kotatsu.settings.userdata
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.runInterruptible import kotlinx.coroutines.runInterruptible
@ -36,6 +38,9 @@ class UserDataSettingsViewModel @Inject constructor(
val feedItemsCount = MutableStateFlow(-1) val feedItemsCount = MutableStateFlow(-1)
val httpCacheSize = MutableStateFlow(-1L) val httpCacheSize = MutableStateFlow(-1L)
val cacheSizes = EnumMap<CacheDir, MutableStateFlow<Long>>(CacheDir::class.java) val cacheSizes = EnumMap<CacheDir, MutableStateFlow<Long>>(CacheDir::class.java)
val storageUsage = MutableStateFlow<StorageUsage?>(null)
private var storageUsageJob: Job? = null
init { init {
CacheDir.values().forEach { CacheDir.values().forEach {
@ -55,6 +60,7 @@ class UserDataSettingsViewModel @Inject constructor(
launchJob(Dispatchers.Default) { launchJob(Dispatchers.Default) {
httpCacheSize.value = runInterruptible { httpCache.size() } httpCacheSize.value = runInterruptible { httpCache.size() }
} }
loadStorageUsage()
} }
fun clearCache(key: String, cache: CacheDir) { fun clearCache(key: String, cache: CacheDir) {
@ -63,6 +69,7 @@ class UserDataSettingsViewModel @Inject constructor(
loadingKeys.update { it + key } loadingKeys.update { it + key }
storageManager.clearCache(cache) storageManager.clearCache(cache)
checkNotNull(cacheSizes[cache]).value = storageManager.computeCacheSize(cache) checkNotNull(cacheSizes[cache]).value = storageManager.computeCacheSize(cache)
loadStorageUsage()
} finally { } finally {
loadingKeys.update { it - key } loadingKeys.update { it - key }
} }
@ -78,6 +85,7 @@ class UserDataSettingsViewModel @Inject constructor(
httpCache.size() httpCache.size()
} }
httpCacheSize.value = size httpCacheSize.value = size
loadStorageUsage()
} finally { } finally {
loadingKeys.update { it - AppSettings.KEY_HTTP_CACHE_CLEAR } loadingKeys.update { it - AppSettings.KEY_HTTP_CACHE_CLEAR }
} }
@ -106,4 +114,34 @@ class UserDataSettingsViewModel @Inject constructor(
onActionDone.call(ReversibleAction(R.string.updates_feed_cleared, null)) onActionDone.call(ReversibleAction(R.string.updates_feed_cleared, null))
} }
} }
private fun loadStorageUsage() {
val prevJob = storageUsageJob
storageUsageJob = launchJob(Dispatchers.Default) {
prevJob?.cancelAndJoin()
val pagesCacheSize = storageManager.computeCacheSize(CacheDir.PAGES)
val otherCacheSize = storageManager.computeCacheSize() - pagesCacheSize
val storageSize = storageManager.computeStorageSize()
val availableSpace = storageManager.computeAvailableSize()
val totalBytes = pagesCacheSize + otherCacheSize + storageSize + availableSpace
storageUsage.value = StorageUsage(
savedManga = StorageUsage.Item(
bytes = storageSize,
percent = (storageSize.toDouble() / totalBytes).toFloat(),
),
pagesCache = StorageUsage.Item(
bytes = pagesCacheSize,
percent = (pagesCacheSize.toDouble() / totalBytes).toFloat(),
),
otherCache = StorageUsage.Item(
bytes = otherCacheSize,
percent = (otherCacheSize.toDouble() / totalBytes).toFloat(),
),
available = StorageUsage.Item(
bytes = availableSpace,
percent = (availableSpace.toDouble() / totalBytes).toFloat(),
),
)
}
}
} }

@ -1,88 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />
<include
android:id="@+id/card_update"
layout="@layout/layout_app_update"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="@dimen/margin_normal"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/guideline"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible" />
<org.koitharu.kotatsu.settings.tools.views.MemoryUsageView
android:id="@+id/memory_usage_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toStartOf="@id/guideline"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/switch_incognito"
style="?listItemTextViewStyle"
android:layout_width="0dp"
android:layout_height="?android:listPreferredItemHeightSmall"
android:layout_marginTop="2dp"
android:drawableStart="@drawable/ic_incognito"
android:drawablePadding="?android:listPreferredItemPaddingStart"
android:paddingStart="?android:listPreferredItemPaddingStart"
android:paddingEnd="?android:listPreferredItemPaddingEnd"
android:text="@string/incognito_mode"
android:textAppearance="?attr/textAppearanceButton"
android:textColor="?attr/colorControlNormal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/guideline"
app:layout_constraintTop_toBottomOf="@id/card_update"
app:layout_goneMarginTop="8dp" />
<org.koitharu.kotatsu.core.ui.widgets.ListItemTextView
android:id="@+id/button_downloads"
android:layout_width="0dp"
android:layout_height="?android:listPreferredItemHeightSmall"
android:drawableStart="@drawable/ic_download"
android:drawablePadding="?android:listPreferredItemPaddingStart"
android:paddingStart="?android:listPreferredItemPaddingStart"
android:paddingEnd="?android:listPreferredItemPaddingEnd"
android:text="@string/downloads"
android:textAppearance="?attr/textAppearanceButton"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/guideline"
app:layout_constraintTop_toBottomOf="@id/switch_incognito" />
<org.koitharu.kotatsu.core.ui.widgets.ListItemTextView
android:id="@+id/button_settings"
android:layout_width="0dp"
android:layout_height="?android:listPreferredItemHeightSmall"
android:drawableStart="@drawable/ic_settings"
android:drawablePadding="?android:listPreferredItemPaddingStart"
android:paddingStart="?android:listPreferredItemPaddingStart"
android:paddingEnd="?android:listPreferredItemPaddingEnd"
android:text="@string/settings"
android:textAppearance="?attr/textAppearanceButton"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/guideline"
app:layout_constraintTop_toBottomOf="@id/button_downloads" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>

@ -1,121 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<include
android:id="@+id/card_update"
layout="@layout/layout_app_update"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/margin_normal"
android:layout_marginTop="@dimen/margin_normal"
android:visibility="gone"
tools:visibility="visible" />
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/margin_normal"
android:layout_marginTop="@dimen/margin_normal"
app:cardBackgroundColor="?attr/colorSurfaceContainerLow"
app:cardCornerRadius="21dp">
<org.koitharu.kotatsu.core.ui.widgets.PieChart
android:id="@+id/chart"
android:layout_width="wrap_content"
android:layout_height="100dp"
android:layout_marginVertical="16dp"
android:layout_marginHorizontal="8dp"
app:pieChartCircleSectionSpace="6"
app:pieChartCircleStrokeWidth="18dp"
app:pieChartColors="@array/chart_colors"
app:pieChartTextAmount="Всего"
app:pieChartTextAmountColor="?attr/colorControlNormal"
app:pieChartTextDescriptionColor="?android:attr/textColorHint"
app:pieChartTextDescriptionSize="14sp"
app:pieChartTextNumberColor="?attr/colorControlNormal"
app:pieChartTextNumberSize="16sp" />
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/margin_normal"
android:layout_marginTop="@dimen/margin_normal"
app:cardBackgroundColor="?attr/colorSurfaceContainerLow"
app:cardCornerRadius="21dp">
<org.koitharu.kotatsu.settings.tools.views.MemoryUsageView
android:id="@+id/memory_usage_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/margin_normal"
android:layout_marginTop="@dimen/margin_normal"
app:cardBackgroundColor="?attr/colorSurfaceContainerLow"
app:cardCornerRadius="21dp"
app:contentPaddingBottom="8dp"
app:contentPaddingTop="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/switch_incognito"
style="?listItemTextViewStyle"
android:layout_width="match_parent"
android:layout_height="?android:listPreferredItemHeightSmall"
android:drawableStart="@drawable/ic_incognito"
android:drawablePadding="?android:listPreferredItemPaddingStart"
android:paddingStart="?android:listPreferredItemPaddingStart"
android:paddingEnd="?android:listPreferredItemPaddingEnd"
android:text="@string/incognito_mode"
android:textAppearance="?attr/textAppearanceButton"
android:textColor="?attr/colorControlNormal" />
<org.koitharu.kotatsu.core.ui.widgets.ListItemTextView
android:id="@+id/button_downloads"
android:layout_width="match_parent"
android:layout_height="?android:listPreferredItemHeightSmall"
android:drawableStart="@drawable/ic_download"
android:drawablePadding="?android:listPreferredItemPaddingStart"
android:paddingStart="?android:listPreferredItemPaddingStart"
android:paddingEnd="?android:listPreferredItemPaddingEnd"
android:text="@string/downloads"
android:textAppearance="?attr/textAppearanceButton" />
<org.koitharu.kotatsu.core.ui.widgets.ListItemTextView
android:id="@+id/button_settings"
android:layout_width="match_parent"
android:layout_height="?android:listPreferredItemHeightSmall"
android:drawableStart="@drawable/ic_settings"
android:drawablePadding="?android:listPreferredItemPaddingStart"
android:paddingStart="?android:listPreferredItemPaddingStart"
android:paddingEnd="?android:listPreferredItemPaddingEnd"
android:text="@string/settings"
android:textAppearance="?attr/textAppearanceButton" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
</androidx.core.widget.NestedScrollView>

@ -1,42 +1,19 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<merge <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
tools:ignore="RtlSymmetry" android:orientation="vertical"
tools:orientation="vertical" tools:ignore="RtlSymmetry">
tools:parentTag="android.widget.LinearLayout">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/screen_padding"
android:layout_marginTop="4dp">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/storage_usage"
android:textAppearance="?textAppearanceTitleMedium" />
<Button
android:id="@+id/button_manage"
style="@style/Widget.Material3.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:text="@string/manage" />
</LinearLayout>
<org.koitharu.kotatsu.core.ui.widgets.SegmentedBarView <org.koitharu.kotatsu.core.ui.widgets.SegmentedBarView
android:id="@+id/bar" android:id="@+id/bar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="18dp" android:layout_height="18dp"
android:layout_marginStart="@dimen/screen_padding" android:layout_marginStart="@dimen/screen_padding"
android:layout_marginTop="@dimen/screen_padding"
android:layout_marginEnd="@dimen/screen_padding" android:layout_marginEnd="@dimen/screen_padding"
android:background="?colorSecondaryContainer" /> android:background="?colorSecondaryContainer" />
@ -89,4 +66,4 @@
app:drawableStartCompat="@drawable/bg_rounded_square" app:drawableStartCompat="@drawable/bg_rounded_square"
app:drawableTint="?colorSecondaryContainer" /> app:drawableTint="?colorSecondaryContainer" />
</merge> </LinearLayout>

@ -27,7 +27,7 @@
android:title="@string/network" /> android:title="@string/network" />
<PreferenceScreen <PreferenceScreen
android:fragment="org.koitharu.kotatsu.settings.UserDataSettingsFragment" android:fragment="org.koitharu.kotatsu.settings.userdata.UserDataSettingsFragment"
android:icon="@drawable/ic_data_privacy" android:icon="@drawable/ic_data_privacy"
android:key="userdata" android:key="userdata"
android:title="@string/data_and_privacy" /> android:title="@string/data_and_privacy" />

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen <PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"> xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<SwitchPreferenceCompat <SwitchPreferenceCompat
android:key="protect_app" android:key="protect_app"
@ -35,13 +36,16 @@
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory android:title="@string/data_deletion"> <PreferenceCategory android:title="@string/storage_usage">
<org.koitharu.kotatsu.settings.userdata.StorageUsagePreference android:key="storage_usage" />
<Preference <Preference
android:key="search_history_clear" android:key="search_history_clear"
android:persistent="false" android:persistent="false"
android:summary="@string/loading_" android:summary="@string/loading_"
android:title="@string/clear_search_history" /> android:title="@string/clear_search_history"
app:allowDividerAbove="true" />
<Preference <Preference
android:key="updates_feed_clear" android:key="updates_feed_clear"

Loading…
Cancel
Save