Different list modes
This commit is contained in:
@@ -8,6 +8,7 @@ import org.koin.android.ext.koin.androidLogger
|
||||
import org.koin.core.context.startKoin
|
||||
import org.koin.dsl.module
|
||||
import org.koitharu.kotatsu.core.db.MangaDatabase
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.domain.MangaLoaderContext
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
@@ -35,6 +36,10 @@ class KotatsuApp : Application() {
|
||||
single {
|
||||
mangaDb().build()
|
||||
}
|
||||
}, module {
|
||||
factory {
|
||||
AppSettings(applicationContext)
|
||||
}
|
||||
}
|
||||
))
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Parcelable
|
||||
import androidx.annotation.IdRes
|
||||
import androidx.annotation.LayoutRes
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
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.StringArgumentDelegate
|
||||
|
||||
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 <T : Parcelable> arg(name: String) = ParcelableArgumentDelegate<T>(name)
|
||||
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) = Unit
|
||||
}
|
||||
@@ -2,41 +2,54 @@ package org.koitharu.kotatsu.ui.common.widgets
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.widget.LinearLayout
|
||||
import androidx.appcompat.widget.AppCompatImageView
|
||||
import androidx.core.content.res.use
|
||||
import moxy.MvpFacade.init
|
||||
import org.koitharu.kotatsu.R
|
||||
|
||||
|
||||
class CoverImageView @JvmOverloads constructor(
|
||||
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
|
||||
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
|
||||
) : AppCompatImageView(context, attrs, defStyleAttr) {
|
||||
|
||||
private var orientation: Int = HORIZONTAL
|
||||
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
val widthMode = MeasureSpec.getMode(widthMeasureSpec)
|
||||
val heightMode = MeasureSpec.getMode(heightMeasureSpec)
|
||||
if (widthMode == MeasureSpec.UNSPECIFIED) {
|
||||
val originalWidth = MeasureSpec.getSize(widthMeasureSpec)
|
||||
super.onMeasure(
|
||||
MeasureSpec.makeMeasureSpec(originalWidth, MeasureSpec.EXACTLY),
|
||||
MeasureSpec.makeMeasureSpec(
|
||||
(originalWidth * ASPECT_RATIO_HEIGHT / ASPECT_RATIO_WIDTH).toInt(),
|
||||
MeasureSpec.EXACTLY
|
||||
)
|
||||
)
|
||||
} else {
|
||||
val originalHeight = MeasureSpec.getSize(heightMeasureSpec)
|
||||
super.onMeasure(
|
||||
MeasureSpec.makeMeasureSpec(
|
||||
(originalHeight * ASPECT_RATIO_WIDTH / ASPECT_RATIO_HEIGHT).toInt(),
|
||||
MeasureSpec.EXACTLY
|
||||
),
|
||||
MeasureSpec.makeMeasureSpec(originalHeight, MeasureSpec.EXACTLY)
|
||||
)
|
||||
}
|
||||
}
|
||||
init {
|
||||
context.theme.obtainStyledAttributes(attrs, R.styleable.CoverImageView, defStyleAttr, 0)
|
||||
.use {
|
||||
orientation = it.getInt(R.styleable.CoverImageView_android_orientation, HORIZONTAL)
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
if (orientation == VERTICAL) {
|
||||
val originalHeight = MeasureSpec.getSize(heightMeasureSpec)
|
||||
super.onMeasure(
|
||||
MeasureSpec.makeMeasureSpec(
|
||||
(originalHeight * ASPECT_RATIO_WIDTH / ASPECT_RATIO_HEIGHT).toInt(),
|
||||
MeasureSpec.EXACTLY
|
||||
),
|
||||
MeasureSpec.makeMeasureSpec(originalHeight, MeasureSpec.EXACTLY)
|
||||
)
|
||||
} else {
|
||||
val originalWidth = MeasureSpec.getSize(widthMeasureSpec)
|
||||
super.onMeasure(
|
||||
MeasureSpec.makeMeasureSpec(originalWidth, MeasureSpec.EXACTLY),
|
||||
MeasureSpec.makeMeasureSpec(
|
||||
(originalWidth * ASPECT_RATIO_HEIGHT / ASPECT_RATIO_WIDTH).toInt(),
|
||||
MeasureSpec.EXACTLY
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const val ASPECT_RATIO_HEIGHT = 18f
|
||||
const val ASPECT_RATIO_WIDTH = 13f
|
||||
}
|
||||
companion object {
|
||||
|
||||
const val VERTICAL = LinearLayout.VERTICAL
|
||||
const val HORIZONTAL = LinearLayout.HORIZONTAL
|
||||
|
||||
private const val ASPECT_RATIO_HEIGHT = 18f
|
||||
private const val ASPECT_RATIO_WIDTH = 13f
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,19 @@ package org.koitharu.kotatsu.ui.main.list
|
||||
|
||||
import android.view.ViewGroup
|
||||
import org.koitharu.kotatsu.core.model.Manga
|
||||
import org.koitharu.kotatsu.core.prefs.ListMode
|
||||
import org.koitharu.kotatsu.ui.common.list.BaseRecyclerAdapter
|
||||
|
||||
class MangaListAdapter(onItemClickListener: ((Manga) -> Unit)?) :
|
||||
BaseRecyclerAdapter<Manga>(onItemClickListener) {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup) = MangaListHolder(parent)
|
||||
var listMode: ListMode = ListMode.LIST
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup) = when(listMode) {
|
||||
ListMode.LIST -> MangaListHolder(parent)
|
||||
ListMode.DETAILED_LIST -> MangaListDetailsHolder(parent)
|
||||
ListMode.GRID -> MangaGridHolder(parent)
|
||||
}
|
||||
|
||||
override fun onGetItemId(item: Manga) = item.id
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,15 @@
|
||||
package org.koitharu.kotatsu.ui.main.list
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import kotlinx.android.synthetic.main.fragment_list.*
|
||||
@@ -11,13 +17,12 @@ import moxy.ktx.moxyPresenter
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.model.Manga
|
||||
import org.koitharu.kotatsu.core.model.MangaSource
|
||||
import org.koitharu.kotatsu.core.prefs.ListMode
|
||||
import org.koitharu.kotatsu.ui.common.BaseFragment
|
||||
import org.koitharu.kotatsu.ui.common.list.PaginationScrollListener
|
||||
import org.koitharu.kotatsu.ui.common.list.SpacingItemDecoration
|
||||
import org.koitharu.kotatsu.ui.main.details.MangaDetailsActivity
|
||||
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
||||
import org.koitharu.kotatsu.utils.ext.hasItems
|
||||
import org.koitharu.kotatsu.utils.ext.withArgs
|
||||
import org.koitharu.kotatsu.utils.ext.*
|
||||
|
||||
class MangaListFragment : BaseFragment(R.layout.fragment_list), MangaListView,
|
||||
PaginationScrollListener.Callback {
|
||||
@@ -28,18 +33,28 @@ class MangaListFragment : BaseFragment(R.layout.fragment_list), MangaListView,
|
||||
|
||||
private lateinit var adapter: MangaListAdapter
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setHasOptionsMenu(true)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
adapter = MangaListAdapter {
|
||||
startActivity(MangaDetailsActivity.newIntent(context ?: return@MangaListAdapter, it))
|
||||
}
|
||||
// recyclerView.addItemDecoration(SpacingItemDecoration(resources.getDimensionPixelOffset(R.dimen.grid_spacing)))
|
||||
recyclerView.addItemDecoration(DividerItemDecoration(view.context, RecyclerView.VERTICAL))
|
||||
initListMode(settings.listMode)
|
||||
recyclerView.adapter = adapter
|
||||
recyclerView.addOnScrollListener(PaginationScrollListener(4, this))
|
||||
swipeRefreshLayout.setOnRefreshListener {
|
||||
presenter.loadList(source, 0)
|
||||
}
|
||||
settings.subscribe(this)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
settings.unsubscribe(this)
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
@@ -47,6 +62,19 @@ class MangaListFragment : BaseFragment(R.layout.fragment_list), MangaListView,
|
||||
presenter.loadList(source, 0)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.opt_list, menu)
|
||||
super.onCreateOptionsMenu(menu, inflater)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem) = when(item.itemId) {
|
||||
R.id.action_list_mode -> {
|
||||
ListModeSelectDialog.show(childFragmentManager)
|
||||
true
|
||||
}
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
override fun onRequestMoreItems(offset: Int) {
|
||||
presenter.loadList(source, offset)
|
||||
}
|
||||
@@ -72,6 +100,33 @@ class MangaListFragment : BaseFragment(R.layout.fragment_list), MangaListView,
|
||||
swipeRefreshLayout.isEnabled = !progressBar.isVisible
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
|
||||
when(key) {
|
||||
getString(R.string.key_list_mode) -> initListMode(settings.listMode)
|
||||
}
|
||||
}
|
||||
|
||||
private fun initListMode(mode: ListMode) {
|
||||
val ctx = context ?: return
|
||||
val position = recyclerView.firstItem
|
||||
recyclerView.adapter = null
|
||||
recyclerView.layoutManager = null
|
||||
recyclerView.clearItemDecorations()
|
||||
adapter.listMode = mode
|
||||
recyclerView.layoutManager = when(mode) {
|
||||
ListMode.GRID -> GridLayoutManager(ctx, 3)
|
||||
else -> LinearLayoutManager(ctx)
|
||||
}
|
||||
recyclerView.adapter = adapter
|
||||
recyclerView.addItemDecoration(when(mode) {
|
||||
ListMode.DETAILED_LIST,
|
||||
ListMode.LIST -> DividerItemDecoration(ctx, RecyclerView.VERTICAL)
|
||||
ListMode.GRID -> SpacingItemDecoration(resources.getDimensionPixelOffset(R.dimen.grid_spacing))
|
||||
})
|
||||
adapter.notifyDataSetChanged()
|
||||
recyclerView.firstItem = position
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val ARG_SOURCE = "provider"
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,9 +12,11 @@ import androidx.annotation.LayoutRes
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.chip.Chip
|
||||
import com.google.android.material.chip.ChipGroup
|
||||
import kotlinx.android.synthetic.main.fragment_list.view.*
|
||||
import org.koitharu.kotatsu.ui.common.ChipsFactory
|
||||
|
||||
fun View.hideKeyboard() {
|
||||
@@ -73,4 +75,19 @@ fun <T> ChipGroup.setChips(data: Iterable<T>, action: ChipsFactory.(T) -> Chip)
|
||||
val chip = factory.action(it)
|
||||
addView(chip)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun RecyclerView.clearItemDecorations() {
|
||||
while (itemDecorationCount > 0) {
|
||||
removeItemDecorationAt(0)
|
||||
}
|
||||
}
|
||||
|
||||
var RecyclerView.firstItem: Int
|
||||
get() = (layoutManager as? LinearLayoutManager)?.findFirstVisibleItemPosition()
|
||||
?: RecyclerView.NO_POSITION
|
||||
set(value) {
|
||||
if (value != RecyclerView.NO_POSITION) {
|
||||
(layoutManager as? LinearLayoutManager)?.scrollToPositionWithOffset(value, 0)
|
||||
}
|
||||
}
|
||||
12
app/src/main/res/drawable/ic_grid.xml
Normal file
12
app/src/main/res/drawable/ic_grid.xml
Normal file
@@ -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>
|
||||
12
app/src/main/res/drawable/ic_list.xml
Normal file
12
app/src/main/res/drawable/ic_list.xml
Normal file
@@ -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>
|
||||
12
app/src/main/res/drawable/ic_list_detailed.xml
Normal file
12
app/src/main/res/drawable/ic_list_detailed.xml
Normal file
@@ -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>
|
||||
51
app/src/main/res/layout/dialog_list_mode.xml
Normal file
51
app/src/main/res/layout/dialog_list_mode.xml
Normal file
@@ -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>
|
||||
@@ -8,6 +8,7 @@
|
||||
<org.koitharu.kotatsu.ui.common.widgets.CoverImageView
|
||||
android:id="@+id/imageView_cover"
|
||||
android:layout_width="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<LinearLayout
|
||||
|
||||
43
app/src/main/res/layout/item_manga_list_details.xml
Normal file
43
app/src/main/res/layout/item_manga_list_details.xml
Normal file
@@ -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>
|
||||
11
app/src/main/res/menu/opt_list.xml
Normal file
11
app/src/main/res/menu/opt_list.xml
Normal file
@@ -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>
|
||||
6
app/src/main/res/values/attrs.xml
Normal file
6
app/src/main/res/values/attrs.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<declare-styleable name="CoverImageView">
|
||||
<attr name="android:orientation" />
|
||||
</declare-styleable>
|
||||
</resources>
|
||||
4
app/src/main/res/values/constants.xml
Normal file
4
app/src/main/res/values/constants.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="key_list_mode">list_mode</string>
|
||||
</resources>
|
||||
@@ -2,5 +2,6 @@
|
||||
<resources>
|
||||
<dimen name="grid_spacing">5dp</dimen>
|
||||
<dimen name="manga_list_item_height">84dp</dimen>
|
||||
<dimen name="manga_list_details_item_height">120dp</dimen>
|
||||
<dimen name="chapter_list_item_height">46dp</dimen>
|
||||
</resources>
|
||||
@@ -9,4 +9,8 @@
|
||||
<string name="network_error">Network connection error</string>
|
||||
<string name="details">Details</string>
|
||||
<string name="chapters">Chapters</string>
|
||||
<string name="list">List</string>
|
||||
<string name="detailed_list">Detailed list</string>
|
||||
<string name="grid">Grid</string>
|
||||
<string name="list_mode">List mode</string>
|
||||
</resources>
|
||||
@@ -1,10 +1,11 @@
|
||||
<resources>
|
||||
<!-- Base application theme. -->
|
||||
<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 name="AppToggleButton" parent="Widget.MaterialComponents.Button.OutlinedButton">
|
||||
<item name="android:checkable">true</item>
|
||||
<item name="android:gravity">center_vertical|start</item>
|
||||
<item name="iconPadding">16dp</item>
|
||||
<item name="android:paddingTop">10dp</item>
|
||||
<item name="android:paddingBottom">10dp</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
11
app/src/main/res/values/themes.xml
Normal file
11
app/src/main/res/values/themes.xml
Normal file
@@ -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>
|
||||
Reference in New Issue
Block a user