Update details activity

This commit is contained in:
Admin
2020-02-02 13:15:47 +02:00
parent 6eea278b4d
commit ef32c49310
13 changed files with 154 additions and 45 deletions

View File

@@ -16,6 +16,9 @@ abstract class HistoryDao {
@Query("SELECT * FROM history ORDER BY :orderBy LIMIT :limit OFFSET :offset") @Query("SELECT * FROM history ORDER BY :orderBy LIMIT :limit OFFSET :offset")
abstract suspend fun getAll(offset: Int, limit: Int, orderBy: String): List<HistoryWithManga> abstract suspend fun getAll(offset: Int, limit: Int, orderBy: String): List<HistoryWithManga>
@Query("SELECT * FROM history WHERE manga_id = :id")
abstract suspend fun getOneOrNull(id: Long): HistoryEntity?
@Query("DELETE FROM history") @Query("DELETE FROM history")
abstract suspend fun clear() abstract suspend fun clear()

View File

@@ -8,6 +8,7 @@ import org.koitharu.kotatsu.core.db.entity.HistoryWithManga
import org.koitharu.kotatsu.core.db.entity.MangaEntity import org.koitharu.kotatsu.core.db.entity.MangaEntity
import org.koitharu.kotatsu.core.model.* import org.koitharu.kotatsu.core.model.*
import java.io.Closeable import java.io.Closeable
import java.util.*
class HistoryRepository() : KoinComponent, MangaRepository, Closeable { class HistoryRepository() : KoinComponent, MangaRepository, Closeable {
@@ -61,6 +62,17 @@ class HistoryRepository() : KoinComponent, MangaRepository, Closeable {
) )
} }
suspend fun getHistory(manga: Manga): MangaHistory? {
return db.historyDao().getOneOrNull(manga.id)?.let {
MangaHistory(
createdAt = Date(it.createdAt),
updatedAt = Date(it.updatedAt),
chapterId = it.chapterId,
page = it.page
)
}
}
suspend fun clear() { suspend fun clear() {
db.historyDao().clear() db.historyDao().clear()
} }

View File

@@ -8,8 +8,9 @@ import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.fragment_chapters.* import kotlinx.android.synthetic.main.fragment_chapters.*
import moxy.ktx.moxyPresenter 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.MangaChapter import org.koitharu.kotatsu.core.model.MangaChapter
import org.koitharu.kotatsu.core.model.MangaHistory
import org.koitharu.kotatsu.core.model.MangaInfo
import org.koitharu.kotatsu.ui.common.BaseFragment import org.koitharu.kotatsu.ui.common.BaseFragment
import org.koitharu.kotatsu.ui.common.list.OnRecyclerItemClickListener import org.koitharu.kotatsu.ui.common.list.OnRecyclerItemClickListener
import org.koitharu.kotatsu.ui.reader.ReaderActivity import org.koitharu.kotatsu.ui.reader.ReaderActivity
@@ -20,20 +21,25 @@ class ChaptersFragment : BaseFragment(R.layout.fragment_chapters), MangaDetailsV
@Suppress("unused") @Suppress("unused")
private val presenter by moxyPresenter { (activity as MangaDetailsActivity).presenter } private val presenter by moxyPresenter { (activity as MangaDetailsActivity).presenter }
private var manga: Manga? = null private var data: MangaInfo<MangaHistory?>? = null
private lateinit var adapter: ChaptersAdapter private lateinit var adapter: ChaptersAdapter
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
adapter = ChaptersAdapter(this) adapter = ChaptersAdapter(this)
recyclerView_chapters.addItemDecoration(DividerItemDecoration(view.context, RecyclerView.VERTICAL)) recyclerView_chapters.addItemDecoration(
DividerItemDecoration(
view.context,
RecyclerView.VERTICAL
)
)
recyclerView_chapters.adapter = adapter recyclerView_chapters.adapter = adapter
} }
override fun onMangaUpdated(manga: Manga) { override fun onMangaUpdated(data: MangaInfo<MangaHistory?>) {
this.manga = manga this.data = data
adapter.replaceData(manga.chapters.orEmpty()) adapter.replaceData(data.manga.chapters.orEmpty())
} }
override fun onLoadingStateChanged(isLoading: Boolean) { override fun onLoadingStateChanged(isLoading: Boolean) {
@@ -45,10 +51,12 @@ class ChaptersFragment : BaseFragment(R.layout.fragment_chapters), MangaDetailsV
} }
override fun onItemClick(item: MangaChapter, position: Int, view: View) { override fun onItemClick(item: MangaChapter, position: Int, view: View) {
startActivity(ReaderActivity.newIntent( startActivity(
context ?: return, ReaderActivity.newIntent(
manga ?: return, context ?: return,
item.id data?.manga ?: return,
)) item.id
)
)
} }
} }

