mirror of
https://github.com/sudoxnym/habitica-android.git
synced 2026-04-14 19:56:32 +00:00
Add search to task view
This commit is contained in:
parent
ca68ac1d18
commit
8ad5ae2d90
13 changed files with 91 additions and 22 deletions
|
|
@ -152,7 +152,7 @@ android {
|
|||
buildConfigField "String", "TESTING_LEVEL", "\"production\""
|
||||
multiDexEnabled true
|
||||
|
||||
versionCode 2224
|
||||
versionCode 2226
|
||||
versionName "2.1"
|
||||
}
|
||||
|
||||
|
|
|
|||
4
Habitica/res/drawable/ic_search.xml
Normal file
4
Habitica/res/drawable/ic_search.xml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:viewportHeight="24.0" android:viewportWidth="24.0" android:width="24dp">
|
||||
<path android:fillColor="#FFFFFF" android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/>
|
||||
</vector>
|
||||
12
Habitica/res/layout/toolbar_search.xml
Normal file
12
Habitica/res/layout/toolbar_search.xml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.appcompat.widget.SearchView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:iconifiedByDefault="false"
|
||||
app:searchHintIcon="@null"
|
||||
app:searchIcon="@null"
|
||||
android:theme="@style/SearchViewStyle"
|
||||
app:queryHint="@string/search"
|
||||
tools:background="@color/brand" />
|
||||
|
|
@ -8,10 +8,14 @@
|
|||
android:icon="@drawable/ic_refresh_white"
|
||||
android:title="@string/action_refresh"
|
||||
app:showAsAction="never"
|
||||
android:actionViewClass="android.widget.ImageButton"/>
|
||||
|
||||
<!-- Search, should appear as action button -->
|
||||
app:actionViewClass="android.widget.ImageButton"/>
|
||||
<item android:id="@+id/action_search"
|
||||
android:title="@string/search"
|
||||
android:icon="@drawable/ic_search"
|
||||
app:showAsAction="collapseActionView|always"
|
||||
app:actionLayout="@layout/toolbar_search" />
|
||||
|
||||
<item android:id="@+id/action_filter"
|
||||
android:icon="@drawable/ic_action_filter_list"
|
||||
android:title="@string/filter"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
|
|
|||
|
|
@ -841,4 +841,6 @@
|
|||
<string name="reject">Reject</string>
|
||||
<string name="complete">Complete</string>
|
||||
<string name="repeat_daily">Daily</string>
|
||||
<string name="search">Search</string>
|
||||
<string name="search_hint">Search tasks</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -46,6 +46,8 @@
|
|||
|
||||
<item name="android:statusBarColor">@color/brand_50</item>
|
||||
<item name="android:navigationBarColor">@color/brand_50</item>
|
||||
|
||||
<item name="searchViewStyle">@style/SearchViewStyle</item>
|
||||
</style>
|
||||
|
||||
|
||||
|
|
@ -646,6 +648,11 @@
|
|||
<item name="android:actionMenuTextColor">@color/red_50</item>
|
||||
</style>
|
||||
|
||||
<style name="SearchViewStyle" parent="Widget.AppCompat.SearchView">
|
||||
<item name="android:textColorHint">@color/white_50_alpha</item>
|
||||
<item name="searchHintIcon">@null</item>
|
||||
</style>
|
||||
|
||||
<color name="taskform_gray">#99edecee</color>
|
||||
|
||||
</resources>
|
||||
4
Habitica/res/xml/searchable.xml
Normal file
4
Habitica/res/xml/searchable.xml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:label="@string/app_name"
|
||||
android:hint="@string/search_hint" />
|
||||
|
|
@ -1,11 +1,13 @@
|
|||
package com.habitrpg.android.habitica.helpers
|
||||
|
||||
import com.habitrpg.android.habitica.models.tasks.Task
|
||||
import io.realm.Case
|
||||
import io.realm.OrderedRealmCollection
|
||||
import io.realm.RealmQuery
|
||||
import java.util.*
|
||||
|
||||
class TaskFilterHelper {
|
||||
var searchQuery: String? = null
|
||||
private var tagsId: MutableList<String> = ArrayList()
|
||||
private val activeFilters = HashMap<String, String>()
|
||||
|
||||
|
|
@ -96,6 +98,9 @@ class TaskFilterHelper {
|
|||
if (tagsId.size > 0) {
|
||||
query = query.`in`("tags.id", tagsId.toTypedArray())
|
||||
}
|
||||
if (searchQuery?.isNotEmpty() == true) {
|
||||
query = query.beginsWith("text", searchQuery ?: "", Case.INSENSITIVE)
|
||||
}
|
||||
if (activeFilter != null && activeFilter != Task.FILTER_ALL) {
|
||||
when (activeFilter) {
|
||||
Task.FILTER_ACTIVE -> query = if (Task.TYPE_DAILY == taskType) {
|
||||
|
|
|
|||
|
|
@ -236,7 +236,10 @@ open class Task : RealmObject, Parcelable {
|
|||
}
|
||||
return if (Task::class.java.isAssignableFrom(other.javaClass)) {
|
||||
val otherTask = other as? Task
|
||||
this.id == otherTask?.id
|
||||
if (!this.isValid || otherTask?.isValid != true) {
|
||||
return false
|
||||
}
|
||||
this.id == otherTask.id
|
||||
} else {
|
||||
super.equals(other)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import android.content.Intent
|
|||
import android.graphics.PorterDuff
|
||||
import android.os.Bundle
|
||||
import android.view.*
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.fragment.app.FragmentPagerAdapter
|
||||
import com.habitrpg.android.habitica.HabiticaBaseApplication
|
||||
import com.habitrpg.android.habitica.R
|
||||
|
|
@ -25,7 +26,8 @@ import java.util.*
|
|||
import javax.inject.Inject
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
class TasksFragment : BaseMainFragment() {
|
||||
|
||||
class TasksFragment : BaseMainFragment(), SearchView.OnQueryTextListener {
|
||||
|
||||
var viewPager: androidx.viewpager.widget.ViewPager? = null
|
||||
@Inject
|
||||
|
|
@ -108,13 +110,40 @@ class TasksFragment : BaseMainFragment() {
|
|||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.menu_main_activity, menu)
|
||||
|
||||
filterMenuItem = menu.findItem(R.id.action_search)
|
||||
filterMenuItem = menu.findItem(R.id.action_filter)
|
||||
updateFilterIcon()
|
||||
|
||||
val item = menu.findItem(R.id.action_search)
|
||||
val sv = item.actionView as? SearchView
|
||||
sv?.setOnQueryTextListener(this)
|
||||
sv?.setIconifiedByDefault(false)
|
||||
item.setOnActionExpandListener(object : MenuItem.OnActionExpandListener {
|
||||
override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
|
||||
filterMenuItem?.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onMenuItemActionExpand(item: MenuItem): Boolean {
|
||||
// Do something when expanded
|
||||
filterMenuItem?.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onQueryTextSubmit(query: String?): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onQueryTextChange(newText: String?): Boolean {
|
||||
taskFilterHelper.searchQuery = newText
|
||||
viewFragmentsDictionary?.values?.forEach { values -> values.recyclerAdapter?.filter() }
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return when (item.itemId) {
|
||||
R.id.action_search -> {
|
||||
R.id.action_filter -> {
|
||||
showFilterDialog()
|
||||
true
|
||||
}
|
||||
|
|
@ -145,14 +174,11 @@ class TasksFragment : BaseMainFragment() {
|
|||
if (viewFragmentsDictionary == null) {
|
||||
return
|
||||
}
|
||||
val activePos = viewPager?.currentItem ?: 0
|
||||
viewFragmentsDictionary?.get(activePos - 1)?.recyclerAdapter?.filter()
|
||||
viewFragmentsDictionary?.get(activePos + 1)?.recyclerAdapter?.filter()
|
||||
taskFilterHelper.tags = activeTags
|
||||
if (activeTaskFilter != null) {
|
||||
activeFragment?.setActiveFilter(activeTaskFilter)
|
||||
}
|
||||
viewFragmentsDictionary?.values?.forEach { it.recyclerAdapter?.filter() }
|
||||
viewFragmentsDictionary?.values?.forEach { values -> values.recyclerAdapter?.filter() }
|
||||
updateFilterIcon()
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import androidx.core.view.ViewCompat
|
|||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.SimpleItemAnimator
|
||||
import com.habitrpg.shared.habitica.LogLevel
|
||||
import com.habitrpg.shared.habitica.Logger
|
||||
import com.habitrpg.shared.habitica.HLogger
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
|
|
@ -226,8 +226,8 @@ class SafeDefaultItemAnimator : SimpleItemAnimator() {
|
|||
if (deltaY != 0) {
|
||||
view.translationY = (-deltaY).toFloat()
|
||||
}
|
||||
Logger.log(LogLevel.INFO, "Moving1", "$toX, $fromX, $deltaX")
|
||||
Logger.log(LogLevel.INFO, "Moving1", "$toX, $fromX, $deltaX")
|
||||
HLogger.log(LogLevel.INFO, "Moving1", "$toX, $fromX, $deltaX")
|
||||
HLogger.log(LogLevel.INFO, "Moving1", "$toX, $fromX, $deltaX")
|
||||
pendingMoves.add(MoveInfo(holder, newFromX, newFromY, toX, toY))
|
||||
return true
|
||||
}
|
||||
|
|
@ -242,8 +242,8 @@ class SafeDefaultItemAnimator : SimpleItemAnimator() {
|
|||
if (deltaY != 0) {
|
||||
view.animate().translationY(0f)
|
||||
}
|
||||
Logger.log(LogLevel.INFO, "Moving", "$toX, $fromX, $deltaX")
|
||||
Logger.log(LogLevel.INFO, "Moving", "$toX, $fromX, $deltaX")
|
||||
HLogger.log(LogLevel.INFO, "Moving", "$toX, $fromX, $deltaX")
|
||||
HLogger.log(LogLevel.INFO, "Moving", "$toX, $fromX, $deltaX")
|
||||
// vpas are canceled (and can't end them. why?)
|
||||
// need listener functionality in VPACompat for this. Ick.
|
||||
val animation = view.animate()
|
||||
|
|
@ -295,8 +295,8 @@ class SafeDefaultItemAnimator : SimpleItemAnimator() {
|
|||
newHolder.itemView.translationY = (-deltaY).toFloat()
|
||||
newHolder.itemView.alpha = 0f
|
||||
}
|
||||
Logger.log(LogLevel.INFO, "Changing", "$toX, $fromX, $deltaX")
|
||||
Logger.log(LogLevel.INFO, "Changing", "$toX, $fromX, $deltaX")
|
||||
HLogger.log(LogLevel.INFO, "Changing", "$toX, $fromX, $deltaX")
|
||||
HLogger.log(LogLevel.INFO, "Changing", "$toX, $fromX, $deltaX")
|
||||
newHolder?.let { pendingChanges.add(ChangeInfo(oldHolder, it, fromX, fromY, toX, toY)) }
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,9 +63,11 @@ private class MessagesDataSource(val socialRepository: SocialRepository, val rec
|
|||
override fun loadInitial(params: LoadInitialParams, callback: LoadInitialCallback<ChatMessage>) {
|
||||
lastFetchWasEnd = false
|
||||
GlobalScope.launch(Dispatchers.Main.immediate) {
|
||||
socialRepository.getInboxMessages(recipientID).firstElement()
|
||||
socialRepository.getInboxMessages(recipientID)
|
||||
.map { socialRepository.getUnmanagedCopy(it) }
|
||||
.firstElement()
|
||||
.flatMapPublisher {
|
||||
if (it.size == 0) {
|
||||
if (it.isEmpty()) {
|
||||
socialRepository.retrieveInboxMessages(recipientID, 0)
|
||||
.doOnNext {
|
||||
messages -> if (messages.size != 10) lastFetchWasEnd = true
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ enum class LogLevel {
|
|||
ERROR, INFO, DEBUG
|
||||
}
|
||||
|
||||
class Logger {
|
||||
class HLogger {
|
||||
|
||||
companion object {
|
||||
private val platformLogger = PlatformLogger()
|
||||
Loading…
Reference in a new issue