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