Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions app/src/main/kotlin/me/xizzhu/android/joshua/Injection.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import me.xizzhu.android.joshua.core.*
import me.xizzhu.android.joshua.core.provider.CoroutineDispatcherProvider
import me.xizzhu.android.joshua.core.provider.DefaultCoroutineDispatcherProvider
import me.xizzhu.android.joshua.core.provider.DefaultTimeProvider
import me.xizzhu.android.joshua.core.provider.TimeProvider
import me.xizzhu.android.joshua.core.repository.*
import me.xizzhu.android.joshua.core.repository.local.android.*
import me.xizzhu.android.joshua.core.repository.local.android.db.AndroidDatabase
Expand All @@ -43,10 +47,6 @@ object AppModule {
@Singleton
fun provideApp(application: Application): App = application as App

@Provides
@Singleton
fun provideCoroutineDispatcherProvider(): CoroutineDispatcherProvider = DefaultCoroutineDispatcherProvider()

@Provides
@Singleton
fun provideAppScope(): CoroutineScope = appScope
Expand Down Expand Up @@ -124,6 +124,18 @@ object AppModule {
TranslationManager(translationRepository)
}

@Module
@InstallIn(SingletonComponent::class)
object ProviderModule {
@Provides
@Singleton
fun provideCoroutineDispatcherProvider(): CoroutineDispatcherProvider = DefaultCoroutineDispatcherProvider()

@Provides
@Singleton
fun provideTimeProvider(): TimeProvider = DefaultTimeProvider()
}

@Module
@InstallIn(SingletonComponent::class)
object RepositoryModule {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,8 @@
package me.xizzhu.android.joshua.annotated

import android.os.Bundle
import android.view.View
import androidx.annotation.StringRes
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import androidx.core.view.isVisible
import me.xizzhu.android.joshua.Navigator
import me.xizzhu.android.joshua.R
import me.xizzhu.android.joshua.annotated.bookmarks.BookmarkItem
Expand All @@ -31,95 +28,106 @@ import me.xizzhu.android.joshua.core.Constants
import me.xizzhu.android.joshua.core.VerseAnnotation
import me.xizzhu.android.joshua.core.VerseIndex
import me.xizzhu.android.joshua.databinding.ActivityAnnotatedBinding
import me.xizzhu.android.joshua.infra.BaseActivity
import me.xizzhu.android.joshua.infra.onEach
import me.xizzhu.android.joshua.infra.onFailure
import me.xizzhu.android.joshua.infra.onSuccess
import me.xizzhu.android.joshua.infra.BaseActivityV2
import me.xizzhu.android.joshua.preview.VersePreviewItem
import me.xizzhu.android.joshua.ui.dialog
import me.xizzhu.android.joshua.ui.fadeIn
import me.xizzhu.android.joshua.ui.listDialog
import javax.inject.Inject

abstract class AnnotatedVersesActivity<V : VerseAnnotation, VM : AnnotatedVersesViewModel<V>>(
@StringRes private val toolbarText: Int
) : BaseActivity<ActivityAnnotatedBinding, VM>(), BookmarkItem.Callback, HighlightItem.Callback, NoteItem.Callback, VersePreviewItem.Callback {
@Inject
lateinit var annotatedVersesViewModel: VM

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@StringRes private val toolbarText: Int
) : BaseActivityV2<ActivityAnnotatedBinding, AnnotatedVersesViewModel.ViewAction, AnnotatedVersesViewModel.ViewState, VM>(), BookmarkItem.Callback, HighlightItem.Callback, NoteItem.Callback, VersePreviewItem.Callback {
override fun inflateViewBinding(): ActivityAnnotatedBinding = ActivityAnnotatedBinding.inflate(layoutInflater)

observeSettings()
observeSortOrder()
observeAnnotatedVerses()
initializeToolbar()
override fun onViewActionEmitted(viewAction: AnnotatedVersesViewModel.ViewAction) = when (viewAction) {
AnnotatedVersesViewModel.ViewAction.OpenReadingScreen -> navigator.navigate(this, Navigator.SCREEN_READING, extrasForOpeningVerse())
}

private fun observeSettings() {
annotatedVersesViewModel.settings().onEach { viewBinding.verseList.setSettings(it) }.launchIn(lifecycleScope)
}
override fun onViewStateUpdated(viewState: AnnotatedVersesViewModel.ViewState) = with(viewBinding) {
viewState.settings?.let { verseList.setSettings(it) }

private fun observeSortOrder() {
annotatedVersesViewModel.sortOrder().onEach { viewBinding.toolbar.setSortOrder(it) }.launchIn(lifecycleScope)
}
toolbar.setSortOrder(viewState.sortOrder)

if (viewState.loading) {
loadingSpinner.fadeIn()
verseList.isVisible = false
} else {
loadingSpinner.isVisible = false
verseList.fadeIn()
}

verseList.setItems(viewState.items)

viewState.preview?.let { preview ->
listDialog(
title = preview.title,
settings = preview.settings,
items = preview.items,
selected = preview.currentPosition,
onDismiss = { viewModel.markPreviewAsClosed() }
)
}

private fun observeAnnotatedVerses() {
annotatedVersesViewModel.annotatedVerses()
.onEach(
onLoading = {
with(viewBinding) {
loadingSpinner.fadeIn()
verseList.visibility = View.GONE
}
},
onSuccess = {
with(viewBinding) {
verseList.setItems(it.items)
verseList.fadeIn()
loadingSpinner.visibility = View.GONE
}
},
onFailure = {
viewBinding.loadingSpinner.visibility = View.GONE
dialog(
false, R.string.dialog_title_error, R.string.dialog_message_failed_to_load_annotated_verses,
{ _, _ -> annotatedVersesViewModel.loadAnnotatedVerses() }, { _, _ -> finish() }
)
}
when (val error = viewState.error) {
is AnnotatedVersesViewModel.ViewState.Error.AnnotatedVersesLoadingError -> {
dialog(
cancelable = false,
title = R.string.dialog_title_error,
message = R.string.dialog_message_failed_to_load_annotated_verses,
onPositive = { _, _ -> viewModel.loadAnnotatedVerses() },
onNegative = { _, _ -> finish() },
onDismiss = { viewModel.markErrorAsShown(error) }
)
.launchIn(lifecycleScope)
}
is AnnotatedVersesViewModel.ViewState.Error.PreviewLoadingError -> {
viewModel.markErrorAsShown(error)

// Very unlikely to fail, so just falls back to open the verse.
openVerse(error.verseToPreview)
}
is AnnotatedVersesViewModel.ViewState.Error.SortOrderSavingError -> {
dialog(
cancelable = true,
title = R.string.dialog_title_error,
message = R.string.dialog_message_failed_to_save_sort_order,
onPositive = { _, _ -> saveSortOrder(error.sortOrder) },
onDismiss = { viewModel.markErrorAsShown(error) }
)
}
is AnnotatedVersesViewModel.ViewState.Error.VerseOpeningError -> {
dialog(
cancelable = true,
title = R.string.dialog_title_error,
message = R.string.dialog_message_failed_to_select_verse,
onPositive = { _, _ -> openVerse(error.verseToOpen) },
onDismiss = { viewModel.markErrorAsShown(error) }
)
}
null -> {
// Do nothing
}
}
}

private fun initializeToolbar() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

with(viewBinding.toolbar) {
setTitle(toolbarText)
sortOrderUpdated = ::saveSortOrder
}
}

private fun saveSortOrder(@Constants.SortOrder sortOrder: Int) {
annotatedVersesViewModel.saveSortOrder(sortOrder)
.onFailure { dialog(true, R.string.dialog_title_error, R.string.dialog_message_failed_to_save_sort_order, { _, _ -> saveSortOrder(sortOrder) }) }
.launchIn(lifecycleScope)
viewModel.saveSortOrder(sortOrder)
}

override fun inflateViewBinding(): ActivityAnnotatedBinding = ActivityAnnotatedBinding.inflate(layoutInflater)

override fun viewModel(): VM = annotatedVersesViewModel

override fun openVerse(verseToOpen: VerseIndex) {
annotatedVersesViewModel.saveCurrentVerseIndex(verseToOpen)
.onSuccess { navigator.navigate(this, Navigator.SCREEN_READING, extrasForOpeningVerse()) }
.onFailure { dialog(true, R.string.dialog_title_error, R.string.dialog_message_failed_to_select_verse, { _, _ -> openVerse(verseToOpen) }) }
.launchIn(lifecycleScope)
viewModel.openVerse(verseToOpen)
}

override fun showPreview(verseIndex: VerseIndex) {
annotatedVersesViewModel.loadVersesForPreview(verseIndex)
.onSuccess { preview -> listDialog(preview.title, preview.settings, preview.items, preview.currentPosition) }
.onFailure { openVerse(verseIndex) } // Very unlikely to fail, so just falls back to open the verse.
.launchIn(lifecycleScope)
viewModel.loadPreview(verseIndex)
}

protected open fun extrasForOpeningVerse(): Bundle? = null
Expand Down
Loading