#1023 Selected files get unselected on screen rotation - store boolean indicating action mode state in out bundle

This commit is contained in:
Sean Mac Gillicuddy 2020-01-31 12:36:11 +00:00
parent 51e90dd9c2
commit 52c642232f
8 changed files with 142 additions and 31 deletions

View File

@ -57,6 +57,7 @@ import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.Re
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestOpen
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestSelect
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestShareMultiSelection
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RestartActionMode
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.FileSelectListState
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects.DeleteFiles
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects.None
@ -93,6 +94,7 @@ class ZimManageViewModel @Inject constructor(
object RequestDeleteMultiSelection : FileSelectActions()
object RequestShareMultiSelection : FileSelectActions()
object MultiModeFinished : FileSelectActions()
object RestartActionMode : FileSelectActions()
}
val sideEffects = PublishProcessor.create<SideEffect<out Any?>>()
@ -150,6 +152,7 @@ class ZimManageViewModel @Inject constructor(
RequestShareMultiSelection -> ShareFiles(selectionsFromState())
MultiModeFinished -> noSideEffectAndClearSelectionState()
is RequestSelect -> noSideEffectSelectBook(it.bookOnDisk)
RestartActionMode -> StartMultiSelection(fileSelectActions)
}
)
}, Throwable::printStackTrace)
@ -165,7 +168,7 @@ class ZimManageViewModel @Inject constructor(
)
)
}
return StartMultiSelection(bookOnDisk, fileSelectActions)
return StartMultiSelection(fileSelectActions)
}
private fun selectBook(
@ -181,10 +184,7 @@ class ZimManageViewModel @Inject constructor(
private fun noSideEffectSelectBook(bookOnDisk: BookOnDisk): SideEffect<Unit> {
fileSelectListStates.value?.let {
fileSelectListStates.postValue(
it.copy(bookOnDiskListItems = it.bookOnDiskListItems.map { listItem ->
if (listItem.id == bookOnDisk.id) listItem.apply { isSelected = !isSelected }
else listItem
})
it.copy(bookOnDiskListItems = selectBook(it, bookOnDisk))
)
}
return None

View File

@ -23,11 +23,11 @@ import android.content.pm.PackageManager
import android.os.Build.VERSION
import android.os.Build.VERSION_CODES
import android.os.Bundle
import android.view.ActionMode
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ActionMode
import androidx.core.content.ContextCompat
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
@ -56,6 +56,8 @@ import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.Re
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestSelect
import javax.inject.Inject
private const val WAS_IN_ACTION_MODE = "WAS_IN_ACTION_MODE"
class ZimFileSelectFragment : BaseFragment() {
@Inject lateinit var sharedPreferenceUtil: SharedPreferenceUtil
@ -108,6 +110,9 @@ class ZimFileSelectFragment : BaseFragment() {
zimManageViewModel.deviceListIsRefreshing.observe(this, Observer {
zim_swiperefresh.isRefreshing = it!!
})
if (savedInstanceState != null && savedInstanceState.getBoolean(WAS_IN_ACTION_MODE)) {
zimManageViewModel.fileSelectActions.offer(FileSelectActions.RestartActionMode)
}
}
private fun sideEffects() = zimManageViewModel.sideEffects.subscribe(
@ -132,6 +137,11 @@ class ZimFileSelectFragment : BaseFragment() {
checkPermissions()
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putBoolean(WAS_IN_ACTION_MODE, actionMode != null)
}
override fun onDestroy() {
super.onDestroy()
disposable.clear()

View File

@ -31,7 +31,7 @@ import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDis
import org.kiwix.kiwixmobile.zim_manager.ZimManageActivity
import javax.inject.Inject
class DeleteFiles(private val booksOnDiskListItem: List<BookOnDisk>) :
data class DeleteFiles(private val booksOnDiskListItem: List<BookOnDisk>) :
SideEffect<Unit> {
@Inject lateinit var dialogShower: DialogShower

View File

@ -26,7 +26,7 @@ import org.kiwix.kiwixmobile.core.extensions.toast
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk
import org.kiwix.kiwixmobile.main.KiwixMainActivity
class OpenFile(private val bookOnDisk: BookOnDisk) :
data class OpenFile(private val bookOnDisk: BookOnDisk) :
SideEffect<Unit> {
override fun invokeWith(activity: AppCompatActivity) {

View File

@ -27,7 +27,7 @@ import org.kiwix.kiwixmobile.core.R
import org.kiwix.kiwixmobile.core.base.SideEffect
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk
class ShareFiles(private val selectedBooks: List<BookOnDisk>) :
data class ShareFiles(private val selectedBooks: List<BookOnDisk>) :
SideEffect<Unit> {
override fun invokeWith(activity: AppCompatActivity) {
val selectedFileShareIntent = Intent()

View File

@ -24,21 +24,20 @@ import io.reactivex.processors.PublishProcessor
import org.kiwix.kiwixmobile.core.R
import org.kiwix.kiwixmobile.core.base.SideEffect
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.startActionMode
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.MultiModeFinished
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestDeleteMultiSelection
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestShareMultiSelection
data class StartMultiSelection(
val bookOnDisk: BooksOnDiskListItem.BookOnDisk,
val fileSelectActions: PublishProcessor<FileSelectActions>
) : SideEffect<ActionMode?> {
override fun invokeWith(activity: AppCompatActivity) =
override fun invokeWith(activity: AppCompatActivity): ActionMode? =
activity.startActionMode(
R.menu.menu_zim_files_contextual,
mapOf(
R.id.zim_file_delete_item to { fileSelectActions.offer(RequestDeleteMultiSelection) },
R.id.zim_file_share_item to { fileSelectActions.offer(RequestShareMultiSelection) }
)
) { fileSelectActions.offer(FileSelectActions.MultiModeFinished) }
) { fileSelectActions.offer(MultiModeFinished) }
}

View File

@ -44,6 +44,8 @@ import org.kiwix.kiwixmobile.core.downloader.model.DownloadModel
import org.kiwix.kiwixmobile.core.entity.LibraryNetworkEntity.Book
import org.kiwix.kiwixmobile.core.utils.BookUtils
import org.kiwix.kiwixmobile.core.zim_manager.Language
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.SelectionMode.MULTI
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.SelectionMode.NORMAL
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState
@ -51,7 +53,19 @@ import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.CanWrite4G
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.CannotWrite4GbFile
import org.kiwix.kiwixmobile.zim_manager.NetworkState.CONNECTED
import org.kiwix.kiwixmobile.zim_manager.NetworkState.NOT_CONNECTED
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.MultiModeFinished
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestDeleteMultiSelection
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestMultiSelection
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestOpen
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestSelect
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestShareMultiSelection
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RestartActionMode
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.FileSelectListState
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects.DeleteFiles
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects.None
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects.OpenFile
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects.ShareFiles
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects.StartMultiSelection
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryListItem
import org.kiwix.sharedFunctions.InstantExecutorExtension
import org.kiwix.sharedFunctions.book
@ -406,4 +420,101 @@ class ZimManageViewModelTest {
)
)
}
@Nested
inner class SideEffects {
@Test
fun `RequestOpen offers OpenFile`() {
val bookOnDisk = bookOnDisk()
viewModel.sideEffects.test()
.also { viewModel.fileSelectActions.offer(RequestOpen(bookOnDisk)) }
.assertValues(OpenFile(bookOnDisk))
}
@Test
fun `RequestMultiSelection offers StartMultiSelection and selects a book`() {
val bookToSelect = bookOnDisk(databaseId = 0L)
val unSelectedBook = bookOnDisk(databaseId = 1L)
viewModel.fileSelectListStates.value = FileSelectListState(
listOf(
bookToSelect,
unSelectedBook
),
NORMAL
)
viewModel.sideEffects.test()
.also { viewModel.fileSelectActions.offer(RequestMultiSelection(bookToSelect)) }
.assertValues(StartMultiSelection(viewModel.fileSelectActions))
viewModel.fileSelectListStates.test()
.assertValue(
FileSelectListState(
listOf(bookToSelect.apply { isSelected = !isSelected }, unSelectedBook),
MULTI
)
)
}
@Test
fun `RequestDeleteMultiSelection offers DeleteFiles with selected books`() {
val selectedBook = bookOnDisk().apply { isSelected = true }
viewModel.fileSelectListStates.value =
FileSelectListState(listOf(selectedBook, bookOnDisk()), NORMAL)
viewModel.sideEffects.test()
.also { viewModel.fileSelectActions.offer(RequestDeleteMultiSelection) }
.assertValues(DeleteFiles(listOf(selectedBook)))
}
@Test
fun `RequestShareMultiSelection offers ShareFiles with selected books`() {
val selectedBook = bookOnDisk().apply { isSelected = true }
viewModel.fileSelectListStates.value =
FileSelectListState(listOf(selectedBook, bookOnDisk()), NORMAL)
viewModel.sideEffects.test()
.also { viewModel.fileSelectActions.offer(RequestShareMultiSelection) }
.assertValues(ShareFiles(listOf(selectedBook)))
}
@Test
fun `MultiModeFinished offers None`() {
val selectedBook = bookOnDisk().apply { isSelected = true }
viewModel.fileSelectListStates.value =
FileSelectListState(listOf(selectedBook, bookOnDisk()), NORMAL)
viewModel.sideEffects.test()
.also { viewModel.fileSelectActions.offer(MultiModeFinished) }
.assertValues(None)
viewModel.fileSelectListStates.test().assertValue(
FileSelectListState(
listOf(
selectedBook.apply { isSelected = false },
bookOnDisk()
)
)
)
}
@Test
fun `RequestSelect offers None and inverts selection`() {
val selectedBook = bookOnDisk(0L).apply { isSelected = true }
viewModel.fileSelectListStates.value =
FileSelectListState(listOf(selectedBook, bookOnDisk(1L)), NORMAL)
viewModel.sideEffects.test()
.also { viewModel.fileSelectActions.offer(RequestSelect(selectedBook)) }
.assertValues(None)
viewModel.fileSelectListStates.test().assertValue(
FileSelectListState(
listOf(
selectedBook.apply { isSelected = false },
bookOnDisk(1L)
)
)
)
}
@Test
fun `RestartActionMode offers StartMultiSelection`() {
viewModel.sideEffects.test()
.also { viewModel.fileSelectActions.offer(RestartActionMode) }
.assertValues(StartMultiSelection(viewModel.fileSelectActions))
}
}
}

View File

@ -38,28 +38,19 @@ object ActivityExtensions {
onDestroyAction: () -> Unit
): ActionMode? {
return startSupportActionMode(object : ActionMode.Callback {
override fun onActionItemClicked(
mode: ActionMode,
item: MenuItem
) = idsToClickActions[item.itemId]?.let {
it()
mode.finish()
true
} ?: false
override fun onActionItemClicked(mode: ActionMode, item: MenuItem) =
idsToClickActions[item.itemId]?.let {
it()
mode.finish()
true
} ?: false
override fun onCreateActionMode(
mode: ActionMode,
menu: Menu?
): Boolean {
mode.menuInflater
.inflate(menuId, menu)
override fun onCreateActionMode(mode: ActionMode, menu: Menu?): Boolean {
mode.menuInflater.inflate(menuId, menu)
return true
}
override fun onPrepareActionMode(
mode: ActionMode?,
menu: Menu?
) = false
override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?) = false
override fun onDestroyActionMode(mode: ActionMode?) {
onDestroyAction()