Different list modes
parent
2bc19afea3
commit
05b0d34c1f
@ -0,0 +1,25 @@
|
|||||||
|
package org.koitharu.kotatsu.core.prefs
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import android.content.res.Resources
|
||||||
|
import androidx.core.content.edit
|
||||||
|
import androidx.lifecycle.LifecycleOwner
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import org.koitharu.kotatsu.R
|
||||||
|
import org.koitharu.kotatsu.utils.delegates.prefs.EnumPreferenceDelegate
|
||||||
|
|
||||||
|
class AppSettings private constructor(private val resources: Resources, private val prefs: SharedPreferences) : SharedPreferences by prefs {
|
||||||
|
|
||||||
|
constructor(context: Context) : this(context.resources, PreferenceManager.getDefaultSharedPreferences(context))
|
||||||
|
|
||||||
|
var listMode by EnumPreferenceDelegate(ListMode::class.java, resources.getString(R.string.key_list_mode), ListMode.LIST)
|
||||||
|
|
||||||
|
fun subscribe(listener: SharedPreferences.OnSharedPreferenceChangeListener) {
|
||||||
|
prefs.registerOnSharedPreferenceChangeListener(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun unsubscribe(listener: SharedPreferences.OnSharedPreferenceChangeListener) {
|
||||||
|
prefs.unregisterOnSharedPreferenceChangeListener(listener)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
package org.koitharu.kotatsu.core.prefs
|
||||||
|
|
||||||
|
enum class ListMode(val id: Int) {
|
||||||
|
|
||||||
|
LIST(0),
|
||||||
|
DETAILED_LIST(1),
|
||||||
|
GRID(2);
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
fun valueOf(id: Int) = values().firstOrNull { it.id == id }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,17 +1,24 @@
|
|||||||
package org.koitharu.kotatsu.ui.common
|
package org.koitharu.kotatsu.ui.common
|
||||||
|
|
||||||
|
import android.content.SharedPreferences
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import androidx.annotation.IdRes
|
import androidx.annotation.IdRes
|
||||||
import androidx.annotation.LayoutRes
|
import androidx.annotation.LayoutRes
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import moxy.MvpAppCompatFragment
|
import moxy.MvpAppCompatFragment
|
||||||
|
import org.koin.android.ext.android.inject
|
||||||
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||||
import org.koitharu.kotatsu.utils.delegates.ParcelableArgumentDelegate
|
import org.koitharu.kotatsu.utils.delegates.ParcelableArgumentDelegate
|
||||||
import org.koitharu.kotatsu.utils.delegates.StringArgumentDelegate
|
import org.koitharu.kotatsu.utils.delegates.StringArgumentDelegate
|
||||||
|
|
||||||
abstract class BaseFragment(@LayoutRes contentLayoutId: Int) :
|
abstract class BaseFragment(@LayoutRes contentLayoutId: Int) :
|
||||||
MvpAppCompatFragment(contentLayoutId) {
|
MvpAppCompatFragment(contentLayoutId), SharedPreferences.OnSharedPreferenceChangeListener {
|
||||||
|
|
||||||
|
protected val settings by inject<AppSettings>()
|
||||||
|
|
||||||
fun stringArg(name: String) = StringArgumentDelegate(name)
|
fun stringArg(name: String) = StringArgumentDelegate(name)
|
||||||
|
|
||||||
fun <T : Parcelable> arg(name: String) = ParcelableArgumentDelegate<T>(name)
|
fun <T : Parcelable> arg(name: String) = ParcelableArgumentDelegate<T>(name)
|
||||||
|
|
||||||
|
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) = Unit
|
||||||
}
|
}
|
||||||
@ -0,0 +1,64 @@
|
|||||||
|
package org.koitharu.kotatsu.ui.main.list
|
||||||
|
|
||||||
|
import android.app.Dialog
|
||||||
|
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.DialogFragment
|
||||||
|
import androidx.fragment.app.FragmentManager
|
||||||
|
import kotlinx.android.synthetic.main.dialog_list_mode.*
|
||||||
|
import org.koin.android.ext.android.inject
|
||||||
|
import org.koitharu.kotatsu.R
|
||||||
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||||
|
import org.koitharu.kotatsu.core.prefs.ListMode
|
||||||
|
|
||||||
|
class ListModeSelectDialog : DialogFragment(), View.OnClickListener {
|
||||||
|
|
||||||
|
private val setting by inject<AppSettings>()
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View? {
|
||||||
|
return inflater.inflate(R.layout.dialog_list_mode, container, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
|
return super.onCreateDialog(savedInstanceState).apply {
|
||||||
|
setTitle(R.string.list_mode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
val mode = setting.listMode
|
||||||
|
button_list.isChecked = mode == ListMode.LIST
|
||||||
|
button_list_detailed.isChecked = mode == ListMode.DETAILED_LIST
|
||||||
|
button_grid.isChecked = mode == ListMode.GRID
|
||||||
|
|
||||||
|
button_ok.setOnClickListener(this)
|
||||||
|
button_list.setOnClickListener(this)
|
||||||
|
button_grid.setOnClickListener(this)
|
||||||
|
button_list_detailed.setOnClickListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClick(v: View) {
|
||||||
|
when (v.id) {
|
||||||
|
R.id.button_ok -> dismiss()
|
||||||
|
R.id.button_list -> setting.listMode = ListMode.LIST
|
||||||
|
R.id.button_list_detailed -> setting.listMode = ListMode.DETAILED_LIST
|
||||||
|
R.id.button_grid -> setting.listMode = ListMode.GRID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private const val TAG = "LIST_MODE"
|
||||||
|
|
||||||
|
fun show(fm: FragmentManager) = ListModeSelectDialog().show(fm, TAG)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
package org.koitharu.kotatsu.ui.main.list
|
||||||
|
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import coil.api.load
|
||||||
|
import coil.request.RequestDisposable
|
||||||
|
import kotlinx.android.synthetic.main.item_manga_list_details.*
|
||||||
|
import org.koitharu.kotatsu.R
|
||||||
|
import org.koitharu.kotatsu.core.model.Manga
|
||||||
|
import org.koitharu.kotatsu.ui.common.list.BaseViewHolder
|
||||||
|
import org.koitharu.kotatsu.utils.ext.textAndVisible
|
||||||
|
|
||||||
|
class MangaListDetailsHolder(parent: ViewGroup) : BaseViewHolder<Manga>(parent, R.layout.item_manga_list_details) {
|
||||||
|
|
||||||
|
private var coverRequest: RequestDisposable? = null
|
||||||
|
|
||||||
|
override fun onBind(data: Manga) {
|
||||||
|
coverRequest?.dispose()
|
||||||
|
textView_title.text = data.title
|
||||||
|
textView_subtitle.textAndVisible = data.localizedTitle
|
||||||
|
coverRequest = imageView_cover.load(data.coverUrl) {
|
||||||
|
crossfade(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
package org.koitharu.kotatsu.utils.delegates.prefs
|
||||||
|
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import androidx.core.content.edit
|
||||||
|
import kotlin.properties.ReadWriteProperty
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
class EnumPreferenceDelegate<E : Enum<*>>(
|
||||||
|
private val cls: Class<E>,
|
||||||
|
private val key: String,
|
||||||
|
private val defValue: E
|
||||||
|
) :
|
||||||
|
ReadWriteProperty<SharedPreferences, E> {
|
||||||
|
|
||||||
|
override fun getValue(thisRef: SharedPreferences, property: KProperty<*>): E {
|
||||||
|
val ord = thisRef.getInt(key, -1)
|
||||||
|
if (ord == -1) {
|
||||||
|
return defValue
|
||||||
|
}
|
||||||
|
return cls.enumConstants?.firstOrNull { it.ordinal == ord } ?: defValue
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setValue(thisRef: SharedPreferences, property: KProperty<*>, value: E) {
|
||||||
|
thisRef.edit {
|
||||||
|
putInt(key, value.ordinal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
package org.koitharu.kotatsu.utils.delegates.prefs
|
||||||
|
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import androidx.core.content.edit
|
||||||
|
import kotlin.properties.ReadWriteProperty
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
class NullableStringPreferenceDelegate(private val key: String) : ReadWriteProperty<SharedPreferences, String?> {
|
||||||
|
|
||||||
|
override fun getValue(thisRef: SharedPreferences, property: KProperty<*>): String? {
|
||||||
|
return thisRef.getString(key, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setValue(thisRef: SharedPreferences, property: KProperty<*>, value: String?) {
|
||||||
|
thisRef.edit {
|
||||||
|
putString(key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
package org.koitharu.kotatsu.utils.delegates.prefs
|
||||||
|
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import androidx.core.content.edit
|
||||||
|
import kotlin.properties.ReadWriteProperty
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
class StringPreferenceDelegate(private val key: String, private val defValue: String) :
|
||||||
|
ReadWriteProperty<SharedPreferences, String> {
|
||||||
|
|
||||||
|
override fun getValue(thisRef: SharedPreferences, property: KProperty<*>): String {
|
||||||
|
return thisRef.getString(key, defValue) ?: defValue
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setValue(thisRef: SharedPreferences, property: KProperty<*>, value: String) {
|
||||||
|
thisRef.edit {
|
||||||
|
putString(key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<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="#000"
|
||||||
|
android:pathData="M3,11H11V3H3M3,21H11V13H3M13,21H21V13H13M13,3V11H21V3" />
|
||||||
|
</vector>
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<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="#000"
|
||||||
|
android:pathData="M3,4H7V8H3V4M9,5V7H21V5H9M3,10H7V14H3V10M9,11V13H21V11H9M3,16H7V20H3V16M9,17V19H21V17H9" />
|
||||||
|
</vector>
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<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="#000"
|
||||||
|
android:pathData="M2 14H8V20H2M16 8H10V10H16M2 10H8V4H2M10 4V6H22V4M10 20H16V18H10M10 16H22V14H10" />
|
||||||
|
</vector>
|
||||||
@ -0,0 +1,51 @@
|
|||||||
|
<?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"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButtonToggleGroup
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
app:selectionRequired="true"
|
||||||
|
app:singleSelection="true">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/button_list"
|
||||||
|
style="@style/AppToggleButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/list"
|
||||||
|
app:icon="@drawable/ic_list" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/button_list_detailed"
|
||||||
|
style="@style/AppToggleButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/detailed_list"
|
||||||
|
app:icon="@drawable/ic_list_detailed" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/button_grid"
|
||||||
|
style="@style/AppToggleButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/grid"
|
||||||
|
app:icon="@drawable/ic_grid" />
|
||||||
|
|
||||||
|
</com.google.android.material.button.MaterialButtonToggleGroup>
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/button_ok"
|
||||||
|
style="@style/Widget.MaterialComponents.Button"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:text="@android:string/ok" />
|
||||||
|
</LinearLayout>
|
||||||
@ -0,0 +1,43 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/manga_list_details_item_height"
|
||||||
|
android:background="?selectableItemBackground"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<org.koitharu.kotatsu.ui.common.widgets.CoverImageView
|
||||||
|
android:id="@+id/imageView_cover"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="top"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingTop="12dp"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingEnd="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textView_title"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:lines="2"
|
||||||
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textView_subtitle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:lines="1"
|
||||||
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Body2"
|
||||||
|
android:textColor="?android:textColorSecondary" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
<?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_list_mode"
|
||||||
|
android:title="@string/list_mode"
|
||||||
|
android:orderInCategory="20"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
</menu>
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<declare-styleable name="CoverImageView">
|
||||||
|
<attr name="android:orientation" />
|
||||||
|
</declare-styleable>
|
||||||
|
</resources>
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="key_list_mode">list_mode</string>
|
||||||
|
</resources>
|
||||||
@ -1,10 +1,11 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<!-- Base application theme. -->
|
|
||||||
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar">
|
<style name="AppToggleButton" parent="Widget.MaterialComponents.Button.OutlinedButton">
|
||||||
<!-- Customize your theme here. -->
|
<item name="android:checkable">true</item>
|
||||||
<item name="colorPrimary">@color/colorPrimary</item>
|
<item name="android:gravity">center_vertical|start</item>
|
||||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
<item name="iconPadding">16dp</item>
|
||||||
<item name="colorAccent">@color/colorAccent</item>
|
<item name="android:paddingTop">10dp</item>
|
||||||
|
<item name="android:paddingBottom">10dp</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar">
|
||||||
|
<!-- Customize your theme here. -->
|
||||||
|
<item name="colorPrimary">@color/colorPrimary</item>
|
||||||
|
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||||
|
<item name="colorAccent">@color/colorAccent</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
</resources>
|
||||||
Loading…
Reference in New Issue