Use coroutines for search

pull/1715/head
Alex Baker 3 years ago
parent 8181d20ff5
commit da967afaf5

@ -41,13 +41,8 @@ import com.todoroo.astrid.service.*
import com.todoroo.astrid.timers.TimerPlugin import com.todoroo.astrid.timers.TimerPlugin
import com.todoroo.astrid.utility.Flags import com.todoroo.astrid.utility.Flags
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import io.reactivex.android.schedulers.AndroidSchedulers import kotlinx.coroutines.*
import io.reactivex.disposables.Disposable import kotlinx.coroutines.flow.*
import io.reactivex.subjects.PublishSubject
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.tasks.LocalBroadcastManager import org.tasks.LocalBroadcastManager
import org.tasks.R import org.tasks.R
import org.tasks.ShortcutManager import org.tasks.ShortcutManager
@ -82,7 +77,6 @@ import timber.log.Timber
import java.text.ParseException import java.text.ParseException
import java.time.format.FormatStyle import java.time.format.FormatStyle
import java.util.* import java.util.*
import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
import kotlin.math.max import kotlin.math.max
@ -126,8 +120,7 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
private lateinit var taskAdapter: TaskAdapter private lateinit var taskAdapter: TaskAdapter
private var recyclerAdapter: TaskListRecyclerAdapter? = null private var recyclerAdapter: TaskListRecyclerAdapter? = null
private lateinit var filter: Filter private lateinit var filter: Filter
private val searchSubject = PublishSubject.create<String>() private var searchJob: Job? = null
private var searchDisposable: Disposable? = null
private lateinit var search: MenuItem private lateinit var search: MenuItem
private var searchQuery: String? = null private var searchQuery: String? = null
private var mode: ActionMode? = null private var mode: ActionMode? = null
@ -287,13 +280,17 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
} }
private fun searchByQuery(query: String?) { private fun searchByQuery(query: String?) {
searchQuery = query?.trim { it <= ' ' } ?: "" searchJob?.cancel()
if (searchQuery?.isEmpty() == true) { searchJob = lifecycleScope.launch {
listViewModel.searchByFilter( delay(SEARCH_DEBOUNCE_TIMEOUT)
searchQuery = query?.trim { it <= ' ' } ?: ""
if (searchQuery?.isEmpty() == true) {
listViewModel.searchByFilter(
BuiltInFilterExposer.getMyTasksFilter(requireContext().resources)) BuiltInFilterExposer.getMyTasksFilter(requireContext().resources))
} else { } else {
val savedFilter = createSearchFilter(searchQuery!!) val savedFilter = createSearchFilter(searchQuery!!)
listViewModel.searchByFilter(savedFilter) listViewModel.searchByFilter(savedFilter)
}
} }
} }
@ -465,13 +462,6 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
localBroadcastManager.unregisterReceiver(refreshReceiver) localBroadcastManager.unregisterReceiver(refreshReceiver)
} }
override fun onDestroyView() {
super.onDestroyView()
if (searchDisposable != null && !searchDisposable!!.isDisposed) {
searchDisposable!!.dispose()
}
}
fun collapseSearchView(): Boolean { fun collapseSearchView(): Boolean {
return (search.isActionViewExpanded return (search.isActionViewExpanded
&& (search.collapseActionView() || !search.isActionViewExpanded)) && (search.collapseActionView() || !search.isActionViewExpanded))
@ -569,10 +559,6 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
} }
override fun onMenuItemActionExpand(item: MenuItem): Boolean { override fun onMenuItemActionExpand(item: MenuItem): Boolean {
searchDisposable = searchSubject
.debounce(SEARCH_DEBOUNCE_TIMEOUT.toLong(), TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe { query: String? -> searchByQuery(query) }
if (searchQuery == null) { if (searchQuery == null) {
searchByQuery("") searchByQuery("")
} }
@ -585,7 +571,7 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
override fun onMenuItemActionCollapse(item: MenuItem): Boolean { override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
listViewModel.searchByFilter(filter) listViewModel.searchByFilter(filter)
searchDisposable?.dispose() searchJob?.cancel()
searchQuery = null searchQuery = null
setupMenu() setupMenu()
return true return true
@ -598,7 +584,7 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
} }
override fun onQueryTextChange(query: String): Boolean { override fun onQueryTextChange(query: String): Boolean {
searchSubject.onNext(query) searchByQuery(query)
return true return true
} }
@ -890,7 +876,7 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
private const val REQUEST_LIST_SETTINGS = 10101 private const val REQUEST_LIST_SETTINGS = 10101
private const val REQUEST_MOVE_TASKS = 10103 private const val REQUEST_MOVE_TASKS = 10103
private const val REQUEST_TAG_TASKS = 10106 private const val REQUEST_TAG_TASKS = 10106
private const val SEARCH_DEBOUNCE_TIMEOUT = 300 private const val SEARCH_DEBOUNCE_TIMEOUT = 300L
fun newTaskListFragment(context: Context, filter: Filter?): TaskListFragment { fun newTaskListFragment(context: Context, filter: Filter?): TaskListFragment {
val fragment = TaskListFragment() val fragment = TaskListFragment()
val bundle = Bundle() val bundle = Bundle()

@ -12,7 +12,6 @@ import androidx.appcompat.widget.SearchView
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.widget.ContentLoadingProgressBar import androidx.core.widget.ContentLoadingProgressBar
import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
@ -22,9 +21,8 @@ import com.google.android.material.appbar.AppBarLayout.OnOffsetChangedListener
import com.google.android.material.appbar.CollapsingToolbarLayout import com.google.android.material.appbar.CollapsingToolbarLayout
import com.todoroo.andlib.utility.AndroidUtilities import com.todoroo.andlib.utility.AndroidUtilities
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import io.reactivex.android.schedulers.AndroidSchedulers import kotlinx.coroutines.Job
import io.reactivex.disposables.CompositeDisposable import kotlinx.coroutines.delay
import io.reactivex.subjects.PublishSubject
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.tasks.Event import org.tasks.Event
import org.tasks.PermissionUtil.verifyPermissions import org.tasks.PermissionUtil.verifyPermissions
@ -52,7 +50,6 @@ import org.tasks.preferences.Preferences
import org.tasks.themes.ColorProvider import org.tasks.themes.ColorProvider
import org.tasks.themes.Theme import org.tasks.themes.Theme
import timber.log.Timber import timber.log.Timber
import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
import kotlin.math.abs import kotlin.math.abs
@ -80,14 +77,13 @@ class LocationPickerActivity : InjectingAppCompatActivity(), Toolbar.OnMenuItemC
@Inject lateinit var firebase: Firebase @Inject lateinit var firebase: Firebase
@Inject lateinit var preferences: Preferences @Inject lateinit var preferences: Preferences
private var disposables: CompositeDisposable? = null
private var mapPosition: MapPosition? = null private var mapPosition: MapPosition? = null
private var recentsAdapter: LocationPickerAdapter? = null private var recentsAdapter: LocationPickerAdapter? = null
private var searchAdapter: LocationSearchAdapter? = null private var searchAdapter: LocationSearchAdapter? = null
private var places: List<PlaceUsage> = emptyList() private var places: List<PlaceUsage> = emptyList()
private var offset = 0 private var offset = 0
private lateinit var search: MenuItem private lateinit var search: MenuItem
private val searchSubject = PublishSubject.create<String>() private var searchJob: Job? = null
private val viewModel: PlaceSearchViewModel by viewModels() private val viewModel: PlaceSearchViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -290,12 +286,12 @@ class LocationPickerActivity : InjectingAppCompatActivity(), Toolbar.OnMenuItemC
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
map.onResume() map.onResume()
viewModel.observe(this, Observer { list: List<PlaceSearchResult?>? -> searchAdapter!!.submitList(list) }, Observer { place: Place? -> returnPlace(place) }, Observer { error: Event<String> -> handleError(error) }) viewModel.observe(
disposables = CompositeDisposable( this,
searchSubject { searchAdapter!!.submitList(it) },
.debounce(SEARCH_DEBOUNCE_TIMEOUT.toLong(), TimeUnit.MILLISECONDS) { returnPlace(it) },
.observeOn(AndroidSchedulers.mainThread()) { handleError(it) }
.subscribe { query: String? -> viewModel.query(query, map.mapPosition) }) )
} }
override fun onDestroy() { override fun onDestroy() {
@ -341,7 +337,6 @@ class LocationPickerActivity : InjectingAppCompatActivity(), Toolbar.OnMenuItemC
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
map.onPause() map.onPause()
disposables!!.dispose()
} }
override fun onSaveInstanceState(outState: Bundle) { override fun onSaveInstanceState(outState: Bundle) {
@ -370,7 +365,11 @@ class LocationPickerActivity : InjectingAppCompatActivity(), Toolbar.OnMenuItemC
override fun onQueryTextSubmit(query: String): Boolean = false override fun onQueryTextSubmit(query: String): Boolean = false
override fun onQueryTextChange(query: String): Boolean { override fun onQueryTextChange(query: String): Boolean {
searchSubject.onNext(query) searchJob?.cancel()
searchJob = lifecycleScope.launch {
delay(SEARCH_DEBOUNCE_TIMEOUT)
viewModel.query(query, map.mapPosition)
}
return true return true
} }
@ -396,6 +395,6 @@ class LocationPickerActivity : InjectingAppCompatActivity(), Toolbar.OnMenuItemC
const val EXTRA_PLACE = "extra_place" const val EXTRA_PLACE = "extra_place"
private const val EXTRA_MAP_POSITION = "extra_map_position" private const val EXTRA_MAP_POSITION = "extra_map_position"
private const val EXTRA_APPBAR_OFFSET = "extra_appbar_offset" private const val EXTRA_APPBAR_OFFSET = "extra_appbar_offset"
private const val SEARCH_DEBOUNCE_TIMEOUT = 300 private const val SEARCH_DEBOUNCE_TIMEOUT = 300L
} }
} }
Loading…
Cancel
Save