Different list modes

pull/1/head
Admin 6 years ago
parent 2bc19afea3
commit 05b0d34c1f

@ -8,6 +8,7 @@ import org.koin.android.ext.koin.androidLogger
import org.koin.core.context.startKoin import org.koin.core.context.startKoin
import org.koin.dsl.module import org.koin.dsl.module
import org.koitharu.kotatsu.core.db.MangaDatabase import org.koitharu.kotatsu.core.db.MangaDatabase
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.domain.MangaLoaderContext import org.koitharu.kotatsu.domain.MangaLoaderContext
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@ -35,6 +36,10 @@ class KotatsuApp : Application() {
single { single {
mangaDb().build() 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 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
} }

@ -2,41 +2,54 @@ package org.koitharu.kotatsu.ui.common.widgets
import android.content.Context import android.content.Context
import android.util.AttributeSet import android.util.AttributeSet
import android.widget.LinearLayout
import androidx.appcompat.widget.AppCompatImageView import androidx.appcompat.widget.AppCompatImageView
import androidx.core.content.res.use
import moxy.MvpFacade.init
import org.koitharu.kotatsu.R
class CoverImageView @JvmOverloads constructor( class CoverImageView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : AppCompatImageView(context, attrs, defStyleAttr) { ) : AppCompatImageView(context, attrs, defStyleAttr) {
private var orientation: Int = HORIZONTAL
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { init {
val widthMode = MeasureSpec.getMode(widthMeasureSpec) context.theme.obtainStyledAttributes(attrs, R.styleable.CoverImageView, defStyleAttr, 0)
val heightMode = MeasureSpec.getMode(heightMeasureSpec) .use {
if (widthMode == MeasureSpec.UNSPECIFIED) { orientation = it.getInt(R.styleable.CoverImageView_android_orientation, HORIZONTAL)
val originalWidth = MeasureSpec.getSize(widthMeasureSpec) }
super.onMeasure( }
MeasureSpec.makeMeasureSpec(originalWidth, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec( override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
(originalWidth * ASPECT_RATIO_HEIGHT / ASPECT_RATIO_WIDTH).toInt(), if (orientation == VERTICAL) {
MeasureSpec.EXACTLY val originalHeight = MeasureSpec.getSize(heightMeasureSpec)
) super.onMeasure(
) MeasureSpec.makeMeasureSpec(
} else { (originalHeight * ASPECT_RATIO_WIDTH / ASPECT_RATIO_HEIGHT).toInt(),
val originalHeight = MeasureSpec.getSize(heightMeasureSpec) MeasureSpec.EXACTLY
super.onMeasure( ),
MeasureSpec.makeMeasureSpec( MeasureSpec.makeMeasureSpec(originalHeight, MeasureSpec.EXACTLY)
(originalHeight * ASPECT_RATIO_WIDTH / ASPECT_RATIO_HEIGHT).toInt(), )
MeasureSpec.EXACTLY } else {
), val originalWidth = MeasureSpec.getSize(widthMeasureSpec)
MeasureSpec.makeMeasureSpec(originalHeight, MeasureSpec.EXACTLY) super.onMeasure(
) MeasureSpec.makeMeasureSpec(originalWidth, MeasureSpec.EXACTLY),
} MeasureSpec.makeMeasureSpec(
} (originalWidth * ASPECT_RATIO_HEIGHT / ASPECT_RATIO_WIDTH).toInt(),
MeasureSpec.EXACTLY
private companion object { )
)
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 android.view.ViewGroup
import org.koitharu.kotatsu.core.model.Manga import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.prefs.ListMode
import org.koitharu.kotatsu.ui.common.list.BaseRecyclerAdapter import org.koitharu.kotatsu.ui.common.list.BaseRecyclerAdapter
class MangaListAdapter(onItemClickListener: ((Manga) -> Unit)?) : class MangaListAdapter(onItemClickListener: ((Manga) -> Unit)?) :
BaseRecyclerAdapter<Manga>(onItemClickListener) { 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 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 package org.koitharu.kotatsu.ui.main.list
import android.content.SharedPreferences
import android.os.Bundle import android.os.Bundle
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View import android.view.View
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.fragment_list.* import kotlinx.android.synthetic.main.fragment_list.*
@ -11,13 +17,12 @@ import moxy.ktx.moxyPresenter
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.Manga import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.model.MangaSource 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.BaseFragment
import org.koitharu.kotatsu.ui.common.list.PaginationScrollListener import org.koitharu.kotatsu.ui.common.list.PaginationScrollListener
import org.koitharu.kotatsu.ui.common.list.SpacingItemDecoration import org.koitharu.kotatsu.ui.common.list.SpacingItemDecoration
import org.koitharu.kotatsu.ui.main.details.MangaDetailsActivity import org.koitharu.kotatsu.ui.main.details.MangaDetailsActivity
import org.koitharu.kotatsu.utils.ext.getDisplayMessage import org.koitharu.kotatsu.utils.ext.*
import org.koitharu.kotatsu.utils.ext.hasItems
import org.koitharu.kotatsu.utils.ext.withArgs
class MangaListFragment : BaseFragment(R.layout.fragment_list), MangaListView, class MangaListFragment : BaseFragment(R.layout.fragment_list), MangaListView,
PaginationScrollListener.Callback { PaginationScrollListener.Callback {
@ -28,18 +33,28 @@ class MangaListFragment : BaseFragment(R.layout.fragment_list), MangaListView,
private lateinit var adapter: MangaListAdapter private lateinit var adapter: MangaListAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
adapter = MangaListAdapter { adapter = MangaListAdapter {
startActivity(MangaDetailsActivity.newIntent(context ?: return@MangaListAdapter, it)) startActivity(MangaDetailsActivity.newIntent(context ?: return@MangaListAdapter, it))
} }
// recyclerView.addItemDecoration(SpacingItemDecoration(resources.getDimensionPixelOffset(R.dimen.grid_spacing))) initListMode(settings.listMode)
recyclerView.addItemDecoration(DividerItemDecoration(view.context, RecyclerView.VERTICAL))
recyclerView.adapter = adapter recyclerView.adapter = adapter
recyclerView.addOnScrollListener(PaginationScrollListener(4, this)) recyclerView.addOnScrollListener(PaginationScrollListener(4, this))
swipeRefreshLayout.setOnRefreshListener { swipeRefreshLayout.setOnRefreshListener {
presenter.loadList(source, 0) presenter.loadList(source, 0)
} }
settings.subscribe(this)
}
override fun onDestroyView() {
settings.unsubscribe(this)
super.onDestroyView()
} }
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {
@ -47,6 +62,19 @@ class MangaListFragment : BaseFragment(R.layout.fragment_list), MangaListView,
presenter.loadList(source, 0) 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) { override fun onRequestMoreItems(offset: Int) {
presenter.loadList(source, offset) presenter.loadList(source, offset)
} }
@ -72,6 +100,33 @@ class MangaListFragment : BaseFragment(R.layout.fragment_list), MangaListView,
swipeRefreshLayout.isEnabled = !progressBar.isVisible 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 { companion object {
private const val ARG_SOURCE = "provider" 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.isGone
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.chip.Chip import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipGroup import com.google.android.material.chip.ChipGroup
import kotlinx.android.synthetic.main.fragment_list.view.*
import org.koitharu.kotatsu.ui.common.ChipsFactory import org.koitharu.kotatsu.ui.common.ChipsFactory
fun View.hideKeyboard() { fun View.hideKeyboard() {
@ -74,3 +76,18 @@ fun <T> ChipGroup.setChips(data: Iterable<T>, action: ChipsFactory.(T) -> Chip)
addView(chip) 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)
}
}

@ -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>

@ -8,6 +8,7 @@
<org.koitharu.kotatsu.ui.common.widgets.CoverImageView <org.koitharu.kotatsu.ui.common.widgets.CoverImageView
android:id="@+id/imageView_cover" android:id="@+id/imageView_cover"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:orientation="vertical"
android:layout_height="match_parent" /> android:layout_height="match_parent" />
<LinearLayout <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>

@ -2,5 +2,6 @@
<resources> <resources>
<dimen name="grid_spacing">5dp</dimen> <dimen name="grid_spacing">5dp</dimen>
<dimen name="manga_list_item_height">84dp</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> <dimen name="chapter_list_item_height">46dp</dimen>
</resources> </resources>

@ -9,4 +9,8 @@
<string name="network_error">Network connection error</string> <string name="network_error">Network connection error</string>
<string name="details">Details</string> <string name="details">Details</string>
<string name="chapters">Chapters</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> </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…
Cancel
Save