From 85608b04d19a9b7526230c88bd3afd605b80ea56 Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 21 Jan 2019 19:39:45 +0100 Subject: [PATCH] Room list : quickly branch filter room name field --- .../riotredesign/core/extensions/EditText.kt | 44 ++++++++++++++++++ .../riotredesign/core/extensions/View.kt | 10 ++++ .../home/room/list/RoomListActions.kt | 2 + .../home/room/list/RoomListFragment.kt | 21 +++++++++ .../home/room/list/RoomListViewModel.kt | 35 +++++++++++--- .../main/res/drawable-hdpi/ic_clear_white.png | Bin 0 -> 276 bytes .../main/res/drawable-mdpi/ic_clear_white.png | Bin 0 -> 209 bytes .../res/drawable-xhdpi/ic_clear_white.png | Bin 0 -> 329 bytes .../res/drawable-xxhdpi/ic_clear_white.png | Bin 0 -> 462 bytes .../res/drawable-xxxhdpi/ic_clear_white.png | Bin 0 -> 601 bytes .../main/res/layout/fragment_room_list.xml | 7 +-- 11 files changed, 109 insertions(+), 10 deletions(-) create mode 100644 app/src/main/java/im/vector/riotredesign/core/extensions/EditText.kt create mode 100644 app/src/main/java/im/vector/riotredesign/core/extensions/View.kt create mode 100644 app/src/main/res/drawable-hdpi/ic_clear_white.png create mode 100644 app/src/main/res/drawable-mdpi/ic_clear_white.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_clear_white.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_clear_white.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_clear_white.png diff --git a/app/src/main/java/im/vector/riotredesign/core/extensions/EditText.kt b/app/src/main/java/im/vector/riotredesign/core/extensions/EditText.kt new file mode 100644 index 0000000000..bace132136 --- /dev/null +++ b/app/src/main/java/im/vector/riotredesign/core/extensions/EditText.kt @@ -0,0 +1,44 @@ +package im.vector.riotredesign.core.extensions + +import android.text.Editable +import android.text.InputType +import android.text.TextWatcher +import android.view.MotionEvent +import android.view.View +import android.view.inputmethod.EditorInfo +import android.widget.EditText +import im.vector.riotredesign.R + +fun EditText.setupAsSearch() { + addTextChangedListener(object : TextWatcher { + override fun afterTextChanged(editable: Editable?) { + val clearIcon = if (editable?.isNotEmpty() == true) R.drawable.ic_clear_white else 0 + setCompoundDrawablesWithIntrinsicBounds(0, 0, clearIcon, 0) + } + + override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit + override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) = Unit + }) + + maxLines = 1 + inputType = InputType.TYPE_CLASS_TEXT + imeOptions = EditorInfo.IME_ACTION_SEARCH + setOnEditorActionListener { _, actionId, event -> + var consumed = false + if (actionId == EditorInfo.IME_ACTION_SEARCH) { + hideKeyboard() + consumed = true + } + consumed + } + + setOnTouchListener(View.OnTouchListener { _, event -> + if (event.action == MotionEvent.ACTION_UP) { + if (event.rawX >= (this.right - this.compoundPaddingRight)) { + text = null + return@OnTouchListener true + } + } + return@OnTouchListener false + }) +} diff --git a/app/src/main/java/im/vector/riotredesign/core/extensions/View.kt b/app/src/main/java/im/vector/riotredesign/core/extensions/View.kt new file mode 100644 index 0000000000..802172906d --- /dev/null +++ b/app/src/main/java/im/vector/riotredesign/core/extensions/View.kt @@ -0,0 +1,10 @@ +package im.vector.riotredesign.core.extensions + +import android.content.Context +import android.view.View +import android.view.inputmethod.InputMethodManager + +fun View.hideKeyboard() { + val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + imm.hideSoftInputFromWindow(windowToken, 0) +} \ No newline at end of file diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListActions.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListActions.kt index 17b8ec9b94..bfed443141 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListActions.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListActions.kt @@ -8,4 +8,6 @@ sealed class RoomListActions { object RoomDisplayed : RoomListActions() + data class FilterRooms(val roomName: CharSequence? = null) : RoomListActions() + } \ No newline at end of file diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListFragment.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListFragment.kt index f66ee63011..43a2ef97d0 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListFragment.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListFragment.kt @@ -1,9 +1,12 @@ package im.vector.riotredesign.features.home.room.list import android.os.Bundle +import android.text.Editable +import android.text.TextWatcher import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.view.inputmethod.EditorInfo import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Incomplete import com.airbnb.mvrx.Success @@ -11,6 +14,8 @@ import com.airbnb.mvrx.activityViewModel import im.vector.matrix.android.api.failure.Failure import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.riotredesign.R +import im.vector.riotredesign.core.extensions.hideKeyboard +import im.vector.riotredesign.core.extensions.setupAsSearch import im.vector.riotredesign.core.platform.RiotFragment import im.vector.riotredesign.core.platform.StateView import im.vector.riotredesign.features.home.HomeNavigator @@ -38,6 +43,7 @@ class RoomListFragment : RiotFragment(), RoomSummaryController.Callback { roomController = RoomSummaryController(this) stateView.contentView = epoxyRecyclerView epoxyRecyclerView.setController(roomController) + setupFilterView() homeViewModel.subscribe { renderState(it) } } @@ -70,6 +76,21 @@ class RoomListFragment : RiotFragment(), RoomSummaryController.Callback { stateView.state = StateView.State.Error(message) } + private fun setupFilterView() { + filterRoomView.setupAsSearch() + filterRoomView.addTextChangedListener(object : TextWatcher { + override fun afterTextChanged(s: Editable?) = Unit + + override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit + + override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { + homeViewModel.accept(RoomListActions.FilterRooms(s)) + } + }) + } + + // RoomSummaryController.Callback ************************************************************** + override fun onRoomSelected(room: RoomSummary) { homeViewModel.accept(RoomListActions.SelectRoom(room)) homeNavigator.openRoomDetail(room.roomId, null) diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListViewModel.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListViewModel.kt index c39317aa0e..cbf2a4897f 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListViewModel.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListViewModel.kt @@ -3,6 +3,7 @@ package im.vector.riotredesign.features.home.room.list import arrow.core.Option import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext +import com.jakewharton.rxrelay2.BehaviorRelay import im.vector.matrix.android.api.Matrix import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.group.model.GroupSummary @@ -12,10 +13,12 @@ import im.vector.riotredesign.core.platform.RiotViewModel import im.vector.riotredesign.features.home.group.SelectedGroupHolder import im.vector.riotredesign.features.home.room.VisibleRoomHolder import io.reactivex.Observable -import io.reactivex.functions.BiFunction +import io.reactivex.functions.Function3 import io.reactivex.rxkotlin.subscribeBy import org.koin.android.ext.android.get +typealias RoomListFilterName = CharSequence + class RoomListViewModel(initialState: RoomListViewState, private val session: Session, private val selectedGroupHolder: SelectedGroupHolder, @@ -35,6 +38,9 @@ class RoomListViewModel(initialState: RoomListViewState, } } + + private val roomListFilter = BehaviorRelay.createDefault>(Option.empty()) + init { observeRoomSummaries() observeVisibleRoom() @@ -42,7 +48,8 @@ class RoomListViewModel(initialState: RoomListViewState, fun accept(action: RoomListActions) { when (action) { - is RoomListActions.SelectRoom -> handleSelectRoom(action) + is RoomListActions.SelectRoom -> handleSelectRoom(action) + is RoomListActions.FilterRooms -> handleFilterRooms(action) } } @@ -54,6 +61,11 @@ class RoomListViewModel(initialState: RoomListViewState, } } + private fun handleFilterRooms(action: RoomListActions.FilterRooms) { + val optionalFilter = Option.fromNullable(action.roomName) + roomListFilter.accept(optionalFilter) + } + private fun observeVisibleRoom() { visibleRoomHolder.visibleRoom() .subscribeBy { @@ -63,13 +75,22 @@ class RoomListViewModel(initialState: RoomListViewState, } private fun observeRoomSummaries() { - Observable.combineLatest, Option, RoomSummaries>( + Observable.combineLatest, Option, Option, RoomSummaries>( session.rx().liveRoomSummaries(), selectedGroupHolder.selectedGroup(), - BiFunction { rooms, selectedGroupOption -> - val selectedGroup = selectedGroupOption.orNull() + roomListFilter, + Function3 { rooms, selectedGroupOption, filterRoomOption -> + val filterRoom = filterRoomOption.orNull() + val filteredRooms = rooms.filter { + if (filterRoom.isNullOrBlank()) { + true + } else { + it.displayName.contains(other = filterRoom, ignoreCase = true) + } + } - val filteredDirectRooms = rooms + val selectedGroup = selectedGroupOption.orNull() + val filteredDirectRooms = filteredRooms .filter { it.isDirect } .filter { if (selectedGroup == null) { @@ -81,7 +102,7 @@ class RoomListViewModel(initialState: RoomListViewState, } } - val filteredGroupRooms = rooms + val filteredGroupRooms = filteredRooms .filter { !it.isDirect } .filter { selectedGroup?.roomIds?.contains(it.roomId) ?: true diff --git a/app/src/main/res/drawable-hdpi/ic_clear_white.png b/app/src/main/res/drawable-hdpi/ic_clear_white.png new file mode 100644 index 0000000000000000000000000000000000000000..fc5a294ed5ff9e16a4cda3e88eb1beb1a078de96 GIT binary patch literal 276 zcmV+v0qg#WP)lUcPU>TM!j6?-=93f~FWKwOUldq_*wN)Ly`le0JY$7NT;DXh4!tA}9z`v#CR% zB}=dr62O?+V=qBb7^Dt`z-yLpjo)buS{O?x8pBlTU$a4D*sG(2vP@MU2GrG(l}?jA zV^jY-+RQZrfh2W?MzzEs*9-(gDlm*X~)of^&6pdjFSw?%K! z@qhbE1hZamlr#`9+%3%F#P(hP{KY*!T1%Jg>zUP*$ZCBw$b`XA#641#Dei!O#RP*t zC*DVOI;2bP0 Hl+XkK)aOdC literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_clear_white.png b/app/src/main/res/drawable-xhdpi/ic_clear_white.png new file mode 100644 index 0000000000000000000000000000000000000000..0b00a33a728a56902219f949ade2a25821dc9276 GIT binary patch literal 329 zcmV-P0k-~$P)-Hp^2CxGti$Q;Z^2qD!Xpyz3;wxcLE3?fB*sr@K1nC z*e>jz5XOc*Z)Z>d@xeF$SSuJ|w`az+!X}>Cv)%zD zqB#87TPTGXYKJemMu!3fEPQDlID>$OM-;LYVyqD!Rf)$p5R~x9R^kXj3yvk`%46&{AjIcz3X)jh<-8f+~U23w9b*s3vFoS36UO{WDAKmY** b5CFaJELQUamZcBV00000NkvXXu0mjf$2@;u literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxhdpi/ic_clear_white.png b/app/src/main/res/drawable-xxhdpi/ic_clear_white.png new file mode 100644 index 0000000000000000000000000000000000000000..717c7b59190385c7c55c515beb1f45439eee6d19 GIT binary patch literal 462 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1|&n@Zgyv2V4Ujd;uunK>+P+Bd4~*mjurad zJya|Ev~7y-*f|&&Sp*!w1Pg@9W~-Y2 z$l$2LoeQ@`j%#ka*^oA;`|mn^!Am{Ham70-6e}Ok;K=Mu=3X*a>V=q}$tB%6s+Zz8 zza;iwFHc_LZc+bw0gKc+4z49ab6%~#p&ZB>#L@R$Q>;^@?Qt#tEMcp^lYgu<`WP@r z%>3mvkB1g(y?Vtwgyu}!IzxRD&+^iZuNqa3cCL8V-o)EiZl-a(Gpl@0(-bE4P4ZuO zf~4lWXp7Vgl9F+C-}vOUNAu#Wo%^~}GLL9RFHSC#DZRW;#IucAz~1oR63b`TOU_Kc zHzCwEPxFHD)58-uHKqO9GCef{d>_ta($w~QqTs;v4uu9V!2zMFy5$om-}hO)V5KH7o)|n`{an^LB{Ts5sZG74 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxxhdpi/ic_clear_white.png b/app/src/main/res/drawable-xxxhdpi/ic_clear_white.png new file mode 100644 index 0000000000000000000000000000000000000000..9ef2d8f9fbe5914285f0776e412c4d1be25d17aa GIT binary patch literal 601 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7Ro>U{dgOaSW-r_4ZC+UbBIS%f%dp zw?z$e53|kETYi>JV@2nbevLzo>E~~VXZ|}qXB+3Y!^v<76Oj13%SGvlPQ2{H$e#~6cAgG*tq85ta5{3<=kvDL;Vg;XKfh-eF}%ooYNfDb ziu|W=>C$9Aj-#jh|A{T>Q3$N-d(Pm>GV@chEvL4k$JF`7x*DPj^B4ZP(_L8W$a3`yKlehY9-ozqI*i9SVs4$z`47%+TcfQ!9FsyHdo|^;^_y zSejygX3hRoEueEcd`?#d`@wBBS2s?X;d`RHW&PO55cgztX3> zd(`%ICm3=(GtsJ8yS9db;X%BB{2_)9rc>Ar8OqjX?doy7rE_fQy0<0gINm+A=3_aX zcj8U#)h#Y7mexeK%sRE*WyRW>rI8_-4KlYo_A)!YUC(+_dCP~W2_{b3?^8}HZTaFC z_C#luNyDW(=N7bGxWZl7#j$p(e*1-_pFtdJr|CBf6b3K8sTX=m|I7=94RSAw3~wvl zY1%MvUTN7+o{H876$!x}KQG>Inx|+X7bzA746p{#gMTmVyD(YUa@(d)$=aN_;*enh fFd`M6eq#S;eQNDygJ~+j2xjnf^>bP0l+XkKX6gf2 literal 0 HcmV?d00001 diff --git a/app/src/main/res/layout/fragment_room_list.xml b/app/src/main/res/layout/fragment_room_list.xml index 96c1eead58..89af705895 100644 --- a/app/src/main/res/layout/fragment_room_list.xml +++ b/app/src/main/res/layout/fragment_room_list.xml @@ -11,15 +11,16 @@ android:id="@+id/filterRoomView" android:layout_width="0dp" android:layout_height="32dp" - android:layout_marginBottom="8dp" - android:layout_marginEnd="16dp" android:layout_marginStart="8dp" android:layout_marginTop="8dp" + android:layout_marginEnd="16dp" + android:layout_marginBottom="8dp" android:background="@drawable/bg_search_edit_text" android:drawableLeft="@drawable/ic_search_white" android:drawablePadding="8dp" android:drawableTint="#9fa9ba" android:hint="Filter by name..." + android:lines="1" android:paddingLeft="8dp" app:layout_constraintBottom_toTopOf="@+id/stateView" app:layout_constraintEnd_toEndOf="parent" @@ -30,8 +31,8 @@ android:id="@+id/stateView" android:layout_width="0dp" android:layout_height="0dp" - android:layout_marginBottom="0dp" android:layout_marginEnd="0dp" + android:layout_marginBottom="0dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent"