Add MemoryUsageView

This commit is contained in:
Koitharu
2023-02-12 10:05:05 +02:00
parent 5ce2bc92d6
commit e3a67940d0
6 changed files with 105 additions and 83 deletions

View File

@@ -1,35 +1,24 @@
package org.koitharu.kotatsu.settings.tools package org.koitharu.kotatsu.settings.tools
import android.content.Intent import android.content.Intent
import android.content.res.ColorStateList
import android.os.Bundle import android.os.Bundle
import android.transition.TransitionManager
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.CompoundButton import android.widget.CompoundButton
import androidx.annotation.ColorInt
import androidx.core.graphics.ColorUtils
import androidx.core.graphics.Insets import androidx.core.graphics.Insets
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.view.updatePadding import androidx.core.view.updatePadding
import androidx.core.widget.TextViewCompat
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import com.google.android.material.color.MaterialColors
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseFragment import org.koitharu.kotatsu.base.ui.BaseFragment
import org.koitharu.kotatsu.base.ui.widgets.SegmentedBarView
import org.koitharu.kotatsu.core.github.AppVersion import org.koitharu.kotatsu.core.github.AppVersion
import org.koitharu.kotatsu.databinding.FragmentToolsBinding import org.koitharu.kotatsu.databinding.FragmentToolsBinding
import org.koitharu.kotatsu.download.ui.DownloadsActivity import org.koitharu.kotatsu.download.ui.DownloadsActivity
import org.koitharu.kotatsu.settings.SettingsActivity import org.koitharu.kotatsu.settings.SettingsActivity
import org.koitharu.kotatsu.settings.about.AppUpdateDialog import org.koitharu.kotatsu.settings.about.AppUpdateDialog
import org.koitharu.kotatsu.settings.tools.model.StorageUsage
import org.koitharu.kotatsu.utils.FileSize
import org.koitharu.kotatsu.utils.ext.getThemeColor
import com.google.android.material.R as materialR
@AndroidEntryPoint @AndroidEntryPoint
class ToolsFragment : class ToolsFragment :
@@ -50,11 +39,14 @@ class ToolsFragment :
binding.cardUpdate.buttonChangelog.setOnClickListener(this) binding.cardUpdate.buttonChangelog.setOnClickListener(this)
binding.cardUpdate.buttonDownload.setOnClickListener(this) binding.cardUpdate.buttonDownload.setOnClickListener(this)
binding.switchIncognito.setOnCheckedChangeListener(this) binding.switchIncognito.setOnCheckedChangeListener(this)
binding.memoryUsageView.setManageButtonOnClickListener(this)
viewModel.isIncognitoModeEnabled.observe(viewLifecycleOwner) { viewModel.isIncognitoModeEnabled.observe(viewLifecycleOwner) {
binding.switchIncognito.isChecked = it binding.switchIncognito.isChecked = it
} }
viewModel.storageUsage.observe(viewLifecycleOwner, ::onStorageUsageChanged) viewModel.storageUsage.observe(viewLifecycleOwner) {
binding.memoryUsageView.bind(it)
}
viewModel.appUpdate.observe(viewLifecycleOwner, ::onAppUpdateAvailable) viewModel.appUpdate.observe(viewLifecycleOwner, ::onAppUpdateAvailable)
} }
@@ -96,51 +88,6 @@ class ToolsFragment :
binding.cardUpdate.root.isVisible = true binding.cardUpdate.root.isVisible = true
} }
private fun onStorageUsageChanged(usage: StorageUsage) {
val storageSegment = SegmentedBarView.Segment(usage.savedManga.percent, segmentColor(1))
val pagesSegment = SegmentedBarView.Segment(usage.pagesCache.percent, segmentColor(2))
val otherSegment = SegmentedBarView.Segment(usage.otherCache.percent, segmentColor(3))
with(binding.layoutStorage) {
buttonManage.setOnClickListener(this@ToolsFragment)
bar.segments = listOf(storageSegment, pagesSegment, otherSegment)
val pattern = getString(R.string.memory_usage_pattern)
labelStorage.text = pattern.format(
FileSize.BYTES.format(root.context, usage.savedManga.bytes),
getString(R.string.saved_manga),
)
labelPagesCache.text = pattern.format(
FileSize.BYTES.format(root.context, usage.pagesCache.bytes),
getString(R.string.pages_cache),
)
labelOtherCache.text = pattern.format(
FileSize.BYTES.format(root.context, usage.otherCache.bytes),
getString(R.string.other_cache),
)
labelAvailable.text = pattern.format(
FileSize.BYTES.format(root.context, usage.available.bytes),
getString(R.string.available),
)
TextViewCompat.setCompoundDrawableTintList(labelStorage, ColorStateList.valueOf(storageSegment.color))
TextViewCompat.setCompoundDrawableTintList(labelPagesCache, ColorStateList.valueOf(pagesSegment.color))
TextViewCompat.setCompoundDrawableTintList(labelOtherCache, ColorStateList.valueOf(otherSegment.color))
if (!labelStorage.isVisible) {
TransitionManager.beginDelayedTransition(root)
}
labelStorage.isVisible = true
labelPagesCache.isVisible = true
labelOtherCache.isVisible = true
}
}
@ColorInt
private fun segmentColor(i: Int): Int {
val hue = (93.6f * i) % 360
val color = ColorUtils.HSLToColor(floatArrayOf(hue, 0.4f, 0.6f))
val backgroundColor = requireContext().getThemeColor(materialR.attr.colorSecondaryContainer)
return MaterialColors.harmonize(color, backgroundColor)
}
companion object { companion object {
fun newInstance() = ToolsFragment() fun newInstance() = ToolsFragment()

View File

@@ -5,7 +5,6 @@ import androidx.lifecycle.asLiveData
import androidx.lifecycle.liveData import androidx.lifecycle.liveData
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import org.koitharu.kotatsu.base.ui.BaseViewModel import org.koitharu.kotatsu.base.ui.BaseViewModel
import org.koitharu.kotatsu.core.github.AppUpdateRepository import org.koitharu.kotatsu.core.github.AppUpdateRepository
@@ -14,6 +13,7 @@ import org.koitharu.kotatsu.core.prefs.observeAsLiveData
import org.koitharu.kotatsu.local.data.CacheDir import org.koitharu.kotatsu.local.data.CacheDir
import org.koitharu.kotatsu.local.data.LocalStorageManager import org.koitharu.kotatsu.local.data.LocalStorageManager
import org.koitharu.kotatsu.settings.tools.model.StorageUsage import org.koitharu.kotatsu.settings.tools.model.StorageUsage
import javax.inject.Inject
@HiltViewModel @HiltViewModel
class ToolsViewModel @Inject constructor( class ToolsViewModel @Inject constructor(
@@ -25,7 +25,7 @@ class ToolsViewModel @Inject constructor(
val appUpdate = appUpdateRepository.observeAvailableUpdate() val appUpdate = appUpdateRepository.observeAvailableUpdate()
.asLiveData(viewModelScope.coroutineContext) .asLiveData(viewModelScope.coroutineContext)
val storageUsage: LiveData<StorageUsage> = liveData( val storageUsage: LiveData<StorageUsage?> = liveData(
context = viewModelScope.coroutineContext + Dispatchers.Default, context = viewModelScope.coroutineContext + Dispatchers.Default,
) { ) {
emit(collectStorageUsage()) emit(collectStorageUsage())

View File

@@ -0,0 +1,77 @@
package org.koitharu.kotatsu.settings.tools.views
import android.content.Context
import android.content.res.ColorStateList
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.LinearLayout
import androidx.annotation.ColorInt
import androidx.annotation.StringRes
import androidx.core.graphics.ColorUtils
import androidx.core.widget.TextViewCompat
import com.google.android.material.color.MaterialColors
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.widgets.SegmentedBarView
import org.koitharu.kotatsu.databinding.LayoutMemoryUsageBinding
import org.koitharu.kotatsu.settings.tools.model.StorageUsage
import org.koitharu.kotatsu.utils.FileSize
import org.koitharu.kotatsu.utils.ext.getThemeColor
class MemoryUsageView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) : LinearLayout(context, attrs) {
private val binding = LayoutMemoryUsageBinding.inflate(LayoutInflater.from(context), this)
private val labelPattern = context.getString(R.string.memory_usage_pattern)
init {
orientation = VERTICAL
bind(null)
}
fun setManageButtonOnClickListener(listener: OnClickListener?) {
binding.buttonManage.setOnClickListener(listener)
}
fun bind(usage: StorageUsage?) {
val storageSegment = SegmentedBarView.Segment(usage?.savedManga?.percent ?: 0f, segmentColor(1))
val pagesSegment = SegmentedBarView.Segment(usage?.pagesCache?.percent ?: 0f, segmentColor(2))
val otherSegment = SegmentedBarView.Segment(usage?.otherCache?.percent ?: 0f, segmentColor(3))
with(binding) {
bar.segments = listOf(storageSegment, pagesSegment, otherSegment).filter { it.percent > 0f }
labelStorage.text = formatLabel(usage?.savedManga, R.string.saved_manga)
labelPagesCache.text = formatLabel(usage?.pagesCache, R.string.pages_cache)
labelOtherCache.text = formatLabel(usage?.otherCache, R.string.other_cache)
labelAvailable.text = formatLabel(usage?.available, R.string.available, R.string.available)
TextViewCompat.setCompoundDrawableTintList(labelStorage, ColorStateList.valueOf(storageSegment.color))
TextViewCompat.setCompoundDrawableTintList(labelPagesCache, ColorStateList.valueOf(pagesSegment.color))
TextViewCompat.setCompoundDrawableTintList(labelOtherCache, ColorStateList.valueOf(otherSegment.color))
}
}
private fun formatLabel(
item: StorageUsage.Item?,
@StringRes labelResId: Int,
@StringRes emptyResId: Int = R.string.computing_,
): String {
return if (item != null) {
labelPattern.format(
FileSize.BYTES.format(context, item.bytes),
context.getString(labelResId),
)
} else {
context.getString(emptyResId)
}
}
@ColorInt
private fun segmentColor(i: Int): Int {
val hue = (93.6f * i) % 360
val color = ColorUtils.HSLToColor(floatArrayOf(hue, 0.4f, 0.6f))
val backgroundColor = context.getThemeColor(com.google.android.material.R.attr.colorSecondaryContainer)
return MaterialColors.harmonize(color, backgroundColor)
}
}

View File

@@ -40,9 +40,8 @@
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible" /> tools:visibility="visible" />
<include <org.koitharu.kotatsu.settings.tools.views.MemoryUsageView
android:id="@+id/layout_storage" android:id="@+id/memory_usage_view"
layout="@layout/layout_memory_usage"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_constraintEnd_toStartOf="@id/guideline" app:layout_constraintEnd_toStartOf="@id/guideline"

View File

@@ -34,9 +34,8 @@
android:visibility="gone" android:visibility="gone"
tools:visibility="visible" /> tools:visibility="visible" />
<include <org.koitharu.kotatsu.settings.tools.views.MemoryUsageView
android:id="@+id/layout_storage" android:id="@+id/memory_usage_view"
layout="@layout/layout_memory_usage"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />

View File

@@ -1,19 +1,19 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <merge
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="match_parent" android:layout_height="wrap_content"
android:orientation="vertical" tools:ignore="RtlSymmetry"
android:paddingStart="@dimen/screen_padding" tools:orientation="vertical"
android:paddingTop="@dimen/margin_small" tools:parentTag="android.widget.LinearLayout">
android:paddingBottom="@dimen/screen_padding"
tools:ignore="RtlSymmetry">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
android:layout_marginStart="@dimen/screen_padding"
android:layout_marginTop="@dimen/margin_small">
<TextView <TextView
android:layout_width="0dp" android:layout_width="0dp"
@@ -36,6 +36,7 @@
android:id="@+id/bar" android:id="@+id/bar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="16dp" android:layout_height="16dp"
android:layout_marginStart="@dimen/screen_padding"
android:layout_marginEnd="@dimen/screen_padding" android:layout_marginEnd="@dimen/screen_padding"
android:background="?colorSecondaryContainer" /> android:background="?colorSecondaryContainer" />
@@ -44,49 +45,48 @@
style="@style/Widget.Kotatsu.TextView.Indicator" style="@style/Widget.Kotatsu.TextView.Indicator"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="@dimen/screen_padding"
android:layout_marginTop="@dimen/margin_normal" android:layout_marginTop="@dimen/margin_normal"
android:layout_marginEnd="@dimen/screen_padding" android:layout_marginEnd="@dimen/screen_padding"
android:text="@string/saved_manga" android:text="@string/saved_manga"
android:visibility="gone"
app:drawableStartCompat="@drawable/bg_circle" app:drawableStartCompat="@drawable/bg_circle"
tools:drawableTint="?colorPrimary" tools:drawableTint="?colorPrimary" />
tools:visibility="visible" />
<TextView <TextView
android:id="@+id/label_pages_cache" android:id="@+id/label_pages_cache"
style="@style/Widget.Kotatsu.TextView.Indicator" style="@style/Widget.Kotatsu.TextView.Indicator"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="@dimen/screen_padding"
android:layout_marginTop="@dimen/margin_small" android:layout_marginTop="@dimen/margin_small"
android:layout_marginEnd="@dimen/screen_padding" android:layout_marginEnd="@dimen/screen_padding"
android:text="@string/pages_cache" android:text="@string/pages_cache"
android:visibility="gone"
app:drawableStartCompat="@drawable/bg_circle" app:drawableStartCompat="@drawable/bg_circle"
tools:drawableTint="?colorSecondary" tools:drawableTint="?colorSecondary" />
tools:visibility="visible" />
<TextView <TextView
android:id="@+id/label_other_cache" android:id="@+id/label_other_cache"
style="@style/Widget.Kotatsu.TextView.Indicator" style="@style/Widget.Kotatsu.TextView.Indicator"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="@dimen/screen_padding"
android:layout_marginTop="@dimen/margin_small" android:layout_marginTop="@dimen/margin_small"
android:layout_marginEnd="@dimen/screen_padding" android:layout_marginEnd="@dimen/screen_padding"
android:text="@string/other_cache" android:text="@string/other_cache"
android:visibility="gone"
app:drawableStartCompat="@drawable/bg_circle" app:drawableStartCompat="@drawable/bg_circle"
tools:drawableTint="?colorTertiary" tools:drawableTint="?colorTertiary" />
tools:visibility="visible" />
<TextView <TextView
android:id="@+id/label_available" android:id="@+id/label_available"
style="@style/Widget.Kotatsu.TextView.Indicator" style="@style/Widget.Kotatsu.TextView.Indicator"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="@dimen/screen_padding"
android:layout_marginTop="@dimen/margin_small" android:layout_marginTop="@dimen/margin_small"
android:layout_marginEnd="@dimen/screen_padding" android:layout_marginEnd="@dimen/screen_padding"
android:layout_marginBottom="@dimen/screen_padding"
android:text="@string/computing_" android:text="@string/computing_"
app:drawableStartCompat="@drawable/bg_circle" app:drawableStartCompat="@drawable/bg_circle"
app:drawableTint="?colorSecondaryContainer" /> app:drawableTint="?colorSecondaryContainer" />
</LinearLayout> </merge>