#1253 readd action mode for sharing and deleting zim files on disk

This commit is contained in:
Sean Mac Gillicuddy 2019-07-02 16:31:29 +01:00
parent 62987fa4ed
commit 51998e8f20
21 changed files with 496 additions and 93 deletions

View File

@ -218,7 +218,7 @@
},
{
"id": "2:6862771806221961183",
"name": "zimID"
"name": "zimId"
},
{
"id": "3:4312769031500860715",

View File

@ -23,6 +23,7 @@ import dagger.Subcomponent
import org.kiwix.kiwixmobile.di.modules.ActivityModule
import org.kiwix.kiwixmobile.downloader.DownloadFragment
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.ZimFileSelectFragment
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects.DeleteFiles
import org.kiwix.kiwixmobile.zim_manager.library_view.LibraryFragment
@Subcomponent(modules = [ActivityModule::class])
@ -33,6 +34,8 @@ interface ActivityComponent {
fun inject(zimFileSelectFragment: ZimFileSelectFragment)
fun inject(deleteFiles: DeleteFiles)
@Subcomponent.Builder
interface Builder {

View File

@ -0,0 +1,43 @@
package org.kiwix.kiwixmobile.extensions
import android.app.Activity
import android.view.ActionMode
import android.view.ActionMode.Callback
import android.view.Menu
import android.view.MenuItem
fun Activity.startActionMode(
menuId: Int,
idsToClickActions: Map<Int, () -> Any>,
onDestroyAction: () -> Unit
): ActionMode? {
return startActionMode(object : Callback {
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.getMenuInflater()
.inflate(menuId, menu)
return true
}
override fun onPrepareActionMode(
mode: ActionMode?,
menu: Menu?
) = false
override fun onDestroyActionMode(mode: ActionMode?) {
onDestroyAction()
}
})
}

View File

@ -37,7 +37,6 @@ import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.Environment;
import android.os.Handler;
import android.provider.Settings;
import android.text.SpannableString;
@ -100,11 +99,9 @@ import org.kiwix.kiwixmobile.base.BaseActivity;
import org.kiwix.kiwixmobile.bookmark.BookmarkItem;
import org.kiwix.kiwixmobile.bookmark.BookmarksActivity;
import org.kiwix.kiwixmobile.data.ZimContentProvider;
import org.kiwix.kiwixmobile.data.local.entity.Bookmark;
import org.kiwix.kiwixmobile.help.HelpActivity;
import org.kiwix.kiwixmobile.history.HistoryActivity;
import org.kiwix.kiwixmobile.history.HistoryListItem;
import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity;
import org.kiwix.kiwixmobile.search.SearchActivity;
import org.kiwix.kiwixmobile.settings.KiwixSettingsActivity;
import org.kiwix.kiwixmobile.utils.DimenUtils;
@ -377,6 +374,7 @@ public class MainActivity extends BaseActivity implements WebViewCallback,
open(bookOnDiskItem);
return Unit.INSTANCE;
},
null,
null),
BookOnDiskDelegate.LanguageDelegate.INSTANCE
);
@ -2105,7 +2103,7 @@ public class MainActivity extends BaseActivity implements WebViewCallback,
@Override
public void addBooks(List<BooksOnDiskListItem> books) {
booksAdapter.setItemList(books);
booksAdapter.setItems(books);
}
private void searchFiles() {

View File

@ -52,9 +52,24 @@ import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.CanWrite4G
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.CannotWrite4GbFile
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.NotEnoughSpaceFor4GbFile
import org.kiwix.kiwixmobile.zim_manager.NetworkState.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.fileselect_view.FileSelectListState
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.SelectionMode.MULTI
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.SelectionMode.NORMAL
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.StorageObserver
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskListItem
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk
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.SideEffect
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects.StartMultiSelection
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryListItem
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryListItem.BookItem
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryListItem.DividerItem
@ -79,16 +94,26 @@ class ZimManageViewModel @Inject constructor(
private val defaultLanguageProvider: DefaultLanguageProvider,
private val dataSource: DataSource
) : ViewModel() {
sealed class FileSelectActions {
data class RequestOpen(val bookOnDisk: BookOnDisk) : FileSelectActions()
data class RequestSelect(val bookOnDisk: BookOnDisk) : FileSelectActions()
data class RequestMultiSelection(val bookOnDisk: BookOnDisk) : FileSelectActions()
object RequestDeleteMultiSelection : FileSelectActions()
object RequestShareMultiSelection : FileSelectActions()
object MultiModeFinished : FileSelectActions()
}
val sideEffects = PublishProcessor.create<SideEffect<out Any?>>()
val libraryItems: MutableLiveData<List<LibraryListItem>> = MutableLiveData()
val downloadItems: MutableLiveData<List<DownloadItem>> = MutableLiveData()
val bookItems: MutableLiveData<List<BooksOnDiskListItem>> = MutableLiveData()
val fileSelectListStates: MutableLiveData<FileSelectListState> = MutableLiveData()
val deviceListIsRefreshing = MutableLiveData<Boolean>()
val libraryListIsRefreshing = MutableLiveData<Boolean>()
val networkStates = MutableLiveData<NetworkState>()
val languageItems = MutableLiveData<List<Language>>()
val requestFileSystemCheck = PublishProcessor.create<Unit>()
val fileSelectActions = PublishProcessor.create<FileSelectActions>()
val requestDownloadLibrary = BehaviorProcessor.createDefault<Unit>(Unit)
val requestFiltering = BehaviorProcessor.createDefault<String>("")
val requestLanguagesDialog = PublishProcessor.create<Unit>()
@ -127,10 +152,74 @@ class ZimManageViewModel @Inject constructor(
updateLanguagesInDao(networkLibrary, languages),
updateNetworkStates(),
updateLanguageItemsForDialog(languages),
requestsAndConnectivtyChangesToLibraryRequests(networkLibrary)
requestsAndConnectivtyChangesToLibraryRequests(networkLibrary),
fileSelectActions()
)
}
private fun fileSelectActions() = fileSelectActions.subscribe({
sideEffects.offer(
when (it) {
is RequestOpen -> OpenFile(it.bookOnDisk)
is RequestMultiSelection -> startMultiSelectionAndSelectBook(it.bookOnDisk)
RequestDeleteMultiSelection -> DeleteFiles(selectionsFromState())
RequestShareMultiSelection -> ShareFiles(selectionsFromState())
MultiModeFinished -> noSideEffectAndClearSelectionState()
is RequestSelect -> noSideEffectSelectBook(it.bookOnDisk)
}
)
}, Throwable::printStackTrace)
private fun startMultiSelectionAndSelectBook(
bookOnDisk: BookOnDisk
): StartMultiSelection {
fileSelectListStates.value?.let {
fileSelectListStates.postValue(
it.copy(
bookOnDiskListItems = selectBook(it, bookOnDisk),
selectionMode = MULTI
)
)
}
return StartMultiSelection(bookOnDisk, fileSelectActions)
}
private fun selectBook(
it: FileSelectListState,
bookOnDisk: BookOnDisk
): List<BooksOnDiskListItem> {
return it.bookOnDiskListItems.map { listItem ->
if (listItem.id == bookOnDisk.id) listItem.apply { isSelected = !isSelected }
else listItem
}
}
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
})
)
}
return None
}
private fun selectionsFromState() = fileSelectListStates.value?.selectedBooks ?: emptyList()
private fun noSideEffectAndClearSelectionState(): SideEffect<Unit> {
fileSelectListStates.value?.let {
fileSelectListStates.postValue(
it.copy(
bookOnDiskListItems = it.bookOnDiskListItems.map { it.apply { isSelected = false } },
selectionMode = NORMAL
)
)
}
return None
}
private fun requestsAndConnectivtyChangesToLibraryRequests(library: PublishProcessor<LibraryNetworkEntity>) =
Flowable.combineLatest(
requestDownloadLibrary,
@ -411,10 +500,27 @@ class ZimManageViewModel @Inject constructor(
private fun updateBookItems() =
dataSource.booksOnDiskAsListItems()
.subscribe(
bookItems::postValue,
{ newList ->
fileSelectListStates.postValue(
fileSelectListStates.value?.let { inheritSelections(it, newList) }
?: FileSelectListState(newList)
)
},
Throwable::printStackTrace
)
private fun inheritSelections(
oldState: FileSelectListState,
newList: MutableList<BooksOnDiskListItem>
): FileSelectListState {
return oldState.copy(
bookOnDiskListItems = newList.map { newBookOnDisk ->
val firstOrNull =
oldState.bookOnDiskListItems.firstOrNull { oldBookOnDisk -> oldBookOnDisk.id == newBookOnDisk.id }
newBookOnDisk.apply { isSelected = firstOrNull?.isSelected ?: false }
})
}
private fun removeCompletedDownloadsFromDb(downloadStatuses: Flowable<List<DownloadStatus>>) =
downloadStatuses
.observeOn(Schedulers.io())

View File

@ -0,0 +1,37 @@
/*
* Kiwix Android
* Copyright (C) 2018 Kiwix <android.kiwix.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.kiwix.kiwixmobile.zim_manager.fileselect_view
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.SelectionMode.NORMAL
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskListItem
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk
data class FileSelectListState(
val bookOnDiskListItems: List<BooksOnDiskListItem>,
val selectionMode: SelectionMode = NORMAL
) {
val selectedBooks by lazy {
bookOnDiskListItems.filter { it.isSelected }.filterIsInstance(BookOnDisk::class.java)
}
}
enum class SelectionMode {
NORMAL,
MULTI
}

View File

@ -21,8 +21,8 @@ package org.kiwix.kiwixmobile.zim_manager.fileselect_view
import android.Manifest
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.view.ActionMode
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@ -32,49 +32,49 @@ import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelProviders
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import io.reactivex.disposables.CompositeDisposable
import kotlinx.android.synthetic.main.zim_list.file_management_no_files
import kotlinx.android.synthetic.main.zim_list.zim_swiperefresh
import kotlinx.android.synthetic.main.zim_list.zimfilelist
import org.kiwix.kiwixmobile.R
import org.kiwix.kiwixmobile.R.string
import org.kiwix.kiwixmobile.base.BaseFragment
import org.kiwix.kiwixmobile.data.ZimContentProvider
import org.kiwix.kiwixmobile.database.newdb.dao.NewBookDao
import org.kiwix.kiwixmobile.di.components.ActivityComponent
import org.kiwix.kiwixmobile.extensions.toast
import org.kiwix.kiwixmobile.utils.BookUtils
import org.kiwix.kiwixmobile.utils.Constants.REQUEST_STORAGE_PERMISSION
import org.kiwix.kiwixmobile.utils.DialogShower
import org.kiwix.kiwixmobile.utils.KiwixDialog.DeleteZim
import org.kiwix.kiwixmobile.utils.LanguageUtils
import org.kiwix.kiwixmobile.utils.SharedPreferenceUtil
import org.kiwix.kiwixmobile.utils.files.FileUtils
import org.kiwix.kiwixmobile.zim_manager.ZimManageActivity
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions
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.fileselect_view.adapter.BookOnDiskDelegate.BookDelegate
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BookOnDiskDelegate.LanguageDelegate
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskAdapter
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk
import javax.inject.Inject
class ZimFileSelectFragment : BaseFragment() {
@Inject lateinit var sharedPreferenceUtil: SharedPreferenceUtil
@Inject lateinit var bookDao: NewBookDao
@Inject lateinit var dialogShower: DialogShower
@Inject lateinit var viewModelFactory: ViewModelProvider.Factory
@Inject lateinit var bookUtils: BookUtils
private var actionMode: ActionMode? = null
val disposable = CompositeDisposable()
private val zimManageViewModel: ZimManageViewModel by lazy {
ViewModelProviders.of(activity!!, viewModelFactory)
.get(ZimManageViewModel::class.java)
}
private val bookDelegate: BookDelegate by lazy {
BookDelegate(sharedPreferenceUtil,
{ offerAction(RequestOpen(it)) },
{ offerAction(RequestMultiSelection(it)) },
{ offerAction(RequestSelect(it)) })
}
private val booksOnDiskAdapter: BooksOnDiskAdapter by lazy {
BooksOnDiskAdapter(
BookDelegate(sharedPreferenceUtil, this::openOnClick, this::deleteOnLongClick),
LanguageDelegate
)
BooksOnDiskAdapter(bookDelegate, LanguageDelegate)
}
override fun inject(activityComponent: ActivityComponent) {
@ -101,31 +101,51 @@ class ZimFileSelectFragment : BaseFragment() {
layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
setHasFixedSize(true)
}
zimManageViewModel.bookItems.observe(this, Observer {
booksOnDiskAdapter.itemList = it!!
checkEmpty(it)
})
zimManageViewModel.fileSelectListStates.observe(this, Observer { render(it) })
disposable.add(sideEffects())
zimManageViewModel.deviceListIsRefreshing.observe(this, Observer {
zim_swiperefresh.isRefreshing = it!!
})
}
private fun sideEffects() = zimManageViewModel.sideEffects.subscribe(
{
val effectResult = it.invokeWith(activity!!)
if (effectResult is ActionMode) {
actionMode = effectResult
}
}, Throwable::printStackTrace
)
private fun render(state: FileSelectListState) {
val items = state.bookOnDiskListItems
bookDelegate.selectionMode = state.selectionMode
booksOnDiskAdapter.items = items
actionMode?.title = String.format("%d", state.selectedBooks.size)
file_management_no_files.visibility = if (items.isEmpty()) View.VISIBLE else View.GONE
}
override fun onResume() {
super.onResume()
checkPermissions()
}
private fun checkEmpty(books: List<Any>) {
file_management_no_files.visibility =
if (books.isEmpty()) View.VISIBLE
else View.GONE
override fun onDestroy() {
super.onDestroy()
disposable.clear()
}
private fun offerAction(
action: FileSelectActions
) {
zimManageViewModel.fileSelectActions.offer(action)
}
private fun checkPermissions() {
if (ContextCompat.checkSelfPermission(
activity!!,
Manifest.permission.WRITE_EXTERNAL_STORAGE
) != PackageManager.PERMISSION_GRANTED && Build.VERSION.SDK_INT > 18
) != PackageManager.PERMISSION_GRANTED
) {
context.toast(R.string.request_storage)
requestPermissions(
@ -140,34 +160,4 @@ class ZimFileSelectFragment : BaseFragment() {
private fun requestFileSystemCheck() {
zimManageViewModel.requestFileSystemCheck.onNext(Unit)
}
private fun openOnClick(it: BookOnDisk) {
val file = it.file
ZimContentProvider.canIterate = false
if (!file.canRead()) {
context.toast(string.error_filenotfound)
} else {
(activity as ZimManageActivity).finishResult(file.path)
}
}
private fun deleteOnLongClick(it: BookOnDisk) {
dialogShower.show(DeleteZim, {
if (deleteSpecificZimFile(it)) {
context.toast(string.delete_specific_zim_toast)
} else {
context.toast(string.delete_zim_failed)
}
})
}
private fun deleteSpecificZimFile(book: BookOnDisk): Boolean {
val file = book.file
FileUtils.deleteZimFile(file.path)
if (file.exists()) {
return false
}
bookDao.delete(book.databaseId!!)
return true
}
}

View File

@ -18,11 +18,13 @@
package org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import org.kiwix.kiwixmobile.R
import org.kiwix.kiwixmobile.extensions.inflate
import org.kiwix.kiwixmobile.utils.SharedPreferenceUtil
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.SelectionMode
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.SelectionMode.NORMAL
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BookOnDiskViewHolder.BookViewHolder
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BookOnDiskViewHolder.LanguageItemViewHolder
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.LanguageItem
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.base.AbsDelegateAdapter
@ -33,17 +35,28 @@ sealed class BookOnDiskDelegate<I : BooksOnDiskListItem, VH : BookOnDiskViewHold
class BookDelegate(
val sharedPreferenceUtil: SharedPreferenceUtil,
val clickAction: (BookOnDisk) -> Unit,
val longClickAction: ((BookOnDisk) -> Unit)? = null
val longClickAction: ((BookOnDisk) -> Unit)? = null,
val multiSelectAction: ((BookOnDisk) -> Unit)? = null
) : BookOnDiskDelegate<BookOnDisk, BookViewHolder>() {
override val itemClass = BookOnDisk::class.java
var selectionMode: SelectionMode = NORMAL
override fun bind(
viewHolder: ViewHolder,
itemToBind: BooksOnDiskListItem
) {
(viewHolder as BookOnDiskViewHolder.BookViewHolder).bind((itemToBind as BookOnDisk), selectionMode)
}
override fun createViewHolder(parent: ViewGroup) =
BookViewHolder(
parent.inflate(R.layout.item_book, false),
sharedPreferenceUtil,
clickAction,
longClickAction
longClickAction,
multiSelectAction
)
}

View File

@ -1,7 +1,6 @@
package org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.base.AdapterDelegate
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.base.AdapterDelegateManager
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.base.BaseDelegateAdapter
class BooksOnDiskAdapter(

View File

@ -6,6 +6,7 @@ import java.io.File
import java.util.Locale
sealed class BooksOnDiskListItem {
var isSelected: Boolean = false
abstract val id: Long
data class LanguageItem constructor(

View File

@ -3,6 +3,7 @@ package org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter
import android.graphics.ColorMatrixColorFilter
import android.view.View
import kotlinx.android.synthetic.main.header_language.header_language
import kotlinx.android.synthetic.main.item_book.itemBookCheckbox
import kotlinx.android.synthetic.main.item_book.item_book_article_count
import kotlinx.android.synthetic.main.item_book.item_book_date
import kotlinx.android.synthetic.main.item_book.item_book_description
@ -17,6 +18,9 @@ import org.kiwix.kiwixmobile.main.KiwixWebView
import org.kiwix.kiwixmobile.utils.SharedPreferenceUtil
import org.kiwix.kiwixmobile.zim_manager.KiloByte
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.ArticleCount
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.SelectionMode
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.SelectionMode.MULTI
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.SelectionMode.NORMAL
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.LanguageItem
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.base.BaseViewHolder
@ -28,10 +32,17 @@ sealed class BookOnDiskViewHolder<T : BooksOnDiskListItem>(containerView: View)
containerView: View,
private val sharedPreferenceUtil: SharedPreferenceUtil,
private val clickAction: (BookOnDisk) -> Unit,
private val longClickAction: ((BookOnDisk) -> Unit)?
private val longClickAction: ((BookOnDisk) -> Unit)?,
private val multiSelectAction: ((BookOnDisk) -> Unit)?
) : BookOnDiskViewHolder<BookOnDisk>(containerView) {
override fun bind(item: BookOnDisk) {
}
fun bind(
item: BookOnDisk,
selectionMode: SelectionMode
) {
val book = item.book
item_book_title.text = book.getTitle()
item_book_date.text = book.getDate()
@ -59,24 +70,29 @@ sealed class BookOnDiskViewHolder<T : BooksOnDiskListItem>(containerView: View)
item_book_label_video.visibility = View.GONE
}
containerView.setOnClickListener {
clickAction.invoke(item)
}
containerView.setOnLongClickListener {
longClickAction?.invoke(item)
return@setOnLongClickListener true
when (selectionMode) {
MULTI -> {
itemBookCheckbox.visibility = View.VISIBLE
containerView.setOnClickListener { multiSelectAction?.invoke(item) }
containerView.setOnLongClickListener(null)
}
NORMAL -> {
itemBookCheckbox.visibility = View.GONE
containerView.setOnClickListener { clickAction.invoke(item) }
containerView.setOnLongClickListener {
longClickAction?.invoke(item)
return@setOnLongClickListener true
}
}
}
}
}
class LanguageItemViewHolder(containerView: View) :
BookOnDiskViewHolder<LanguageItem>(containerView) {
override fun bind(item: LanguageItem) {
header_language.text = item.text
}
}
}
class LanguageItemViewHolder(containerView: View) :
BookOnDiskViewHolder<LanguageItem>(containerView) {
override fun bind(item: LanguageItem) {
header_language.text = item.text
}
}

View File

@ -0,0 +1,42 @@
package org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects
import android.app.Activity
import org.kiwix.kiwixmobile.R.string
import org.kiwix.kiwixmobile.database.newdb.dao.NewBookDao
import org.kiwix.kiwixmobile.extensions.toast
import org.kiwix.kiwixmobile.utils.DialogShower
import org.kiwix.kiwixmobile.utils.KiwixDialog.DeleteZim
import org.kiwix.kiwixmobile.utils.files.FileUtils
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk
import javax.inject.Inject
class DeleteFiles(val booksOnDiskListItem: List<BookOnDisk>) :
SideEffect<Unit> {
@Inject lateinit var dialogShower: DialogShower
@Inject lateinit var newBookDao: NewBookDao
override fun invokeWith(activity: Activity) {
activityComponent(activity).inject(this)
booksOnDiskListItem.forEach {
dialogShower.show(DeleteZim, {
if (deleteSpecificZimFile(it)) {
activity.toast(string.delete_specific_zim_toast)
} else {
activity.toast(string.delete_zim_failed)
}
})
}
}
private fun deleteSpecificZimFile(book: BookOnDisk): Boolean {
val file = book.file
FileUtils.deleteZimFile(file.path)
if (file.exists()) {
return false
}
newBookDao.delete(book.databaseId!!)
return true
}
}

View File

@ -0,0 +1,8 @@
package org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects
import android.app.Activity
object None : SideEffect<Unit> {
override fun invokeWith(activity: Activity) {
}
}

View File

@ -0,0 +1,38 @@
/*
* Kiwix Android
* Copyright (C) 2018 Kiwix <android.kiwix.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects
import android.app.Activity
import org.kiwix.kiwixmobile.R
import org.kiwix.kiwixmobile.data.ZimContentProvider
import org.kiwix.kiwixmobile.extensions.toast
import org.kiwix.kiwixmobile.zim_manager.ZimManageActivity
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk
class OpenFile(val bookOnDisk: BookOnDisk): SideEffect<Unit> {
override fun invokeWith(activity: Activity) {
val file = bookOnDisk.file
ZimContentProvider.canIterate = false
if (!file.canRead()) {
activity.toast(R.string.error_filenotfound)
} else {
(activity as ZimManageActivity).finishResult(file.path)
}
}
}

View File

@ -0,0 +1,42 @@
package org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects
import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.os.Build
import androidx.core.content.FileProvider
import org.kiwix.kiwixmobile.BuildConfig
import org.kiwix.kiwixmobile.R
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk
class ShareFiles(val selectedBooks: List<BookOnDisk>) : SideEffect<Unit> {
override fun invokeWith(activity: Activity) {
val selectedFileShareIntent = Intent()
selectedFileShareIntent.action = Intent.ACTION_SEND_MULTIPLE
selectedFileShareIntent.type = "application/octet-stream"
val selectedFileContentURIs = selectedBooks.mapNotNull {
if (Build.VERSION.SDK_INT >= 24) {
FileProvider.getUriForFile(
activity,
BuildConfig.APPLICATION_ID + ".fileprovider",
it.file
)
} else {
Uri.fromFile(it.file)
}
}
selectedFileShareIntent.putParcelableArrayListExtra(
Intent.EXTRA_STREAM,
ArrayList(selectedFileContentURIs)
)
selectedFileShareIntent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
val shareChooserIntent = Intent.createChooser(
selectedFileShareIntent,
activity.getString(R.string.selected_file_cab_app_chooser_title)
)
if (shareChooserIntent.resolveActivity(activity.getPackageManager()) != null) {
activity.startActivity(shareChooserIntent) // Open the app chooser dialog
}
}
}

View File

@ -0,0 +1,28 @@
/*
* Kiwix Android
* Copyright (C) 2018 Kiwix <android.kiwix.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects
import android.app.Activity
import org.kiwix.kiwixmobile.KiwixApplication
interface SideEffect<T:Any?> {
fun invokeWith(activity: Activity):T
fun activityComponent(activity: Activity) =
KiwixApplication.getApplicationComponent().activityComponent().activity(activity).build()
}

View File

@ -0,0 +1,27 @@
package org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects
import android.app.Activity
import android.view.ActionMode
import io.reactivex.processors.PublishProcessor
import org.kiwix.kiwixmobile.R
import org.kiwix.kiwixmobile.extensions.startActionMode
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestDeleteMultiSelection
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestShareMultiSelection
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskListItem
data class StartMultiSelection(
val bookOnDisk: BooksOnDiskListItem.BookOnDisk,
val fileSelectActions: PublishProcessor<FileSelectActions>
) : SideEffect<ActionMode?> {
override fun invokeWith(activity: Activity) =
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) }
)
}

View File

@ -140,7 +140,7 @@ class LibraryFragment : BaseFragment() {
}
private fun onLibraryItemsChange(it: List<LibraryListItem>?) {
libraryAdapter.itemList = it!!
libraryAdapter.items = it!!
if (it.isEmpty()) {
libraryErrorText.setText(
if (isNotConnected) R.string.no_network_connection

View File

@ -30,7 +30,7 @@ abstract class BaseDelegateAdapter<ITEM>(
setHasStableIds(true)
}
var itemList: List<ITEM> = mutableListOf()
var items: List<ITEM> = mutableListOf()
set(value) {
field = value
notifyDataSetChanged()
@ -41,19 +41,19 @@ abstract class BaseDelegateAdapter<ITEM>(
viewType: Int
) = delegateManager.createViewHolder(parent, viewType)
override fun getItemCount() = itemList.size
override fun getItemCount() = items.size
override fun onBindViewHolder(
holder: ViewHolder,
position: Int
) {
delegateManager.onBindViewHolder(itemList[position], holder)
delegateManager.onBindViewHolder(items[position], holder)
}
override fun getItemViewType(position: Int) =
delegateManager.getViewTypeFor(itemList[position])
delegateManager.getViewTypeFor(items[position])
override fun getItemId(position: Int): Long {
return getIdFor(itemList[position])
return getIdFor(items[position])
}
abstract fun getIdFor(item:ITEM):Long

View File

@ -4,7 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:background="?android:attr/selectableItemBackground"
android:paddingEnd="0dp"
android:paddingLeft="0dp"
android:paddingRight="0dp"
@ -12,13 +12,25 @@
android:paddingTop="@dimen/activity_vertical_margin"
>
<CheckBox
android:id="@+id/itemBookCheckbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
android:paddingRight="10dp"
android:paddingEnd="10dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
<ImageView
android:id="@+id/item_book_icon"
android:layout_width="40dp"
android:layout_height="40dp"
android:contentDescription="@string/favicon"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintStart_toEndOf="@id/itemBookCheckbox"
app:layout_constraintTop_toTopOf="parent"
tools:src="@mipmap/kiwix_icon_round"
/>

View File

@ -235,7 +235,7 @@ class ZimManageViewModelTest {
val expectedList = listOf(bookOnDisk())
booksOnDiskListItems.onNext(expectedList)
testScheduler.triggerActions()
viewModel.bookItems.test()
viewModel.fileSelectListStates.test()
.assertValue(expectedList)
}