#1990 more experimentation with history listt rewrite

This commit is contained in:
Frans-Lukas 2020-04-12 18:19:40 +02:00
parent de88dd6c55
commit f3b891ad32
11 changed files with 44 additions and 155 deletions

View File

@ -9,6 +9,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import kotlinx.android.synthetic.main.activity_history.recycler_view import kotlinx.android.synthetic.main.activity_history.recycler_view
import kotlinx.android.synthetic.main.layout_toolbar.toolbar
import org.kiwix.kiwixmobile.core.R import org.kiwix.kiwixmobile.core.R
import org.kiwix.kiwixmobile.core.R.id import org.kiwix.kiwixmobile.core.R.id
import org.kiwix.kiwixmobile.core.R.string import org.kiwix.kiwixmobile.core.R.string
@ -38,9 +39,7 @@ class HistoryActivity : OnItemClickListener, BaseActivity(){
private val compositeDisposable = CompositeDisposable() private val compositeDisposable = CompositeDisposable()
private val historyAdapter: HistoryAdapter2 by lazy { private val historyAdapter: HistoryAdapter2 by lazy {
HistoryAdapter2( HistoryAdapter2(
HistoryItemDelegate(deleteList,this){ HistoryItemDelegate(deleteList, ::onItemClick, ::onItemLongClick)
historyViewModel.actions.offer(OnItemLongClick(it))
}
) )
} }

View File

@ -1,10 +1,10 @@
package org.kiwix.kiwixmobile.core.history package org.kiwix.kiwixmobile.core.history
import android.content.DialogInterface.OnClickListener import android.view.View.OnClickListener
import android.view.View.OnLongClickListener
import android.view.ViewGroup import android.view.ViewGroup
import org.kiwix.kiwixmobile.core.base.adapter.AbsDelegateAdapter import org.kiwix.kiwixmobile.core.base.adapter.AbsDelegateAdapter
import org.kiwix.kiwixmobile.core.extensions.ViewGroupExtensions.inflate import org.kiwix.kiwixmobile.core.extensions.ViewGroupExtensions.inflate
import org.kiwix.kiwixmobile.core.history.HistoryAdapter.OnItemClickListener
import org.kiwix.kiwixmobile.core.history.HistoryListItem.HistoryItem import org.kiwix.kiwixmobile.core.history.HistoryListItem.HistoryItem
sealed class HistoryDelegate<I : HistoryListItem, out VH : HistoryItemViewHolder2<I>> : sealed class HistoryDelegate<I : HistoryListItem, out VH : HistoryItemViewHolder2<I>> :
@ -12,12 +12,13 @@ sealed class HistoryDelegate<I : HistoryListItem, out VH : HistoryItemViewHolder
class HistoryItemDelegate( class HistoryItemDelegate(
private val deleteList: List<HistoryListItem>, private val deleteList: List<HistoryListItem>,
private val itemClickListener: OnItemClickListener private val onClickListener: OnClickListener,
private val onLongClickListener: OnLongClickListener,
) : HistoryDelegate<HistoryItem, HistoryItemViewHolder2.HistoryItemViewHolder>(){ ) : HistoryDelegate<HistoryItem, HistoryItemViewHolder2.HistoryItemViewHolder>(){
override val itemClass = HistoryItem::class.java override val itemClass = HistoryItem::class.java
override fun createViewHolder(parent: ViewGroup) = HistoryItemViewHolder2.HistoryItemViewHolder(parent.inflate(android.R.layout.simple_selectable_list_item, false), override fun createViewHolder(parent: ViewGroup) = HistoryItemViewHolder2.HistoryItemViewHolder(parent.inflate(android.R.layout.simple_selectable_list_item, false),
deleteList, itemClickListener) deleteList, onClickListener, onLongClickListener)
} }
} }

View File

