Manga languages onboarding
parent
cd7d6d7674
commit
8c2bc078e5
@ -0,0 +1,86 @@
|
|||||||
|
package org.koitharu.kotatsu.settings.onboard
|
||||||
|
|
||||||
|
import android.content.DialogInterface
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.fragment.app.FragmentManager
|
||||||
|
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||||
|
import org.koitharu.kotatsu.R
|
||||||
|
import org.koitharu.kotatsu.base.ui.AlertDialogFragment
|
||||||
|
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
||||||
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||||
|
import org.koitharu.kotatsu.databinding.DialogOnboardBinding
|
||||||
|
import org.koitharu.kotatsu.settings.onboard.adapter.SourceLocalesAdapter
|
||||||
|
import org.koitharu.kotatsu.settings.onboard.model.SourceLocale
|
||||||
|
import org.koitharu.kotatsu.utils.ext.observeNotNull
|
||||||
|
import org.koitharu.kotatsu.utils.ext.withArgs
|
||||||
|
|
||||||
|
class OnboardDialogFragment : AlertDialogFragment<DialogOnboardBinding>(),
|
||||||
|
OnListItemClickListener<SourceLocale>, DialogInterface.OnClickListener {
|
||||||
|
|
||||||
|
private val viewModel by viewModel<OnboardViewModel>(mode = LazyThreadSafetyMode.NONE)
|
||||||
|
private var isWelcome: Boolean = false
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
arguments?.run {
|
||||||
|
isWelcome = getBoolean(ARG_WELCOME, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onInflateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
) = DialogOnboardBinding.inflate(inflater, container, false)
|
||||||
|
|
||||||
|
override fun onBuildDialog(builder: AlertDialog.Builder) {
|
||||||
|
builder
|
||||||
|
.setPositiveButton(R.string.done, this)
|
||||||
|
.setCancelable(true)
|
||||||
|
if (isWelcome) {
|
||||||
|
builder.setTitle(R.string.welcome)
|
||||||
|
} else {
|
||||||
|
builder
|
||||||
|
.setTitle(R.string.remote_sources)
|
||||||
|
.setNegativeButton(android.R.string.cancel, this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
val adapter = SourceLocalesAdapter(this)
|
||||||
|
binding.recyclerView.adapter = adapter
|
||||||
|
viewModel.list.observeNotNull(viewLifecycleOwner) {
|
||||||
|
adapter.items = it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onItemClick(item: SourceLocale, view: View) {
|
||||||
|
viewModel.setItemChecked(item.key, !item.isChecked)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClick(dialog: DialogInterface?, which: Int) {
|
||||||
|
when (which) {
|
||||||
|
DialogInterface.BUTTON_POSITIVE -> viewModel.apply()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private const val TAG = "OnboardDialog"
|
||||||
|
private const val ARG_WELCOME = "welcome"
|
||||||
|
|
||||||
|
fun show(fm: FragmentManager) = OnboardDialogFragment().show(fm, TAG)
|
||||||
|
|
||||||
|
fun showWelcome(settings: AppSettings, fm: FragmentManager) {
|
||||||
|
if (!settings.isSourcesSelected) {
|
||||||
|
OnboardDialogFragment().withArgs(1) {
|
||||||
|
putBoolean(ARG_WELCOME, true)
|
||||||
|
}.show(fm, TAG)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,95 @@
|
|||||||
|
package org.koitharu.kotatsu.settings.onboard
|
||||||
|
|
||||||
|
import androidx.collection.ArraySet
|
||||||
|
import androidx.core.os.LocaleListCompat
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import org.koitharu.kotatsu.base.ui.BaseViewModel
|
||||||
|
import org.koitharu.kotatsu.core.model.MangaSource
|
||||||
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||||
|
import org.koitharu.kotatsu.settings.onboard.model.SourceLocale
|
||||||
|
import org.koitharu.kotatsu.utils.ext.map
|
||||||
|
import org.koitharu.kotatsu.utils.ext.mapTo
|
||||||
|
import org.koitharu.kotatsu.utils.ext.mapToSet
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class OnboardViewModel(
|
||||||
|
private val settings: AppSettings,
|
||||||
|
) : BaseViewModel() {
|
||||||
|
|
||||||
|
private val allSources = MangaSource.values().filterNot { x -> x == MangaSource.LOCAL }
|
||||||
|
|
||||||
|
private val locales = allSources.mapTo(ArraySet()) {
|
||||||
|
it.locale
|
||||||
|
}
|
||||||
|
|
||||||
|
private val selectedLocales = locales.toMutableSet()
|
||||||
|
|
||||||
|
val list = MutableLiveData<List<SourceLocale>?>()
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (settings.isSourcesSelected) {
|
||||||
|
selectedLocales.removeAll(settings.hiddenSources.map { x -> MangaSource.valueOf(x).locale })
|
||||||
|
} else {
|
||||||
|
LocaleListCompat.getDefault().mapTo(selectedLocales) { x ->
|
||||||
|
x.language
|
||||||
|
}
|
||||||
|
selectedLocales.retainAll(allSources.map { x -> x.locale })
|
||||||
|
if (selectedLocales.isEmpty()) {
|
||||||
|
selectedLocales += "en"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rebuildList()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setItemChecked(key: String?, isChecked: Boolean) {
|
||||||
|
val isModified = if (isChecked) {
|
||||||
|
selectedLocales.add(key)
|
||||||
|
} else {
|
||||||
|
selectedLocales.remove(key)
|
||||||
|
}
|
||||||
|
if (isModified) {
|
||||||
|
rebuildList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun apply() {
|
||||||
|
settings.hiddenSources = allSources.filterNot { x ->
|
||||||
|
x.locale in selectedLocales
|
||||||
|
}.mapToSet { x -> x.name }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun rebuildList() {
|
||||||
|
list.value = locales.map { key ->
|
||||||
|
val locale = if (key != null) {
|
||||||
|
Locale(key)
|
||||||
|
} else null
|
||||||
|
SourceLocale(
|
||||||
|
key = key,
|
||||||
|
title = locale?.getDisplayLanguage(locale)?.capitalize(locale),
|
||||||
|
isChecked = key in selectedLocales
|
||||||
|
)
|
||||||
|
}.sortedWith(SourceLocaleComparator())
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SourceLocaleComparator : Comparator<SourceLocale> {
|
||||||
|
|
||||||
|
private val deviceLocales = LocaleListCompat.getAdjustedDefault()
|
||||||
|
.map { it.language }
|
||||||
|
|
||||||
|
override fun compare(a: SourceLocale?, b: SourceLocale?): Int {
|
||||||
|
return when {
|
||||||
|
a === b -> 0
|
||||||
|
a?.key == null -> 1
|
||||||
|
b?.key == null -> -1
|
||||||
|
else -> {
|
||||||
|
val index = deviceLocales.indexOf(a.key)
|
||||||
|
if (index == -1) {
|
||||||
|
compareValues(a.title, b.title)
|
||||||
|
} else {
|
||||||
|
-2 - index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
package org.koitharu.kotatsu.settings.onboard.adapter
|
||||||
|
|
||||||
|
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
||||||
|
import org.koitharu.kotatsu.R
|
||||||
|
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
||||||
|
import org.koitharu.kotatsu.databinding.ItemSourceLocaleBinding
|
||||||
|
import org.koitharu.kotatsu.settings.onboard.model.SourceLocale
|
||||||
|
|
||||||
|
fun sourceLocaleAD(
|
||||||
|
clickListener: OnListItemClickListener<SourceLocale>
|
||||||
|
) = adapterDelegateViewBinding<SourceLocale, SourceLocale, ItemSourceLocaleBinding>(
|
||||||
|
{ inflater, parent -> ItemSourceLocaleBinding.inflate(inflater, parent, false) }
|
||||||
|
) {
|
||||||
|
|
||||||
|
binding.root.setOnClickListener {
|
||||||
|
clickListener.onItemClick(item, it)
|
||||||
|
}
|
||||||
|
|
||||||
|
bind {
|
||||||
|
binding.root.text = item.title ?: getString(R.string.other)
|
||||||
|
binding.root.isChecked = item.isChecked
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
package org.koitharu.kotatsu.settings.onboard.adapter
|
||||||
|
|
||||||
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
|
import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter
|
||||||
|
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
||||||
|
import org.koitharu.kotatsu.settings.onboard.model.SourceLocale
|
||||||
|
|
||||||
|
class SourceLocalesAdapter(
|
||||||
|
clickListener: OnListItemClickListener<SourceLocale>,
|
||||||
|
) : AsyncListDifferDelegationAdapter<SourceLocale>(DiffCallback()) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
delegatesManager.addDelegate(sourceLocaleAD(clickListener))
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DiffCallback : DiffUtil.ItemCallback<SourceLocale>() {
|
||||||
|
|
||||||
|
override fun areItemsTheSame(
|
||||||
|
oldItem: SourceLocale,
|
||||||
|
newItem: SourceLocale,
|
||||||
|
): Boolean = oldItem.key == newItem.key
|
||||||
|
|
||||||
|
override fun areContentsTheSame(
|
||||||
|
oldItem: SourceLocale,
|
||||||
|
newItem: SourceLocale,
|
||||||
|
): Boolean = oldItem == newItem
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
package org.koitharu.kotatsu.settings.onboard.model
|
||||||
|
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
data class SourceLocale(
|
||||||
|
val key: String?,
|
||||||
|
val title: String?,
|
||||||
|
val isChecked: Boolean,
|
||||||
|
) : Comparable<SourceLocale> {
|
||||||
|
|
||||||
|
override fun compareTo(other: SourceLocale): Int {
|
||||||
|
return when {
|
||||||
|
this === other -> 0
|
||||||
|
key == Locale.getDefault().language -> -2
|
||||||
|
key == null -> 1
|
||||||
|
other.key == null -> -1
|
||||||
|
else -> compareValues(title, other.title)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
package org.koitharu.kotatsu.utils.ext
|
||||||
|
|
||||||
|
import androidx.core.os.LocaleListCompat
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.collections.ArrayList
|
||||||
|
|
||||||
|
fun LocaleListCompat.toList(): List<Locale> {
|
||||||
|
val list = ArrayList<Locale>(size())
|
||||||
|
for (i in 0 until size()) {
|
||||||
|
list += get(i)
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <R, C : MutableCollection<in R>> LocaleListCompat.mapTo(
|
||||||
|
destination: C,
|
||||||
|
block: (Locale) -> R,
|
||||||
|
): C {
|
||||||
|
val len = size()
|
||||||
|
for (i in 0 until len) {
|
||||||
|
val item = get(i)
|
||||||
|
destination.add(block(item))
|
||||||
|
}
|
||||||
|
return destination
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <T> LocaleListCompat.map(block: (Locale) -> T): List<T> {
|
||||||
|
return mapTo(ArrayList(size()), block)
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
<vector
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="?attr/colorControlNormal"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M12.87,15.07l-2.54,-2.51 0.03,-0.03c1.74,-1.94 2.98,-4.17 3.71,-6.53L17,6L17,4h-7L10,2L8,2v2L1,4v1.99h11.17C11.5,7.92 10.44,9.75 9,11.35 8.07,10.32 7.3,9.19 6.69,8h-2c0.73,1.63 1.73,3.17 2.98,4.56l-5.09,5.02L4,19l5,-5 3.11,3.11 0.76,-2.04zM18.5,10h-2L12,22h2l1.12,-3h4.75L21,22h2l-4.5,-12zM15.88,17l1.62,-4.33L19.12,17h-3.24z" />
|
||||||
|
</vector>
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
<?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">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingStart="?listPreferredItemPaddingStart"
|
||||||
|
android:paddingTop="6dp"
|
||||||
|
android:paddingEnd="?listPreferredItemPaddingEnd"
|
||||||
|
android:paddingBottom="6dp"
|
||||||
|
android:text="Select languages which you want to read manga. You can change it later in settings."
|
||||||
|
android:textAppearance="?android:textAppearanceSmall" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/recycler_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:scrollbars="vertical"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
tools:listitem="@layout/item_source_locale" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<CheckedTextView
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?android:listPreferredItemHeightSmall"
|
||||||
|
android:background="?selectableItemBackground"
|
||||||
|
android:drawableStart="?android:listChoiceIndicatorMultiple"
|
||||||
|
android:drawablePadding="10dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:paddingStart="?listPreferredItemPaddingStart"
|
||||||
|
android:paddingEnd="?listPreferredItemPaddingEnd"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
|
tools:text="@tools:sample/lorem[2]" />
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_languages"
|
||||||
|
android:icon="@drawable/ic_locale"
|
||||||
|
android:title="@string/languages"
|
||||||
|
app:showAsAction="ifRoom" />
|
||||||
|
|
||||||
|
</menu>
|
||||||
Loading…
Reference in New Issue