View File

@@ -8,6 +8,8 @@ import kotlinx.android.synthetic.main.activity_details.*
import moxy.ktx.moxyPresenter 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.MangaHistory
import org.koitharu.kotatsu.core.model.MangaInfo
import org.koitharu.kotatsu.ui.common.BaseActivity import org.koitharu.kotatsu.ui.common.BaseActivity
import org.koitharu.kotatsu.utils.ext.getDisplayMessage import org.koitharu.kotatsu.utils.ext.getDisplayMessage
@@ -26,8 +28,8 @@ class MangaDetailsActivity : BaseActivity(), MangaDetailsView {
} ?: finish() } ?: finish()
} }
override fun onMangaUpdated(manga: Manga) { override fun onMangaUpdated(data: MangaInfo<MangaHistory?>) {
title = manga.title title = data.manga.title
} }
override fun onLoadingStateChanged(isLoading: Boolean) = Unit override fun onLoadingStateChanged(isLoading: Boolean) = Unit

View File

@@ -6,7 +6,10 @@ import kotlinx.android.synthetic.main.fragment_details.*
import moxy.ktx.moxyPresenter 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.MangaHistory
import org.koitharu.kotatsu.core.model.MangaInfo
import org.koitharu.kotatsu.ui.common.BaseFragment import org.koitharu.kotatsu.ui.common.BaseFragment
import org.koitharu.kotatsu.ui.reader.ReaderActivity
import org.koitharu.kotatsu.utils.ext.setChips import org.koitharu.kotatsu.utils.ext.setChips
import kotlin.math.roundToInt import kotlin.math.roundToInt
@@ -15,24 +18,46 @@ class MangaDetailsFragment : BaseFragment(R.layout.fragment_details), MangaDetai
@Suppress("unused") @Suppress("unused")
private val presenter by moxyPresenter { (activity as MangaDetailsActivity).presenter } private val presenter by moxyPresenter { (activity as MangaDetailsActivity).presenter }
override fun onMangaUpdated(manga: Manga) { override fun onMangaUpdated(data: MangaInfo<MangaHistory?>) {
imageView_cover.load(manga.largeCoverUrl ?: manga.coverUrl) imageView_cover.load(data.manga.largeCoverUrl ?: data.manga.coverUrl)
textView_title.text = manga.title textView_title.text = data.manga.title
textView_subtitle.text = manga.localizedTitle textView_subtitle.text = data.manga.localizedTitle
textView_description.text = manga.description textView_description.text = data.manga.description
if (manga.rating == Manga.NO_RATING) { if (data.manga.rating == Manga.NO_RATING) {
ratingBar.isVisible = false ratingBar.isVisible = false
} else { } else {
ratingBar.progress = (ratingBar.max * manga.rating).roundToInt() ratingBar.progress = (ratingBar.max * data.manga.rating).roundToInt()
ratingBar.isVisible = true ratingBar.isVisible = true
} }
chips_tags.setChips(manga.tags) { chips_tags.setChips(data.manga.tags) {
create( create(
text = it.title, text = it.title,
iconRes = R.drawable.ic_chip_tag, iconRes = R.drawable.ic_chip_tag,
tag = it tag = it
) )
} }
if (data.manga.chapters.isNullOrEmpty()) {
button_read.isEnabled = false
} else {
button_read.isEnabled = true
if (data.extra == null) {
button_read.setText(R.string.read)
button_read.setIconResource(R.drawable.ic_read)
} else {
button_read.setText(R.string.continue_)
button_read.setIconResource(R.drawable.ic_play)
}
val chapterId = data.extra?.chapterId ?: data.manga.chapters.first().id
button_read.setOnClickListener {
startActivity(
ReaderActivity.newIntent(
context ?: return@setOnClickListener,
data.manga,
chapterId
)
)
}
}
} }
override fun onLoadingStateChanged(isLoading: Boolean) { override fun onLoadingStateChanged(isLoading: Boolean) {

View File

@@ -1,11 +1,14 @@
package org.koitharu.kotatsu.ui.details package org.koitharu.kotatsu.ui.details
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import moxy.InjectViewState import moxy.InjectViewState
import org.koitharu.kotatsu.BuildConfig import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.core.model.Manga import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.model.MangaInfo
import org.koitharu.kotatsu.domain.HistoryRepository
import org.koitharu.kotatsu.domain.MangaProviderFactory import org.koitharu.kotatsu.domain.MangaProviderFactory
import org.koitharu.kotatsu.ui.common.BasePresenter import org.koitharu.kotatsu.ui.common.BasePresenter
@@ -18,14 +21,20 @@ class MangaDetailsPresenter : BasePresenter<MangaDetailsView>() {
if (isLoaded) { if (isLoaded) {
return return
} }
viewState.onMangaUpdated(manga) viewState.onMangaUpdated(MangaInfo(manga, null))
launch { launch {
try { try {
viewState.onLoadingStateChanged(true) viewState.onLoadingStateChanged(true)
val details = withContext(Dispatchers.IO) { val data = withContext(Dispatchers.IO) {
MangaProviderFactory.create(manga.source).getDetails(manga) val details = async {
MangaProviderFactory.create(manga.source).getDetails(manga)
}
val history = async {
HistoryRepository().use { it.getHistory(manga) }
}
MangaInfo(details.await(), history.await())
} }
viewState.onMangaUpdated(details) viewState.onMangaUpdated(data)
isLoaded = true isLoaded = true
} catch (e: Exception) { } catch (e: Exception) {
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {

View File

@@ -4,12 +4,13 @@ import moxy.MvpView
import moxy.viewstate.strategy.AddToEndSingleStrategy import moxy.viewstate.strategy.AddToEndSingleStrategy
import moxy.viewstate.strategy.OneExecutionStateStrategy import moxy.viewstate.strategy.OneExecutionStateStrategy
import moxy.viewstate.strategy.StateStrategyType import moxy.viewstate.strategy.StateStrategyType
import org.koitharu.kotatsu.core.model.Manga import org.koitharu.kotatsu.core.model.MangaHistory
import org.koitharu.kotatsu.core.model.MangaInfo
interface MangaDetailsView : MvpView { interface MangaDetailsView : MvpView {
@StateStrategyType(AddToEndSingleStrategy::class) @StateStrategyType(AddToEndSingleStrategy::class)
fun onMangaUpdated(manga: Manga) fun onMangaUpdated(data: MangaInfo<MangaHistory?>)
@StateStrategyType(AddToEndSingleStrategy::class) @StateStrategyType(AddToEndSingleStrategy::class)
fun onLoadingStateChanged(isLoading: Boolean) fun onLoadingStateChanged(isLoading: Boolean)

View File

@@ -1,17 +1,20 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector
android:width="108dp" xmlns:android="http://schemas.android.com/apk/res/android"
android:height="108dp" android:width="108dp"
android:viewportWidth="108" android:height="108dp"
android:viewportHeight="108" android:tint="#0D47A1"
android:tint="#0D47A1"> android:viewportWidth="108"
<group android:scaleX="0.40188664" android:viewportHeight="108">
android:scaleY="0.40188664" <group
android:translateX="32.90095" android:scaleX="0.40188664"
android:translateY="18.7272"> android:scaleY="0.40188664"
<group android:translateY="139.39206"> android:translateX="32.90095"
<path android:pathData="M83.796875,-0L105.6875,-0L60.765625,-55.828125L103.09375,-101L82.078125,-101L32.25,-49.1875L32.25,-101L13.53125,-101L13.53125,-0L32.25,-0L32.25,-25.8125L48.234375,-42.265625L83.796875,-0Z" android:translateY="18.7272">
android:fillColor="#0D47A1"/> <group android:translateY="139.39206">
</group> <path
</group> android:fillColor="#0D47A1"
android:pathData="M83.796875,-0L105.6875,-0L60.765625,-55.828125L103.09375,-101L82.078125,-101L32.25,-49.1875L32.25,-101L13.53125,-101L13.53125,-0L32.25,-0L32.25,-25.8125L48.234375,-42.265625L83.796875,-0Z" />
</group>
</group>
</vector> </vector>

View File

@@ -0,0 +1,11 @@
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?android:colorControlNormal"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="@android:color/white"
android:pathData="M10,8.64L15.27,12 10,15.36V8.64M8,5v14l11,-7L8,5z" />
</vector>

View File

@@ -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.0"
android:viewportHeight="24.0">
<path
android:fillColor="@android:color/white"
android:pathData="M12,9c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,3c1.1,0 2,0.9 2,2s-0.9,2 -2,2 -2,-0.9 -2,-2 0.9,-2 2,-2zM12,11.55C9.64,9.35 6.48,8 3,8v11c3.48,0 6.64,1.35 9,3.55 2.36,-2.19 5.52,-3.55 9,-3.55L21,8c-3.48,0 -6.64,1.35 -9,3.55zM19,17.13c-2.53,0.34 -4.93,1.3 -7,2.82 -2.06,-1.52 -4.47,-2.49 -7,-2.83v-6.95c2.1,0.38 4.05,1.35 5.64,2.83L12,14.28l1.36,-1.27c1.59,-1.48 3.54,-2.45 5.64,-2.83v6.95z" />
</vector>

View File

@@ -55,16 +55,31 @@
android:layout_marginTop="10dp" android:layout_marginTop="10dp"
android:isIndicator="true" android:isIndicator="true"
android:max="100" android:max="100"
tools:progress="70"
app:layout_constraintStart_toStartOf="@id/textView_title" app:layout_constraintStart_toStartOf="@id/textView_title"
app:layout_constraintTop_toBottomOf="@id/textView_subtitle" /> app:layout_constraintTop_toBottomOf="@id/textView_subtitle"
tools:progress="70" />
<com.google.android.material.button.MaterialButton
android:id="@+id/button_read"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/read"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
app:icon="@drawable/ic_read"
app:iconPadding="12dp"
android:enabled="false"
android:layout_marginEnd="4dp"
app:layout_constraintBottom_toBottomOf="@id/imageView_cover"
app:layout_constraintEnd_toEndOf="@id/textView_title"
app:layout_constraintTop_toBottomOf="@id/ratingBar"
app:layout_constraintVertical_bias="1" />
<androidx.constraintlayout.widget.Barrier <androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier_title" android:id="@+id/barrier_title"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:barrierDirection="bottom" app:barrierDirection="bottom"
app:constraint_referenced_ids="imageView_cover, textView_subtitle, ratingBar" /> app:constraint_referenced_ids="imageView_cover, button_read" />
<com.google.android.material.chip.ChipGroup <com.google.android.material.chip.ChipGroup
android:id="@+id/chips_tags" android:id="@+id/chips_tags"

View File

@@ -4,6 +4,13 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="92dp"> android:layout_height="92dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_launcher_foreground"
android:layout_alignParentStart="true"
android:layout_centerVertical="true" />
<View <View
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="1dp" android:layout_height="1dp"

View File

@@ -22,4 +22,6 @@
<string name="clear_history">Clear history</string> <string name="clear_history">Clear history</string>
<string name="nothing_found">Nothing found</string> <string name="nothing_found">Nothing found</string>
<string name="history_is_empty">History is empty</string> <string name="history_is_empty">History is empty</string>
<string name="read">Read</string>
<string name="continue_">Continue</string>
</resources> </resources>