@ -1,6 +1,8 @@
package org.kiwix.kiwixmobile.core.history package org.kiwix.kiwixmobile.core.history
import android.view.View import android.view.View
import android.view.View.OnClickListener
import android.view.View.OnLongClickListener
import kotlinx.android.synthetic.main.item_bookmark_history.favicon import kotlinx.android.synthetic.main.item_bookmark_history.favicon
import kotlinx.android.synthetic.main.item_bookmark_history.title import kotlinx.android.synthetic.main.item_bookmark_history.title
import org.kiwix.kiwixmobile.core.R import org.kiwix.kiwixmobile.core.R
@ -18,7 +20,8 @@ sealed class HistoryItemViewHolder2 <in T : HistoryListItem>(containerView: View
class HistoryItemViewHolder( class HistoryItemViewHolder(
override val containerView: View, override val containerView: View,
private val deleteList: List<HistoryListItem>, private val deleteList: List<HistoryListItem>,
private val itemClickListener: OnItemClickListener private val onClickListener: OnClickListener,
private val onLongClickListener: OnLongClickListener
) : HistoryItemViewHolder2<HistoryItem>(containerView){ ) : HistoryItemViewHolder2<HistoryItem>(containerView){
override fun bind(item: HistoryItem) { override fun bind(item: HistoryItem) {
title.text = item.historyTitle title.text = item.historyTitle
@ -27,8 +30,8 @@ sealed class HistoryItemViewHolder2 <in T : HistoryListItem>(containerView: View
} else { } else {
favicon.setBitmap(Base64String(item.favicon)) favicon.setBitmap(Base64String(item.favicon))
} }
itemView.setOnClickListener { itemClickListener.onItemClick(favicon, item) } itemView.setOnClickListener(onClickListener)
itemView.setOnLongClickListener { itemClickListener.onItemLongClick(favicon, item) } itemView.setOnLongClickListener(onLongClickListener)
} }
} }

View File

