Add Resume reading fab

This commit is contained in:
Koitharu
2020-03-09 11:20:15 +02:00
parent fec3481d27
commit cbba4a850c
10 changed files with 137 additions and 7 deletions

View File

@@ -0,0 +1,3 @@
package org.koitharu.kotatsu.core.exceptions
class EmptyHistoryException : RuntimeException()

View File

@@ -16,8 +16,8 @@ class HistoryRepository : KoinComponent {
private val db: MangaDatabase by inject()
suspend fun getList(offset: Int): List<Manga> {
val entities = db.historyDao().findAll(offset, 20)
suspend fun getList(offset: Int, limit: Int = 20): List<Manga> {
val entities = db.historyDao().findAll(offset, limit)
return entities.map { it.manga.toManga(it.tags.map(TagEntity::toMangaTag).toSet()) }
}

View File

@@ -1,14 +1,20 @@
package org.koitharu.kotatsu.ui.main
import android.content.SharedPreferences
import android.content.res.ColorStateList
import android.content.res.Configuration
import android.graphics.Color
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.swiperefreshlayout.widget.CircularProgressDrawable
import com.google.android.material.navigation.NavigationView
import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.activity_main.*
import moxy.ktx.moxyPresenter
import org.koin.core.inject
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.MangaSource
@@ -19,10 +25,16 @@ import org.koitharu.kotatsu.ui.main.list.favourites.FavouritesListFragment
import org.koitharu.kotatsu.ui.main.list.history.HistoryListFragment
import org.koitharu.kotatsu.ui.main.list.local.LocalListFragment
import org.koitharu.kotatsu.ui.main.list.remote.RemoteListFragment
import org.koitharu.kotatsu.ui.reader.ReaderActivity
import org.koitharu.kotatsu.ui.reader.ReaderState
import org.koitharu.kotatsu.ui.settings.SettingsActivity
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
import org.koitharu.kotatsu.utils.ext.resolveDp
class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedListener,
SharedPreferences.OnSharedPreferenceChangeListener {
SharedPreferences.OnSharedPreferenceChangeListener, MainView {
private val presenter by moxyPresenter(factory = ::MainPresenter)
private val settings by inject<AppSettings>()
private lateinit var drawerToggle: ActionBarDrawerToggle
@@ -40,7 +52,15 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
navigationView.setNavigationItemSelectedListener(this)
settings.subscribe(this)
if (supportFragmentManager.findFragmentById(R.id.container) == null) {
fab.imageTintList = ColorStateList.valueOf(Color.WHITE)
fab.isVisible = true
fab.setOnClickListener {
presenter.openLastReader()
}
supportFragmentManager.findFragmentById(R.id.container)?.let {
fab.isVisible = it is HistoryListFragment
} ?: run {
navigationView.setCheckedItem(R.id.nav_history)
setPrimaryFragment(HistoryListFragment.newInstance())
}
@@ -91,6 +111,27 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
return true
}
override fun onOpenReader(state: ReaderState) {
startActivity(ReaderActivity.newIntent(this, state))
}
override fun onError(e: Throwable) {
Snackbar.make(container, e.getDisplayMessage(resources), Snackbar.LENGTH_SHORT).show()
}
override fun onLoadingStateChanged(isLoading: Boolean) {
fab.isEnabled = !isLoading
if (isLoading) {
fab.setImageDrawable(CircularProgressDrawable(this).also {
it.setColorSchemeColors(Color.WHITE)
it.strokeWidth = resources.resolveDp(2f)
it.start()
})
} else {
fab.setImageResource(R.drawable.ic_read_fill)
}
}
private fun initSideMenu(remoteSources: List<MangaSource>) {
val submenu = navigationView.menu.findItem(R.id.nav_remote_sources).subMenu
submenu.removeGroup(R.id.group_remote_sources)
@@ -110,5 +151,6 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
supportFragmentManager.beginTransaction()
.replace(R.id.container, fragment)
.commit()
fab.isVisible = fragment is HistoryListFragment
}
}

View File

@@ -0,0 +1,41 @@
package org.koitharu.kotatsu.ui.main
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import moxy.InjectViewState
import moxy.presenterScope
import org.koitharu.kotatsu.core.exceptions.EmptyHistoryException
import org.koitharu.kotatsu.domain.MangaProviderFactory
import org.koitharu.kotatsu.domain.history.HistoryRepository
import org.koitharu.kotatsu.ui.common.BasePresenter
import org.koitharu.kotatsu.ui.reader.ReaderState
@InjectViewState
class MainPresenter : BasePresenter<MainView>() {
fun openLastReader() {
presenterScope.launch {
viewState.onLoadingStateChanged(isLoading = true)
try {
val state = withContext(Dispatchers.IO) {
val repo = HistoryRepository()
val manga = repo.getList(0, 1).firstOrNull()
?: throw EmptyHistoryException()
val history = repo.getOne(manga) ?: throw EmptyHistoryException()
ReaderState(
MangaProviderFactory.create(manga.source).getDetails(manga),
history.chapterId, history.page
)
}
viewState.onOpenReader(state)
} catch (_: CancellationException) {
} catch (e: Throwable) {
viewState.onError(e)
} finally {
viewState.onLoadingStateChanged(isLoading = false)
}
}
}
}

View File

@@ -0,0 +1,12 @@
package org.koitharu.kotatsu.ui.main
import moxy.viewstate.strategy.alias.OneExecution
import org.koitharu.kotatsu.core.model.MangaState
import org.koitharu.kotatsu.ui.common.BaseMvpView
import org.koitharu.kotatsu.ui.reader.ReaderState
interface MainView : BaseMvpView {
@OneExecution
fun onOpenReader(state: ReaderState)
}

View File

@@ -1,9 +1,14 @@
package org.koitharu.kotatsu.ui.main.list.history
import android.content.res.ColorStateList
import android.graphics.Color
import android.os.Bundle
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import androidx.appcompat.app.AlertDialog
import androidx.core.view.isVisible
import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.fragment_list.*
import moxy.ktx.moxyPresenter

View File

@@ -4,6 +4,7 @@ import android.content.res.Resources
import kotlinx.coroutines.delay
import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.exceptions.EmptyHistoryException
import org.koitharu.kotatsu.core.exceptions.UnsupportedFileException
import java.io.IOException
@@ -35,6 +36,7 @@ suspend inline fun <T, R> T.retryUntilSuccess(maxAttempts: Int, action: T.() ->
fun Throwable.getDisplayMessage(resources: Resources) = when (this) {
is UnsupportedOperationException -> resources.getString(R.string.operation_not_supported)
is UnsupportedFileException -> resources.getString(R.string.text_file_not_supported)
is EmptyHistoryException -> resources.getString(R.string.history_is_empty)
is IOException -> resources.getString(R.string.network_error)
else -> if (BuildConfig.DEBUG) {
message ?: resources.getString(R.string.error_occurred)

View 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="M12,8A3,3 0 0,0 15,5A3,3 0 0,0 12,2A3,3 0 0,0 9,5A3,3 0 0,0 12,8M12,11.54C9.64,9.35 6.5,8 3,8V19C6.5,19 9.64,20.35 12,22.54C14.36,20.35 17.5,19 21,19V8C17.5,8 14.36,9.35 12,11.54Z" />
</vector>

View File

@@ -34,6 +34,20 @@
android:layout_height="match_parent"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:src="@drawable/ic_read_fill"
android:visibility="gone"
app:fabSize="normal"
app:backgroundTint="?colorAccent"
app:layout_anchor="@id/container"
app:layout_anchorGravity="bottom|end"
app:layout_behavior="com.google.android.material.behavior.HideBottomViewOnScrollBehavior"
tools:visibility="visible" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<com.google.android.material.navigation.NavigationView

View File

@@ -5,8 +5,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:openDrawer="end">
android:layout_height="match_parent">
<FrameLayout
android:layout_width="match_parent"
@@ -44,8 +43,8 @@
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:textColor="?android:textColorSecondary"
android:textAppearance="?android:textAppearanceMedium"
android:textColor="?android:textColorSecondary"
tools:text="@tools:sample/lorem[3]" />
</LinearLayout>