Local manga info dialog
parent
1e19f32fc5
commit
3cf2c58058
@ -0,0 +1,41 @@
|
||||
package org.koitharu.kotatsu.core.util
|
||||
|
||||
import android.content.Context
|
||||
import androidx.annotation.AttrRes
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import com.google.android.material.R
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import org.koitharu.kotatsu.core.util.ext.getThemeColor
|
||||
import kotlin.math.absoluteValue
|
||||
|
||||
object Colors {
|
||||
|
||||
@ColorInt
|
||||
fun segmentColor(context: Context, @AttrRes resId: Int): Int {
|
||||
val colorHex = String.format("%06x", context.getThemeColor(resId))
|
||||
val hue = getHue(colorHex)
|
||||
val color = ColorUtils.HSLToColor(floatArrayOf(hue, 0.5f, 0.5f))
|
||||
val backgroundColor = context.getThemeColor(R.attr.colorSurfaceContainerHigh)
|
||||
return MaterialColors.harmonize(color, backgroundColor)
|
||||
}
|
||||
|
||||
fun random(seed: Any): Int {
|
||||
val hue = (seed.hashCode() % 360).absoluteValue.toFloat()
|
||||
return ColorUtils.HSLToColor(floatArrayOf(hue, 0.5f, 0.5f))
|
||||
}
|
||||
|
||||
private fun getHue(hex: String): Float {
|
||||
val r = (hex.substring(0, 2).toInt(16)).toFloat()
|
||||
val g = (hex.substring(2, 4).toInt(16)).toFloat()
|
||||
val b = (hex.substring(4, 6).toInt(16)).toFloat()
|
||||
|
||||
var hue = 0F
|
||||
if ((r >= g) && (g >= b)) {
|
||||
hue = 60 * (g - b) / (r - b)
|
||||
} else if ((g > r) && (r >= b)) {
|
||||
hue = 60 * (2 - (r - b) / (g - b))
|
||||
}
|
||||
return hue
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,100 @@
|
||||
package org.koitharu.kotatsu.local.ui.info
|
||||
|
||||
import android.content.res.ColorStateList
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.AttrRes
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import androidx.core.widget.TextViewCompat
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.fragment.app.viewModels
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga
|
||||
import org.koitharu.kotatsu.core.ui.AlertDialogFragment
|
||||
import org.koitharu.kotatsu.core.ui.widgets.SegmentedBarView
|
||||
import org.koitharu.kotatsu.core.util.Colors
|
||||
import org.koitharu.kotatsu.core.util.FileSize
|
||||
import org.koitharu.kotatsu.core.util.ext.combine
|
||||
import org.koitharu.kotatsu.core.util.ext.getThemeColor
|
||||
import org.koitharu.kotatsu.core.util.ext.observe
|
||||
import org.koitharu.kotatsu.core.util.ext.showDistinct
|
||||
import org.koitharu.kotatsu.core.util.ext.withArgs
|
||||
import org.koitharu.kotatsu.databinding.DialogLocalInfoBinding
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.settings.userdata.StorageUsage
|
||||
import com.google.android.material.R as materialR
|
||||
|
||||
@AndroidEntryPoint
|
||||
class LocalInfoDialog : AlertDialogFragment<DialogLocalInfoBinding>() {
|
||||
|
||||
private val viewModel: LocalInfoViewModel by viewModels()
|
||||
|
||||
override fun onBuildDialog(builder: MaterialAlertDialogBuilder): MaterialAlertDialogBuilder {
|
||||
return super.onBuildDialog(builder)
|
||||
.setTitle(R.string.saved_manga)
|
||||
.setNegativeButton(R.string.close, null)
|
||||
}
|
||||
|
||||
override fun onCreateViewBinding(inflater: LayoutInflater, container: ViewGroup?): DialogLocalInfoBinding {
|
||||
return DialogLocalInfoBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun onViewBindingCreated(binding: DialogLocalInfoBinding, savedInstanceState: Bundle?) {
|
||||
super.onViewBindingCreated(binding, savedInstanceState)
|
||||
viewModel.path.observe(this) {
|
||||
binding.textViewPath.text = it
|
||||
}
|
||||
combine(viewModel.size, viewModel.availableSize, ::Pair).observe(this) {
|
||||
if (it.first >= 0 && it.second >= 0) {
|
||||
setSegments(it.first, it.second)
|
||||
} else {
|
||||
binding.barView.animateSegments(emptyList())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setSegments(size: Long, available: Long) {
|
||||
val view = viewBinding?.barView ?: return
|
||||
val total = size + available
|
||||
val segment = SegmentedBarView.Segment(
|
||||
percent = (size.toDouble() / total.toDouble()).toFloat(),
|
||||
color = Colors.segmentColor(view.context, materialR.attr.colorPrimary),
|
||||
)
|
||||
requireViewBinding().labelUsed.text = view.context.getString(
|
||||
R.string.memory_usage_pattern,
|
||||
getString(R.string.this_manga),
|
||||
FileSize.BYTES.format(view.context, size),
|
||||
)
|
||||
requireViewBinding().labelAvailable.text = view.context.getString(
|
||||
R.string.memory_usage_pattern,
|
||||
getString(R.string.available),
|
||||
FileSize.BYTES.format(view.context, available),
|
||||
)
|
||||
TextViewCompat.setCompoundDrawableTintList(
|
||||
requireViewBinding().labelUsed,
|
||||
ColorStateList.valueOf(segment.color),
|
||||
)
|
||||
view.animateSegments(listOf(segment))
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
const val ARG_MANGA = "manga"
|
||||
private const val TAG = "LocalInfoDialog"
|
||||
|
||||
fun show(fm: FragmentManager, manga: Manga) {
|
||||
LocalInfoDialog().withArgs(1) {
|
||||
putParcelable(ARG_MANGA, ParcelableManga(manga))
|
||||
}.showDistinct(fm, TAG)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
package org.koitharu.kotatsu.local.ui.info
|
||||
|
||||
import androidx.core.net.toFile
|
||||
import androidx.core.net.toUri
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga
|
||||
import org.koitharu.kotatsu.core.ui.BaseViewModel
|
||||
import org.koitharu.kotatsu.core.ui.widgets.SegmentedBarView
|
||||
import org.koitharu.kotatsu.core.util.ext.computeSize
|
||||
import org.koitharu.kotatsu.core.util.ext.require
|
||||
import org.koitharu.kotatsu.core.util.ext.toFileOrNull
|
||||
import org.koitharu.kotatsu.local.data.LocalMangaRepository
|
||||
import org.koitharu.kotatsu.local.data.LocalStorageManager
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class LocalInfoViewModel @Inject constructor(
|
||||
savedStateHandle: SavedStateHandle,
|
||||
private val localMangaRepository: LocalMangaRepository,
|
||||
private val storageManager: LocalStorageManager,
|
||||
) : BaseViewModel() {
|
||||
|
||||
private val manga = savedStateHandle.require<ParcelableManga>(LocalInfoDialog.ARG_MANGA).manga
|
||||
|
||||
val path = MutableStateFlow<String?>(null)
|
||||
val size = MutableStateFlow(-1L)
|
||||
val availableSize = MutableStateFlow(-1L)
|
||||
|
||||
init {
|
||||
launchLoadingJob(Dispatchers.Default) {
|
||||
val file = manga.url.toUri().toFileOrNull() ?: localMangaRepository.findSavedManga(manga)?.file
|
||||
requireNotNull(file)
|
||||
path.value = file.path
|
||||
size.value = file.computeSize()
|
||||
availableSize.value = storageManager.computeAvailableSize()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
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="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="?dialogPreferredPadding">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_path_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:text="@string/location"
|
||||
android:textAppearance="?textAppearanceLabelMedium" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_path"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:textAppearance="?textAppearanceBodyMedium"
|
||||
tools:text="/storage/emulated/0/Manga/lorem.cbz" />
|
||||
|
||||
<org.koitharu.kotatsu.core.ui.widgets.SegmentedBarView
|
||||
android:id="@+id/barView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="18dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:background="?colorSecondaryContainer" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/label_used"
|
||||
style="@style/Widget.Kotatsu.TextView.Indicator"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/margin_normal"
|
||||
android:text="@string/this_manga"
|
||||
app:drawableStartCompat="@drawable/bg_rounded_square"
|
||||
tools:drawableTint="?colorPrimary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/label_available"
|
||||
style="@style/Widget.Kotatsu.TextView.Indicator"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/margin_small"
|
||||
android:text="@string/available"
|
||||
app:drawableStartCompat="@drawable/bg_rounded_square"
|
||||
app:drawableTint="?colorSecondaryContainer" />
|
||||
|
||||
</LinearLayout>
|
||||
Loading…
Reference in New Issue