Add Resume reading fab

pull/1/head
Koitharu 6 years ago
parent fec3481d27
commit cbba4a850c

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

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

@ -1,14 +1,20 @@
package org.koitharu.kotatsu.ui.main package org.koitharu.kotatsu.ui.main
import android.content.SharedPreferences import android.content.SharedPreferences
import android.content.res.ColorStateList
import android.content.res.Configuration import android.content.res.Configuration
import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import androidx.appcompat.app.ActionBarDrawerToggle import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.swiperefreshlayout.widget.CircularProgressDrawable
import com.google.android.material.navigation.NavigationView import com.google.android.material.navigation.NavigationView
import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.activity_main.*
import moxy.ktx.moxyPresenter
import org.koin.core.inject import org.koin.core.inject
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.MangaSource 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.history.HistoryListFragment
import org.koitharu.kotatsu.ui.main.list.local.LocalListFragment import org.koitharu.kotatsu.ui.main.list.local.LocalListFragment
import org.koitharu.kotatsu.ui.main.list.remote.RemoteListFragment 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.ui.settings.SettingsActivity
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
import org.koitharu.kotatsu.utils.ext.resolveDp
class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedListener, class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedListener,
SharedPreferences.OnSharedPreferenceChangeListener { SharedPreferences.OnSharedPreferenceChangeListener, MainView {
private val presenter by moxyPresenter(factory = ::MainPresenter)
private val settings by inject<AppSettings>() private val settings by inject<AppSettings>()
private lateinit var drawerToggle: ActionBarDrawerToggle private lateinit var drawerToggle: ActionBarDrawerToggle
@ -40,7 +52,15 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
navigationView.setNavigationItemSelectedListener(this) navigationView.setNavigationItemSelectedListener(this)
settings.subscribe(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) navigationView.setCheckedItem(R.id.nav_history)
setPrimaryFragment(HistoryListFragment.newInstance()) setPrimaryFragment(HistoryListFragment.newInstance())
} }
@ -91,6 +111,27 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
return true 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>) { private fun initSideMenu(remoteSources: List<MangaSource>) {
val submenu = navigationView.menu.findItem(R.id.nav_remote_sources).subMenu val submenu = navigationView.menu.findItem(R.id.nav_remote_sources).subMenu
submenu.removeGroup(R.id.group_remote_sources) submenu.removeGroup(R.id.group_remote_sources)
@ -110,5 +151,6 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
supportFragmentManager.beginTransaction() supportFragmentManager.beginTransaction()
.replace(R.id.container, fragment) .replace(R.id.container, fragment)
.commit() .commit()
fab.isVisible = fragment is HistoryListFragment
} }
} }

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

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

@ -1,9 +1,14 @@
package org.koitharu.kotatsu.ui.main.list.history 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.Menu
import android.view.MenuInflater import android.view.MenuInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.core.view.isVisible
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.*
import moxy.ktx.moxyPresenter import moxy.ktx.moxyPresenter

@ -4,6 +4,7 @@ import android.content.res.Resources
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import org.koitharu.kotatsu.BuildConfig import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.exceptions.EmptyHistoryException
import org.koitharu.kotatsu.core.exceptions.UnsupportedFileException import org.koitharu.kotatsu.core.exceptions.UnsupportedFileException
import java.io.IOException 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) { fun Throwable.getDisplayMessage(resources: Resources) = when (this) {
is UnsupportedOperationException -> resources.getString(R.string.operation_not_supported) is UnsupportedOperationException -> resources.getString(R.string.operation_not_supported)
is UnsupportedFileException -> resources.getString(R.string.text_file_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) is IOException -> resources.getString(R.string.network_error)
else -> if (BuildConfig.DEBUG) { else -> if (BuildConfig.DEBUG) {
message ?: resources.getString(R.string.error_occurred) message ?: resources.getString(R.string.error_occurred)

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

@ -34,6 +34,20 @@
android:layout_height="match_parent" android:layout_height="match_parent"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" /> 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> </androidx.coordinatorlayout.widget.CoordinatorLayout>
<com.google.android.material.navigation.NavigationView <com.google.android.material.navigation.NavigationView

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

Loading…
Cancel
Save