@ -31,7 +31,7 @@ sealed class Action {
data class OnItemClick(val historyListItem: HistoryListItem) : Action() data class OnItemClick(val historyListItem: HistoryListItem) : Action()
data class OnItemLongClick(val historyListItem: HistoryListItem) : Action() data class OnItemLongClick(val historyListItem: HistoryListItem) : Action()
data class Filter(val term: String) : Action() data class Filter(val term: String) : Action()
data class ConfirmedDelete(val historyListItem: HistoryListItem) : Action() data class ConfirmedDelete(val historyListItems: List<HistoryListItem.HistoryItem>) : Action()
data class CreatedWithIntent(val intent: Intent?) : Action() data class CreatedWithIntent(val intent: Intent?) : Action()
data class ActivityResultReceived(val requestCode: Int, val resultCode: Int, val data: Intent?) : data class ActivityResultReceived(val requestCode: Int, val resultCode: Int, val data: Intent?) :
Action() Action()

View File

@ -1,17 +1,23 @@
package org.kiwix.kiwixmobile.core.history.ViewModel package org.kiwix.kiwixmobile.core.history.ViewModel
import DeleteRecentSearch
import Finish import Finish
import OpenSearchItem
import ProcessActivityResult import ProcessActivityResult
import SearchInPreviousScreen
import SearchIntentProcessing import SearchIntentProcessing
import ShowToast import ShowToast
import StartSpeechInput import StartSpeechInput
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import io.reactivex.Flowable
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import io.reactivex.processors.BehaviorProcessor import io.reactivex.processors.BehaviorProcessor
import io.reactivex.processors.PublishProcessor import io.reactivex.processors.PublishProcessor
import io.reactivex.schedulers.Schedulers
import org.kiwix.kiwixmobile.core.R import org.kiwix.kiwixmobile.core.R
import org.kiwix.kiwixmobile.core.base.SideEffect import org.kiwix.kiwixmobile.core.base.SideEffect
import org.kiwix.kiwixmobile.core.dao.HistoryDao
import org.kiwix.kiwixmobile.core.history.ViewModel.Action.ActivityResultReceived import org.kiwix.kiwixmobile.core.history.ViewModel.Action.ActivityResultReceived
import org.kiwix.kiwixmobile.core.history.ViewModel.Action.ClickedSearchInText import org.kiwix.kiwixmobile.core.history.ViewModel.Action.ClickedSearchInText
import org.kiwix.kiwixmobile.core.history.ViewModel.Action.ConfirmedDelete import org.kiwix.kiwixmobile.core.history.ViewModel.Action.ConfirmedDelete
@ -23,19 +29,19 @@ import org.kiwix.kiwixmobile.core.history.ViewModel.Action.OnItemLongClick
import org.kiwix.kiwixmobile.core.history.ViewModel.Action.ReceivedPromptForSpeechInput import org.kiwix.kiwixmobile.core.history.ViewModel.Action.ReceivedPromptForSpeechInput
import org.kiwix.kiwixmobile.core.history.ViewModel.Action.StartSpeechInputFailed import org.kiwix.kiwixmobile.core.history.ViewModel.Action.StartSpeechInputFailed
import org.kiwix.kiwixmobile.core.history.ViewModel.State.NoResults import org.kiwix.kiwixmobile.core.history.ViewModel.State.NoResults
import org.kiwix.kiwixmobile.core.search.viewmodel.SearchResultGenerator
import javax.inject.Inject import javax.inject.Inject
class HistoryViewModel @Inject constructor() : ViewModel(){ class HistoryViewModel @Inject constructor(
private val historyDao: HistoryDao,
private val searchResultGenerator: SearchResultGenerator
) : ViewModel(){
val state = MutableLiveData<State>().apply{ value = NoResults("")} val state = MutableLiveData<State>().apply{ value = NoResults("")}
val effects = PublishProcessor.create<SideEffect<*>>() val effects = PublishProcessor.create<SideEffect<*>>()
val actions = PublishProcessor.create<Action>() val actions = PublishProcessor.create<Action>()
private val filter = BehaviorProcessor.createDefault("") private val filter = BehaviorProcessor.createDefault("")
private val compositeDisposable = CompositeDisposable() private val compositeDisposable = CompositeDisposable()
init {
compositeDisposable.addAll(viwStateRduce(),actionMapper())
}
override fun onCleared() { override fun onCleared() {
compositeDisposable.clear() compositeDisposable.clear()
super.onCleared() super.onCleared()
@ -44,8 +50,6 @@ class HistoryViewModel @Inject constructor() : ViewModel(){
private fun actionMapper() = actions.map { private fun actionMapper() = actions.map {
when (it) { when (it) {
ExitedSearch -> effects.offer(Finish) ExitedSearch -> effects.offer(Finish)
is OnItemClick -> saveSearchAndOpenItem(it)
is OnItemLongClick -> showDeleteDialog(it)
is Filter -> filter.offer(it.term) is Filter -> filter.offer(it.term)
ClickedSearchInText -> searchPreviousScreenWhenStateIsValid() ClickedSearchInText -> searchPreviousScreenWhenStateIsValid()
is ConfirmedDelete -> deleteItemAndShowToast(it) is ConfirmedDelete -> deleteItemAndShowToast(it)
@ -54,59 +58,25 @@ class HistoryViewModel @Inject constructor() : ViewModel(){
StartSpeechInputFailed -> effects.offer(ShowToast(R.string.speech_not_supported)) StartSpeechInputFailed -> effects.offer(ShowToast(R.string.speech_not_supported))
is ActivityResultReceived -> is ActivityResultReceived ->
effects.offer(ProcessActivityResult(it.requestCode, it.resultCode, it.data, actions)) effects.offer(ProcessActivityResult(it.requestCode, it.resultCode, it.data, actions))
else -> {}
} }
}.subscribe( }.subscribe(
{}, {},
Throwable::printStackTrace Throwable::printStackTrace
) )
private fun deleteItemAndShowToast(it: ConfirmedDelete) { private fun deleteItemsAndShowToast(it: ConfirmedDelete) {
effects.offer(DeleteRecentSearch(it.searchListItem, recentSearchDao)) effects.offer(DeleteRecentSearch(it.historyListItems, historyDao))
effects.offer(ShowToast(R.string.delete_specific_search_toast)) effects.offer(ShowToast(R.string.delete_specific_search_toast))
}
private fun deleteItemAndShowToast(it: ConfirmedDelete) {
} }
private fun searchPreviousScreenWhenStateIsValid(): Any = private fun searchPreviousScreenWhenStateIsValid(): Any =
effects.offer(SearchInPreviousScreen(state.value!!.searchString)) effects.offer(SearchInPreviousScreen(state.value!!.searchString))
private fun showDeleteDialog(longClick: OnItemLongClick) {
effects.offer(ShowDeleteSearchDialog(longClick.searchListItem, actions))
}
private fun saveSearchAndOpenItem(it: OnItemClick) {
effects.offer(
SaveSearchToRecents(recentSearchDao, it.searchListItem, zimReaderContainer.id)
)
effects.offer(
OpenSearchItem(it.searchListItem)
)
}
private fun viewStateReducer() =
Flowable.combineLatest(
recentSearchDao.recentSearches(zimReaderContainer.id),
searchResultsFromZimReader(),
filter,
searchOrigin,
Function4(this::reduce)
).subscribe(state::postValue, Throwable::printStackTrace)
private fun reduce(
recentSearchResults: List<SearchListItem>,
zimSearchResults: List<SearchListItem>,
searchString: String,
searchOrigin: SearchOrigin
) = when {
searchString.isNotEmpty() && zimSearchResults.isNotEmpty() ->
Results(searchString, zimSearchResults, searchOrigin)
searchString.isEmpty() && recentSearchResults.isNotEmpty() ->
Results(searchString, recentSearchResults, searchOrigin)
else -> NoResults(searchString, searchOrigin)
}
private fun searchResultsFromZimReader() = filter
.distinctUntilChanged()
.switchMap(::searchResults)
private fun searchResults(it: String) = Flowable.fromCallable { private fun searchResults(it: String) = Flowable.fromCallable {
searchResultGenerator.generateSearchResults(it) searchResultGenerator.generateSearchResults(it)
}.subscribeOn(Schedulers.io()) }.subscribeOn(Schedulers.io())

View File

@ -19,14 +19,14 @@
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import org.kiwix.kiwixmobile.core.base.SideEffect import org.kiwix.kiwixmobile.core.base.SideEffect
import org.kiwix.kiwixmobile.core.dao.NewRecentSearchDao import org.kiwix.kiwixmobile.core.dao.HistoryDao
import org.kiwix.kiwixmobile.core.search.adapter.SearchListItem import org.kiwix.kiwixmobile.core.history.HistoryListItem
data class DeleteRecentSearch( data class DeleteRecentSearch(
private val searchListItem: SearchListItem, private val historyListItems: List<HistoryListItem.HistoryItem>,
private val recentSearchDao: NewRecentSearchDao private val historyDao: HistoryDao
) : SideEffect<Unit> { ) : SideEffect<Unit> {
override fun invokeWith(activity: AppCompatActivity) { override fun invokeWith(activity: AppCompatActivity) {
recentSearchDao.deleteSearchString(searchListItem.value) historyDao.deleteHistory(historyListItems)
} }
} }

View File

@ -23,8 +23,8 @@ import android.speech.RecognizerIntent
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import io.reactivex.processors.PublishProcessor import io.reactivex.processors.PublishProcessor
import org.kiwix.kiwixmobile.core.base.SideEffect import org.kiwix.kiwixmobile.core.base.SideEffect
import org.kiwix.kiwixmobile.core.search.viewmodel.Action import org.kiwix.kiwixmobile.core.history.ViewModel.Action
import org.kiwix.kiwixmobile.core.search.viewmodel.Action.Filter import org.kiwix.kiwixmobile.core.history.ViewModel.Action.Filter
data class ProcessActivityResult( data class ProcessActivityResult(
private val requestCode: Int, private val requestCode: Int,

View File

@ -1,33 +0,0 @@
/*
* Kiwix Android
* Copyright (c) 2020 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/>.
*
*/
import androidx.appcompat.app.AppCompatActivity
import org.kiwix.kiwixmobile.core.base.SideEffect
import org.kiwix.kiwixmobile.core.dao.NewRecentSearchDao
import org.kiwix.kiwixmobile.core.search.adapter.SearchListItem
data class SaveSearchToRecents(
private val recentSearchDao: NewRecentSearchDao,
private val searchListItem: SearchListItem,
private val id: String?
) : SideEffect<Unit> {
override fun invokeWith(activity: AppCompatActivity) {
id?.let { recentSearchDao.saveSearch(searchListItem.value, it) }
}
}

View File

@ -23,15 +23,11 @@ import android.os.Build.VERSION_CODES
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import io.reactivex.processors.PublishProcessor import io.reactivex.processors.PublishProcessor
import org.kiwix.kiwixmobile.core.base.SideEffect import org.kiwix.kiwixmobile.core.base.SideEffect
import org.kiwix.kiwixmobile.core.search.viewmodel.Action import org.kiwix.kiwixmobile.core.history.ViewModel.Action
import org.kiwix.kiwixmobile.core.search.viewmodel.Action.Filter import org.kiwix.kiwixmobile.core.history.ViewModel.Action.Filter
import org.kiwix.kiwixmobile.core.search.viewmodel.Action.ReceivedPromptForSpeechInput import org.kiwix.kiwixmobile.core.history.ViewModel.Action.ReceivedPromptForSpeechInput
import org.kiwix.kiwixmobile.core.search.viewmodel.Action.ScreenWasStartedFrom
import org.kiwix.kiwixmobile.core.search.viewmodel.SearchOrigin.FromTabView
import org.kiwix.kiwixmobile.core.search.viewmodel.SearchOrigin.FromWebView
import org.kiwix.kiwixmobile.core.utils.EXTRA_SEARCH import org.kiwix.kiwixmobile.core.utils.EXTRA_SEARCH
import org.kiwix.kiwixmobile.core.utils.EXTRA_IS_WIDGET_VOICE import org.kiwix.kiwixmobile.core.utils.EXTRA_IS_WIDGET_VOICE
import org.kiwix.kiwixmobile.core.utils.TAG_FROM_TAB_SWITCHER
data class SearchIntentProcessing( data class SearchIntentProcessing(
private val intent: Intent?, private val intent: Intent?,
@ -40,12 +36,6 @@ data class SearchIntentProcessing(
@TargetApi(VERSION_CODES.M) @TargetApi(VERSION_CODES.M)
override fun invokeWith(activity: AppCompatActivity) { override fun invokeWith(activity: AppCompatActivity) {
intent?.let { intent?.let {
actions.offer(
ScreenWasStartedFrom(
if (it.getBooleanExtra(TAG_FROM_TAB_SWITCHER, false)) FromTabView
else FromWebView
)
)
if (it.hasExtra(Intent.EXTRA_PROCESS_TEXT)) { if (it.hasExtra(Intent.EXTRA_PROCESS_TEXT)) {
actions.offer(Filter(it.getStringExtra(Intent.EXTRA_PROCESS_TEXT))) actions.offer(Filter(it.getStringExtra(Intent.EXTRA_PROCESS_TEXT)))
} }

View File

@ -1,41 +0,0 @@
/*
* Kiwix Android
* Copyright (c) 2020 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/>.
*
*/
import androidx.appcompat.app.AppCompatActivity
import io.reactivex.processors.PublishProcessor
import org.kiwix.kiwixmobile.core.base.SideEffect
import org.kiwix.kiwixmobile.core.search.SearchActivity
import org.kiwix.kiwixmobile.core.search.adapter.SearchListItem
import org.kiwix.kiwixmobile.core.search.viewmodel.Action
import org.kiwix.kiwixmobile.core.utils.DialogShower
import org.kiwix.kiwixmobile.core.utils.KiwixDialog.DeleteSearch
import javax.inject.Inject
data class ShowDeleteSearchDialog(
private val searchListItem: SearchListItem,
private val actions: PublishProcessor<Action>
) : SideEffect<Unit> {
@Inject lateinit var dialogShower: DialogShower
override fun invokeWith(activity: AppCompatActivity) {
(activity as SearchActivity).activityComponent.inject(this)
dialogShower.show(DeleteSearch, { actions.offer(Action.ConfirmedDelete(searchListItem)) })
}
}

View File

@ -24,8 +24,8 @@ import androidx.appcompat.app.AppCompatActivity
import io.reactivex.processors.PublishProcessor import io.reactivex.processors.PublishProcessor
import org.kiwix.kiwixmobile.core.R import org.kiwix.kiwixmobile.core.R
import org.kiwix.kiwixmobile.core.base.SideEffect import org.kiwix.kiwixmobile.core.base.SideEffect
import org.kiwix.kiwixmobile.core.search.viewmodel.Action import org.kiwix.kiwixmobile.core.history.ViewModel.Action
import org.kiwix.kiwixmobile.core.search.viewmodel.Action.StartSpeechInputFailed import org.kiwix.kiwixmobile.core.history.ViewModel.Action.StartSpeechInputFailed
import java.util.Locale import java.util.Locale
data class StartSpeechInput(private val actions: PublishProcessor<Action>) : SideEffect<Unit> { data class StartSpeechInput(private val actions: PublishProcessor<Action>) : SideEffect<Unit> {