From 5fcabca87c003089fa6b1d44a8283902d6ccdb25 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Wed, 16 Sep 2020 14:08:35 +0300 Subject: [PATCH 01/36] Data classes for request body created. --- .../session/room/search/EventContext.kt | 34 ++++++++++++++++++ .../internal/session/room/search/Filter.kt | 31 ++++++++++++++++ .../sdk/internal/session/room/search/Order.kt | 29 +++++++++++++++ .../session/room/search/RoomEvents.kt | 36 +++++++++++++++++++ .../session/room/search/SearchBody.kt | 27 ++++++++++++++ .../session/room/search/SearchCategories.kt | 28 +++++++++++++++ 6 files changed, 185 insertions(+) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/EventContext.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/Filter.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/Order.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/RoomEvents.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/SearchBody.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/SearchCategories.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/EventContext.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/EventContext.kt new file mode 100644 index 0000000000..36de3e30a8 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/EventContext.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.session.room.search + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class EventContext( + // How many events before the result are returned. + @Json(name = "before_limit") + val beforeLimit: Int? = null, + // How many events after the result are returned. + @Json(name = "after_limit") + val afterLimit: Int? = null, + // Requests that the server returns the historic profile information + @Json(name = "include_profile") + val includeProfile: Boolean? = null +) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/Filter.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/Filter.kt new file mode 100644 index 0000000000..717957fa00 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/Filter.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.session.room.search + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class Filter( + // The maximum number of events to return. + @Json(name = "limit") + val limit: Int? = null, + // A list of room IDs to include. If this list is absent then all rooms are included. + @Json(name = "rooms") + val rooms: List? = null +) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/Order.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/Order.kt new file mode 100644 index 0000000000..05a8fe6d3b --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/Order.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.session.room.search + +import com.squareup.moshi.JsonClass + +/** + * Represents the order in which to search for results. + */ +@JsonClass(generateAdapter = false) +enum class Order(val value: String) { + RANK("rank"), + RECENT("recent") +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/RoomEvents.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/RoomEvents.kt new file mode 100644 index 0000000000..83cc91e098 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/RoomEvents.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.session.room.search + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class RoomEvents( + // Required. The string to search events for. + @Json(name = "search_term") + val searchTerm: String, + @Json(name = "filter") + val filter: Filter? = null, + // By default, this is "rank". One of: ["recent", "rank"] + @Json(name = "order_by") + val orderBy: Order? = null, + // Configures whether any context for the events returned are included in the response. + @Json(name = "event_context") + val eventContext: EventContext? = null +) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/SearchBody.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/SearchBody.kt new file mode 100644 index 0000000000..08d4c0bec8 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/SearchBody.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.session.room.search + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class SearchBody( + @Json(name = "search_categories") + val searchCategories: SearchCategories +) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/SearchCategories.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/SearchCategories.kt new file mode 100644 index 0000000000..cfc887ca40 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/SearchCategories.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.session.room.search + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class SearchCategories( + // Mapping of category name to search criteria. + @Json(name = "room_events") + val roomEvents: RoomEvents? = null +) From 04b41fce3020196cfe9f2ddecb265f6e7639cb04 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Wed, 16 Sep 2020 16:04:12 +0300 Subject: [PATCH 02/36] Data classes for the search response created. --- .../sdk/internal/session/room/RoomAPI.kt | 3 ++ .../SearchRequestBody.kt} | 6 +-- .../SearchRequestCategories.kt} | 6 +-- .../SearchRequestEventContext.kt} | 4 +- .../SearchRequestFilter.kt} | 4 +- .../SearchRequestOrder.kt} | 4 +- .../SearchRequestRoomEvents.kt} | 10 ++--- .../room/search/response/SearchResponse.kt | 28 +++++++++++++ .../response/SearchResponseCategories.kt | 27 ++++++++++++ .../response/SearchResponseEventContext.kt | 42 +++++++++++++++++++ .../search/response/SearchResponseItem.kt | 32 ++++++++++++++ .../response/SearchResponseRoomEvents.kt | 33 +++++++++++++++ 12 files changed, 182 insertions(+), 17 deletions(-) rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/{SearchBody.kt => request/SearchRequestBody.kt} (84%) rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/{SearchCategories.kt => request/SearchRequestCategories.kt} (84%) rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/{EventContext.kt => request/SearchRequestEventContext.kt} (91%) rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/{Filter.kt => request/SearchRequestFilter.kt} (91%) rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/{Order.kt => request/SearchRequestOrder.kt} (87%) rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/{RoomEvents.kt => request/SearchRequestRoomEvents.kt} (81%) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/response/SearchResponse.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/response/SearchResponseCategories.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/response/SearchResponseEventContext.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/response/SearchResponseItem.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/response/SearchResponseRoomEvents.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt index 35c20cf5cb..ab33249e30 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt @@ -365,4 +365,7 @@ internal interface RoomAPI { fun deleteTag(@Path("userId") userId: String, @Path("roomId") roomId: String, @Path("tag") tag: String): Call + + @POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "search") + fun search() } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/SearchBody.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/request/SearchRequestBody.kt similarity index 84% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/SearchBody.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/request/SearchRequestBody.kt index 08d4c0bec8..c174761b08 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/SearchBody.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/request/SearchRequestBody.kt @@ -15,13 +15,13 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.room.search +package org.matrix.android.sdk.internal.session.room.search.request import com.squareup.moshi.Json import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) -data class SearchBody( +data class SearchRequestBody( @Json(name = "search_categories") - val searchCategories: SearchCategories + val searchCategories: SearchRequestCategories ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/SearchCategories.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/request/SearchRequestCategories.kt similarity index 84% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/SearchCategories.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/request/SearchRequestCategories.kt index cfc887ca40..25e078b608 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/SearchCategories.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/request/SearchRequestCategories.kt @@ -15,14 +15,14 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.room.search +package org.matrix.android.sdk.internal.session.room.search.request import com.squareup.moshi.Json import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) -data class SearchCategories( +data class SearchRequestCategories( // Mapping of category name to search criteria. @Json(name = "room_events") - val roomEvents: RoomEvents? = null + val roomEvents: SearchRequestRoomEvents? = null ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/EventContext.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/request/SearchRequestEventContext.kt similarity index 91% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/EventContext.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/request/SearchRequestEventContext.kt index 36de3e30a8..e74faf85b2 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/EventContext.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/request/SearchRequestEventContext.kt @@ -15,13 +15,13 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.room.search +package org.matrix.android.sdk.internal.session.room.search.request import com.squareup.moshi.Json import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) -data class EventContext( +data class SearchRequestEventContext( // How many events before the result are returned. @Json(name = "before_limit") val beforeLimit: Int? = null, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/Filter.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/request/SearchRequestFilter.kt similarity index 91% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/Filter.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/request/SearchRequestFilter.kt index 717957fa00..25b361a2b2 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/Filter.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/request/SearchRequestFilter.kt @@ -15,13 +15,13 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.room.search +package org.matrix.android.sdk.internal.session.room.search.request import com.squareup.moshi.Json import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) -data class Filter( +data class SearchRequestFilter( // The maximum number of events to return. @Json(name = "limit") val limit: Int? = null, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/Order.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/request/SearchRequestOrder.kt similarity index 87% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/Order.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/request/SearchRequestOrder.kt index 05a8fe6d3b..3fe17c69ae 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/Order.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/request/SearchRequestOrder.kt @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.room.search +package org.matrix.android.sdk.internal.session.room.search.request import com.squareup.moshi.JsonClass @@ -23,7 +23,7 @@ import com.squareup.moshi.JsonClass * Represents the order in which to search for results. */ @JsonClass(generateAdapter = false) -enum class Order(val value: String) { +enum class SearchRequestOrder(val value: String) { RANK("rank"), RECENT("recent") } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/RoomEvents.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/request/SearchRequestRoomEvents.kt similarity index 81% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/RoomEvents.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/request/SearchRequestRoomEvents.kt index 83cc91e098..ca2bfdc10e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/RoomEvents.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/request/SearchRequestRoomEvents.kt @@ -15,22 +15,22 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.room.search +package org.matrix.android.sdk.internal.session.room.search.request import com.squareup.moshi.Json import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) -data class RoomEvents( +data class SearchRequestRoomEvents( // Required. The string to search events for. @Json(name = "search_term") val searchTerm: String, @Json(name = "filter") - val filter: Filter? = null, + val filter: SearchRequestFilter? = null, // By default, this is "rank". One of: ["recent", "rank"] @Json(name = "order_by") - val orderBy: Order? = null, + val orderBy: SearchRequestOrder? = null, // Configures whether any context for the events returned are included in the response. @Json(name = "event_context") - val eventContext: EventContext? = null + val eventContext: SearchRequestEventContext? = null ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/response/SearchResponse.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/response/SearchResponse.kt new file mode 100644 index 0000000000..78294268bf --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/response/SearchResponse.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.session.room.search.response + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class SearchResponse( + // Required. Describes which categories to search in and their criteria. + @Json(name = "search_categories") + val searchCategories: SearchResponseCategories +) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/response/SearchResponseCategories.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/response/SearchResponseCategories.kt new file mode 100644 index 0000000000..757c6c5ea8 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/response/SearchResponseCategories.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.session.room.search.response + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class SearchResponseCategories( + @Json(name = "room_events") + val roomEvents: SearchResponseRoomEvents? = null +) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/response/SearchResponseEventContext.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/response/SearchResponseEventContext.kt new file mode 100644 index 0000000000..3950fc7bbb --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/response/SearchResponseEventContext.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.session.room.search.response + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.api.util.JsonDict + +@JsonClass(generateAdapter = true) +data class SearchResponseEventContext( + // Events just before the result. + @Json(name = "events_before") + val eventsBefore: List, + // Events just after the result. + @Json(name = "events_after") + val eventsAfter: List, + // Pagination token for the start of the chunk + @Json(name = "start") + val start: String? = null, + // Pagination token for the end of the chunk + @Json(name = "end") + val end: String? = null, + // The historic profile information of the users that sent the events returned. The string key is the user ID for which the profile belongs to. + @Json(name = "profile_info") + val profileInfo: JsonDict? = null +) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/response/SearchResponseItem.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/response/SearchResponseItem.kt new file mode 100644 index 0000000000..248589cf6e --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/response/SearchResponseItem.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.session.room.search.response + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import org.matrix.android.sdk.api.session.events.model.Event + +@JsonClass(generateAdapter = true) +data class SearchResponseItem( + // A number that describes how closely this result matches the search. Higher is closer. + @Json(name = "rank") + val rank: Int? = null, + // The event that matched. + @Json(name = "result") + val event: Event +) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/response/SearchResponseRoomEvents.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/response/SearchResponseRoomEvents.kt new file mode 100644 index 0000000000..236a93e2c6 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/response/SearchResponseRoomEvents.kt @@ -0,0 +1,33 @@ +/* + * Copyright 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.session.room.search.response + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +class SearchResponseRoomEvents( + // List of results in the requested order. + @Json(name = "results") + val results: List? = null, + @Json(name = "count") + val count: Int? = null, + // List of words which should be highlighted, useful for stemming which may change the query terms. + @Json(name = "highlights") + val highlights: List? = null +) From 051b431f1dd4647f25a101f3471547c8c18ee0f3 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Wed, 16 Sep 2020 16:20:57 +0300 Subject: [PATCH 03/36] Search api added to RoomAPI. --- .../matrix/android/sdk/internal/session/room/RoomAPI.kt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt index ab33249e30..c1a06ace1d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt @@ -35,6 +35,8 @@ import org.matrix.android.sdk.internal.session.room.membership.joining.InviteBod import org.matrix.android.sdk.internal.session.room.membership.threepid.ThreePidInviteBody import org.matrix.android.sdk.internal.session.room.relation.RelationsResponse import org.matrix.android.sdk.internal.session.room.reporting.ReportContentBody +import org.matrix.android.sdk.internal.session.room.search.request.SearchRequestBody +import org.matrix.android.sdk.internal.session.room.search.response.SearchResponse import org.matrix.android.sdk.internal.session.room.send.SendResponse import org.matrix.android.sdk.internal.session.room.tags.TagBody import org.matrix.android.sdk.internal.session.room.timeline.EventContextResponse @@ -366,6 +368,11 @@ internal interface RoomAPI { @Path("roomId") roomId: String, @Path("tag") tag: String): Call + /** + * Performs a full text search across different categories. + */ @POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "search") - fun search() + fun search( + @Query("next_batch") nextBatch: String?, + @Body body: SearchRequestBody): Call } From b521f3656929096d678818a2c44a67dc07f36a1c Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Wed, 16 Sep 2020 17:18:47 +0300 Subject: [PATCH 04/36] SearchTask implementation. --- .../sdk/internal/session/room/RoomModule.kt | 5 ++ .../session/room/search/SearchTask.kt | 74 +++++++++++++++++++ .../response/SearchResponseRoomEvents.kt | 5 +- 3 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/SearchTask.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt index 700507735b..d3a7cf7df0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt @@ -61,6 +61,8 @@ import org.matrix.android.sdk.internal.session.room.relation.FindReactionEventFo import org.matrix.android.sdk.internal.session.room.relation.UpdateQuickReactionTask import org.matrix.android.sdk.internal.session.room.reporting.DefaultReportContentTask import org.matrix.android.sdk.internal.session.room.reporting.ReportContentTask +import org.matrix.android.sdk.internal.session.room.search.DefaultSearchTask +import org.matrix.android.sdk.internal.session.room.search.SearchTask import org.matrix.android.sdk.internal.session.room.state.DefaultSendStateTask import org.matrix.android.sdk.internal.session.room.state.SendStateTask import org.matrix.android.sdk.internal.session.room.tags.AddTagToRoomTask @@ -195,4 +197,7 @@ internal abstract class RoomModule { @Binds abstract fun bindDeleteTagFromRoomTask(task: DefaultDeleteTagFromRoomTask): DeleteTagFromRoomTask + + @Binds + abstract fun bindSearchTask(task: DefaultSearchTask): SearchTask } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/SearchTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/SearchTask.kt new file mode 100644 index 0000000000..db9e1fc604 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/SearchTask.kt @@ -0,0 +1,74 @@ +/* + * Copyright 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.session.room.search + +import org.greenrobot.eventbus.EventBus +import org.matrix.android.sdk.internal.network.executeRequest +import org.matrix.android.sdk.internal.session.room.RoomAPI +import org.matrix.android.sdk.internal.session.room.search.request.SearchRequestBody +import org.matrix.android.sdk.internal.session.room.search.request.SearchRequestCategories +import org.matrix.android.sdk.internal.session.room.search.request.SearchRequestEventContext +import org.matrix.android.sdk.internal.session.room.search.request.SearchRequestFilter +import org.matrix.android.sdk.internal.session.room.search.request.SearchRequestOrder +import org.matrix.android.sdk.internal.session.room.search.request.SearchRequestRoomEvents +import org.matrix.android.sdk.internal.session.room.search.response.SearchResponse +import org.matrix.android.sdk.internal.task.Task +import javax.inject.Inject + +internal interface SearchTask : Task { + + data class Params( + val searchTerm: String, + val rooms: List, + val nextBatch: String? = null, + val orderByRecent: Boolean, + val limit: Int, + val beforeLimit: Int, + val afterLimit: Int, + val includeProfile: Boolean + ) +} + +internal class DefaultSearchTask @Inject constructor( + private val roomAPI: RoomAPI, + private val eventBus: EventBus +) : SearchTask { + + override suspend fun execute(params: SearchTask.Params): SearchResponse { + return executeRequest(eventBus) { + val searchRequestBody = SearchRequestBody( + searchCategories = SearchRequestCategories( + roomEvents = SearchRequestRoomEvents( + searchTerm = params.searchTerm, + orderBy = if (params.orderByRecent) SearchRequestOrder.RECENT else SearchRequestOrder.RANK, + filter = SearchRequestFilter( + limit = params.limit, + rooms = params.rooms + ), + eventContext = SearchRequestEventContext( + beforeLimit = params.beforeLimit, + afterLimit = params.afterLimit, + includeProfile = params.includeProfile + ) + ) + ) + ) + apiCall = roomAPI.search(params.nextBatch, searchRequestBody) + } + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/response/SearchResponseRoomEvents.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/response/SearchResponseRoomEvents.kt index 236a93e2c6..a14f78ee00 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/response/SearchResponseRoomEvents.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/response/SearchResponseRoomEvents.kt @@ -29,5 +29,8 @@ class SearchResponseRoomEvents( val count: Int? = null, // List of words which should be highlighted, useful for stemming which may change the query terms. @Json(name = "highlights") - val highlights: List? = null + val highlights: List? = null, + // Token that can be used to get the next batch of results, by passing as the next_batch parameter to the next call. If this field is absent, there are no more results. + @Json(name = "next_batch") + val nextBatch: String? = null ) From d599cab017d00270a91d128c26c27b5f9f213c14 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Wed, 16 Sep 2020 18:38:04 +0300 Subject: [PATCH 05/36] SearchService implementation. --- .../api/session/room/search/SearchService.kt | 49 ++++++++++++++++ .../sdk/internal/session/room/RoomModule.kt | 5 ++ .../room/search/DefaultSearchService.kt | 56 +++++++++++++++++++ 3 files changed, 110 insertions(+) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/search/SearchService.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/DefaultSearchService.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/search/SearchService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/search/SearchService.kt new file mode 100644 index 0000000000..0976ceba18 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/search/SearchService.kt @@ -0,0 +1,49 @@ +/* + * Copyright 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.api.session.room.search + +import org.matrix.android.sdk.api.MatrixCallback +import org.matrix.android.sdk.api.util.Cancelable +import org.matrix.android.sdk.internal.session.room.search.response.SearchResponse + +/** + * This interface defines methods to search messages in rooms. + */ +interface SearchService { + + /** + * Generic function to search a term in multiple rooms. + * Ref: https://matrix.org/docs/spec/client_server/latest#module-search + * @param searchTerm the term to search + * @param rooms roomIds to search term inside + * @param nextBatch the token that retrieved from the previous response. Should be provided to get the next batch of results + * @param limit the maximum number of events to return. + * @param beforeLimit how many events before the result are returned. + * @param afterLimit how many events after the result are returned. + * @param includeProfile requests that the server returns the historic profile information for the users that sent the events that were returned. + */ + fun search(searchTerm: String, + rooms: List, + nextBatch: String?, + orderByRecent: Boolean, + limit: Int, + beforeLimit: Int, + afterLimit: Int, + includeProfile: Boolean, + callback: MatrixCallback): Cancelable +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt index d3a7cf7df0..af0abc526a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt @@ -25,6 +25,7 @@ import org.commonmark.renderer.html.HtmlRenderer import org.matrix.android.sdk.api.session.file.FileService import org.matrix.android.sdk.api.session.room.RoomDirectoryService import org.matrix.android.sdk.api.session.room.RoomService +import org.matrix.android.sdk.api.session.room.search.SearchService import org.matrix.android.sdk.internal.session.DefaultFileService import org.matrix.android.sdk.internal.session.SessionScope import org.matrix.android.sdk.internal.session.room.alias.AddRoomAliasTask @@ -61,6 +62,7 @@ import org.matrix.android.sdk.internal.session.room.relation.FindReactionEventFo import org.matrix.android.sdk.internal.session.room.relation.UpdateQuickReactionTask import org.matrix.android.sdk.internal.session.room.reporting.DefaultReportContentTask import org.matrix.android.sdk.internal.session.room.reporting.ReportContentTask +import org.matrix.android.sdk.internal.session.room.search.DefaultSearchService import org.matrix.android.sdk.internal.session.room.search.DefaultSearchTask import org.matrix.android.sdk.internal.session.room.search.SearchTask import org.matrix.android.sdk.internal.session.room.state.DefaultSendStateTask @@ -123,6 +125,9 @@ internal abstract class RoomModule { @Binds abstract fun bindFileService(service: DefaultFileService): FileService + @Binds + abstract fun bindSearchService(service: DefaultSearchService): SearchService + @Binds abstract fun bindCreateRoomTask(task: DefaultCreateRoomTask): CreateRoomTask diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/DefaultSearchService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/DefaultSearchService.kt new file mode 100644 index 0000000000..cf57e2a491 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/DefaultSearchService.kt @@ -0,0 +1,56 @@ +/* + * Copyright 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.session.room.search + +import org.matrix.android.sdk.api.MatrixCallback +import org.matrix.android.sdk.api.session.room.search.SearchService +import org.matrix.android.sdk.api.util.Cancelable +import org.matrix.android.sdk.internal.session.room.search.response.SearchResponse +import javax.inject.Inject +import org.matrix.android.sdk.internal.task.TaskExecutor +import org.matrix.android.sdk.internal.task.configureWith + +internal class DefaultSearchService @Inject constructor( + private val taskExecutor: TaskExecutor, + private val searchTask: SearchTask +) : SearchService { + + override fun search(searchTerm: String, + rooms: List, + nextBatch: String?, + orderByRecent: Boolean, + limit: Int, + beforeLimit: Int, + afterLimit: Int, + includeProfile: Boolean, + callback: MatrixCallback): Cancelable { + return searchTask + .configureWith(SearchTask.Params( + searchTerm = searchTerm, + rooms = rooms, + nextBatch = nextBatch, + orderByRecent = orderByRecent, + limit = limit, + beforeLimit = beforeLimit, + afterLimit = afterLimit, + includeProfile = includeProfile + )) { + this.callback = callback + }.executeBy(taskExecutor) + } +} From 05f48255a341274898b3da363146c784665e8695 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Thu, 17 Sep 2020 12:02:32 +0300 Subject: [PATCH 06/36] Move search service into session module. --- .../session/{room => }/search/SearchService.kt | 4 ++-- .../sdk/internal/session/SessionModule.kt | 5 +++++ .../android/sdk/internal/session/room/RoomAPI.kt | 4 ++-- .../sdk/internal/session/room/RoomModule.kt | 9 ++------- .../{room => }/search/DefaultSearchService.kt | 6 +++--- .../session/{room => }/search/SearchTask.kt | 16 ++++++++-------- .../search/request/SearchRequestBody.kt | 4 ++-- .../search/request/SearchRequestCategories.kt | 4 ++-- .../search/request/SearchRequestEventContext.kt | 4 ++-- .../search/request/SearchRequestFilter.kt | 4 ++-- .../search/request/SearchRequestOrder.kt | 4 ++-- .../search/request/SearchRequestRoomEvents.kt | 4 ++-- .../{room => }/search/response/SearchResponse.kt | 2 +- .../search/response/SearchResponseCategories.kt | 2 +- .../response/SearchResponseEventContext.kt | 4 ++-- .../search/response/SearchResponseItem.kt | 2 +- .../search/response/SearchResponseRoomEvents.kt | 2 +- 17 files changed, 40 insertions(+), 40 deletions(-) rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/{room => }/search/SearchService.kt (93%) rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/{room => }/search/DefaultSearchService.kt (90%) rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/{room => }/search/SearchTask.kt (79%) rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/{room => }/search/request/SearchRequestBody.kt (89%) rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/{room => }/search/request/SearchRequestCategories.kt (89%) rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/{room => }/search/request/SearchRequestEventContext.kt (91%) rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/{room => }/search/request/SearchRequestFilter.kt (90%) rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/{room => }/search/request/SearchRequestOrder.kt (87%) rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/{room => }/search/request/SearchRequestRoomEvents.kt (92%) rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/{room => }/search/response/SearchResponse.kt (93%) rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/{room => }/search/response/SearchResponseCategories.kt (92%) rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/{room => }/search/response/SearchResponseEventContext.kt (93%) rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/{room => }/search/response/SearchResponseItem.kt (94%) rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/{room => }/search/response/SearchResponseRoomEvents.kt (95%) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/search/SearchService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/search/SearchService.kt similarity index 93% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/search/SearchService.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/search/SearchService.kt index 0976ceba18..9a8b2e49e2 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/search/SearchService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/search/SearchService.kt @@ -15,11 +15,11 @@ * limitations under the License. */ -package org.matrix.android.sdk.api.session.room.search +package org.matrix.android.sdk.api.session.search import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.util.Cancelable -import org.matrix.android.sdk.internal.session.room.search.response.SearchResponse +import org.matrix.android.sdk.internal.session.search.response.SearchResponse /** * This interface defines methods to search messages in rooms. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt index 5397b8d9bd..d615d9e35b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt @@ -39,6 +39,7 @@ import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.accountdata.AccountDataService import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilitiesService import org.matrix.android.sdk.api.session.permalinks.PermalinkService +import org.matrix.android.sdk.api.session.search.SearchService import org.matrix.android.sdk.api.session.securestorage.SecureStorageService import org.matrix.android.sdk.api.session.securestorage.SharedSecretStorageService import org.matrix.android.sdk.api.session.typing.TypingUsersTracker @@ -81,6 +82,7 @@ import org.matrix.android.sdk.internal.session.permalinks.DefaultPermalinkServic import org.matrix.android.sdk.internal.session.room.EventRelationsAggregationProcessor import org.matrix.android.sdk.internal.session.room.create.RoomCreateEventProcessor import org.matrix.android.sdk.internal.session.room.prune.RedactionEventProcessor +import org.matrix.android.sdk.internal.session.search.DefaultSearchService import org.matrix.android.sdk.internal.session.room.tombstone.RoomTombstoneEventProcessor import org.matrix.android.sdk.internal.session.securestorage.DefaultSecureStorageService import org.matrix.android.sdk.internal.session.typing.DefaultTypingUsersTracker @@ -368,4 +370,7 @@ internal abstract class SessionModule { @Binds abstract fun bindTypingUsersTracker(tracker: DefaultTypingUsersTracker): TypingUsersTracker + + @Binds + abstract fun bindSearchService(service: DefaultSearchService): SearchService } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt index c1a06ace1d..1a23f3a2f5 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt @@ -35,8 +35,8 @@ import org.matrix.android.sdk.internal.session.room.membership.joining.InviteBod import org.matrix.android.sdk.internal.session.room.membership.threepid.ThreePidInviteBody import org.matrix.android.sdk.internal.session.room.relation.RelationsResponse import org.matrix.android.sdk.internal.session.room.reporting.ReportContentBody -import org.matrix.android.sdk.internal.session.room.search.request.SearchRequestBody -import org.matrix.android.sdk.internal.session.room.search.response.SearchResponse +import org.matrix.android.sdk.internal.session.search.request.SearchRequestBody +import org.matrix.android.sdk.internal.session.search.response.SearchResponse import org.matrix.android.sdk.internal.session.room.send.SendResponse import org.matrix.android.sdk.internal.session.room.tags.TagBody import org.matrix.android.sdk.internal.session.room.timeline.EventContextResponse diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt index af0abc526a..77f8f38410 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt @@ -25,7 +25,6 @@ import org.commonmark.renderer.html.HtmlRenderer import org.matrix.android.sdk.api.session.file.FileService import org.matrix.android.sdk.api.session.room.RoomDirectoryService import org.matrix.android.sdk.api.session.room.RoomService -import org.matrix.android.sdk.api.session.room.search.SearchService import org.matrix.android.sdk.internal.session.DefaultFileService import org.matrix.android.sdk.internal.session.SessionScope import org.matrix.android.sdk.internal.session.room.alias.AddRoomAliasTask @@ -62,9 +61,8 @@ import org.matrix.android.sdk.internal.session.room.relation.FindReactionEventFo import org.matrix.android.sdk.internal.session.room.relation.UpdateQuickReactionTask import org.matrix.android.sdk.internal.session.room.reporting.DefaultReportContentTask import org.matrix.android.sdk.internal.session.room.reporting.ReportContentTask -import org.matrix.android.sdk.internal.session.room.search.DefaultSearchService -import org.matrix.android.sdk.internal.session.room.search.DefaultSearchTask -import org.matrix.android.sdk.internal.session.room.search.SearchTask +import org.matrix.android.sdk.internal.session.search.DefaultSearchTask +import org.matrix.android.sdk.internal.session.search.SearchTask import org.matrix.android.sdk.internal.session.room.state.DefaultSendStateTask import org.matrix.android.sdk.internal.session.room.state.SendStateTask import org.matrix.android.sdk.internal.session.room.tags.AddTagToRoomTask @@ -125,9 +123,6 @@ internal abstract class RoomModule { @Binds abstract fun bindFileService(service: DefaultFileService): FileService - @Binds - abstract fun bindSearchService(service: DefaultSearchService): SearchService - @Binds abstract fun bindCreateRoomTask(task: DefaultCreateRoomTask): CreateRoomTask diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/DefaultSearchService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/DefaultSearchService.kt similarity index 90% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/DefaultSearchService.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/DefaultSearchService.kt index cf57e2a491..1937735191 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/DefaultSearchService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/DefaultSearchService.kt @@ -15,12 +15,12 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.room.search +package org.matrix.android.sdk.internal.session.search import org.matrix.android.sdk.api.MatrixCallback -import org.matrix.android.sdk.api.session.room.search.SearchService +import org.matrix.android.sdk.api.session.search.SearchService import org.matrix.android.sdk.api.util.Cancelable -import org.matrix.android.sdk.internal.session.room.search.response.SearchResponse +import org.matrix.android.sdk.internal.session.search.response.SearchResponse import javax.inject.Inject import org.matrix.android.sdk.internal.task.TaskExecutor import org.matrix.android.sdk.internal.task.configureWith diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/SearchTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchTask.kt similarity index 79% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/SearchTask.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchTask.kt index db9e1fc604..38e87a1251 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/SearchTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchTask.kt @@ -15,18 +15,18 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.room.search +package org.matrix.android.sdk.internal.session.search import org.greenrobot.eventbus.EventBus import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.session.room.RoomAPI -import org.matrix.android.sdk.internal.session.room.search.request.SearchRequestBody -import org.matrix.android.sdk.internal.session.room.search.request.SearchRequestCategories -import org.matrix.android.sdk.internal.session.room.search.request.SearchRequestEventContext -import org.matrix.android.sdk.internal.session.room.search.request.SearchRequestFilter -import org.matrix.android.sdk.internal.session.room.search.request.SearchRequestOrder -import org.matrix.android.sdk.internal.session.room.search.request.SearchRequestRoomEvents -import org.matrix.android.sdk.internal.session.room.search.response.SearchResponse +import org.matrix.android.sdk.internal.session.search.request.SearchRequestBody +import org.matrix.android.sdk.internal.session.search.request.SearchRequestCategories +import org.matrix.android.sdk.internal.session.search.request.SearchRequestEventContext +import org.matrix.android.sdk.internal.session.search.request.SearchRequestFilter +import org.matrix.android.sdk.internal.session.search.request.SearchRequestOrder +import org.matrix.android.sdk.internal.session.search.request.SearchRequestRoomEvents +import org.matrix.android.sdk.internal.session.search.response.SearchResponse import org.matrix.android.sdk.internal.task.Task import javax.inject.Inject diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/request/SearchRequestBody.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/request/SearchRequestBody.kt similarity index 89% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/request/SearchRequestBody.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/request/SearchRequestBody.kt index c174761b08..000f89e751 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/request/SearchRequestBody.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/request/SearchRequestBody.kt @@ -15,13 +15,13 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.room.search.request +package org.matrix.android.sdk.internal.session.search.request import com.squareup.moshi.Json import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) -data class SearchRequestBody( +internal data class SearchRequestBody( @Json(name = "search_categories") val searchCategories: SearchRequestCategories ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/request/SearchRequestCategories.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/request/SearchRequestCategories.kt similarity index 89% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/request/SearchRequestCategories.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/request/SearchRequestCategories.kt index 25e078b608..606b7320e5 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/request/SearchRequestCategories.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/request/SearchRequestCategories.kt @@ -15,13 +15,13 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.room.search.request +package org.matrix.android.sdk.internal.session.search.request import com.squareup.moshi.Json import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) -data class SearchRequestCategories( +internal data class SearchRequestCategories( // Mapping of category name to search criteria. @Json(name = "room_events") val roomEvents: SearchRequestRoomEvents? = null diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/request/SearchRequestEventContext.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/request/SearchRequestEventContext.kt similarity index 91% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/request/SearchRequestEventContext.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/request/SearchRequestEventContext.kt index e74faf85b2..9db97295dc 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/request/SearchRequestEventContext.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/request/SearchRequestEventContext.kt @@ -15,13 +15,13 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.room.search.request +package org.matrix.android.sdk.internal.session.search.request import com.squareup.moshi.Json import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) -data class SearchRequestEventContext( +internal data class SearchRequestEventContext( // How many events before the result are returned. @Json(name = "before_limit") val beforeLimit: Int? = null, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/request/SearchRequestFilter.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/request/SearchRequestFilter.kt similarity index 90% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/request/SearchRequestFilter.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/request/SearchRequestFilter.kt index 25b361a2b2..196d49346c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/request/SearchRequestFilter.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/request/SearchRequestFilter.kt @@ -15,13 +15,13 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.room.search.request +package org.matrix.android.sdk.internal.session.search.request import com.squareup.moshi.Json import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) -data class SearchRequestFilter( +internal data class SearchRequestFilter( // The maximum number of events to return. @Json(name = "limit") val limit: Int? = null, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/request/SearchRequestOrder.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/request/SearchRequestOrder.kt similarity index 87% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/request/SearchRequestOrder.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/request/SearchRequestOrder.kt index 3fe17c69ae..eac51ad2df 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/request/SearchRequestOrder.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/request/SearchRequestOrder.kt @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.room.search.request +package org.matrix.android.sdk.internal.session.search.request import com.squareup.moshi.JsonClass @@ -23,7 +23,7 @@ import com.squareup.moshi.JsonClass * Represents the order in which to search for results. */ @JsonClass(generateAdapter = false) -enum class SearchRequestOrder(val value: String) { +internal enum class SearchRequestOrder(val value: String) { RANK("rank"), RECENT("recent") } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/request/SearchRequestRoomEvents.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/request/SearchRequestRoomEvents.kt similarity index 92% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/request/SearchRequestRoomEvents.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/request/SearchRequestRoomEvents.kt index ca2bfdc10e..fef1baa990 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/request/SearchRequestRoomEvents.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/request/SearchRequestRoomEvents.kt @@ -15,13 +15,13 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.room.search.request +package org.matrix.android.sdk.internal.session.search.request import com.squareup.moshi.Json import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) -data class SearchRequestRoomEvents( +internal data class SearchRequestRoomEvents( // Required. The string to search events for. @Json(name = "search_term") val searchTerm: String, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/response/SearchResponse.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/response/SearchResponse.kt similarity index 93% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/response/SearchResponse.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/response/SearchResponse.kt index 78294268bf..b7debb6b1e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/response/SearchResponse.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/response/SearchResponse.kt @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.room.search.response +package org.matrix.android.sdk.internal.session.search.response import com.squareup.moshi.Json import com.squareup.moshi.JsonClass diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/response/SearchResponseCategories.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/response/SearchResponseCategories.kt similarity index 92% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/response/SearchResponseCategories.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/response/SearchResponseCategories.kt index 757c6c5ea8..c931b23826 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/response/SearchResponseCategories.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/response/SearchResponseCategories.kt @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.room.search.response +package org.matrix.android.sdk.internal.session.search.response import com.squareup.moshi.Json import com.squareup.moshi.JsonClass diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/response/SearchResponseEventContext.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/response/SearchResponseEventContext.kt similarity index 93% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/response/SearchResponseEventContext.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/response/SearchResponseEventContext.kt index 3950fc7bbb..596645c355 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/response/SearchResponseEventContext.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/response/SearchResponseEventContext.kt @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.room.search.response +package org.matrix.android.sdk.internal.session.search.response import com.squareup.moshi.Json import com.squareup.moshi.JsonClass @@ -23,7 +23,7 @@ import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.util.JsonDict @JsonClass(generateAdapter = true) -data class SearchResponseEventContext( +internal data class SearchResponseEventContext( // Events just before the result. @Json(name = "events_before") val eventsBefore: List, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/response/SearchResponseItem.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/response/SearchResponseItem.kt similarity index 94% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/response/SearchResponseItem.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/response/SearchResponseItem.kt index 248589cf6e..6c284b61c7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/response/SearchResponseItem.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/response/SearchResponseItem.kt @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.room.search.response +package org.matrix.android.sdk.internal.session.search.response import com.squareup.moshi.Json import com.squareup.moshi.JsonClass diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/response/SearchResponseRoomEvents.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/response/SearchResponseRoomEvents.kt similarity index 95% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/response/SearchResponseRoomEvents.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/response/SearchResponseRoomEvents.kt index a14f78ee00..61ed2bf3cf 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/search/response/SearchResponseRoomEvents.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/response/SearchResponseRoomEvents.kt @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.room.search.response +package org.matrix.android.sdk.internal.session.search.response import com.squareup.moshi.Json import com.squareup.moshi.JsonClass From 2eb60213b461ac4b256b78bf8e8d4d05862efb22 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Thu, 17 Sep 2020 12:25:07 +0300 Subject: [PATCH 07/36] Do not support searching in multiple rooms for now. --- .../matrix/android/sdk/api/session/search/SearchService.kt | 4 ++-- .../sdk/internal/session/search/DefaultSearchService.kt | 4 ++-- .../matrix/android/sdk/internal/session/search/SearchTask.kt | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/search/SearchService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/search/SearchService.kt index 9a8b2e49e2..68fc4e5d6f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/search/SearchService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/search/SearchService.kt @@ -30,7 +30,7 @@ interface SearchService { * Generic function to search a term in multiple rooms. * Ref: https://matrix.org/docs/spec/client_server/latest#module-search * @param searchTerm the term to search - * @param rooms roomIds to search term inside + * @param roomId the roomId to search term inside * @param nextBatch the token that retrieved from the previous response. Should be provided to get the next batch of results * @param limit the maximum number of events to return. * @param beforeLimit how many events before the result are returned. @@ -38,7 +38,7 @@ interface SearchService { * @param includeProfile requests that the server returns the historic profile information for the users that sent the events that were returned. */ fun search(searchTerm: String, - rooms: List, + roomId: String, nextBatch: String?, orderByRecent: Boolean, limit: Int, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/DefaultSearchService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/DefaultSearchService.kt index 1937735191..1f158ec41b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/DefaultSearchService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/DefaultSearchService.kt @@ -31,7 +31,7 @@ internal class DefaultSearchService @Inject constructor( ) : SearchService { override fun search(searchTerm: String, - rooms: List, + roomId: String, nextBatch: String?, orderByRecent: Boolean, limit: Int, @@ -42,7 +42,7 @@ internal class DefaultSearchService @Inject constructor( return searchTask .configureWith(SearchTask.Params( searchTerm = searchTerm, - rooms = rooms, + roomId = roomId, nextBatch = nextBatch, orderByRecent = orderByRecent, limit = limit, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchTask.kt index 38e87a1251..52b4805e02 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchTask.kt @@ -34,7 +34,7 @@ internal interface SearchTask : Task { data class Params( val searchTerm: String, - val rooms: List, + val roomId: String, val nextBatch: String? = null, val orderByRecent: Boolean, val limit: Int, @@ -58,7 +58,7 @@ internal class DefaultSearchTask @Inject constructor( orderBy = if (params.orderByRecent) SearchRequestOrder.RECENT else SearchRequestOrder.RANK, filter = SearchRequestFilter( limit = params.limit, - rooms = params.rooms + rooms = listOf(params.roomId) ), eventContext = SearchRequestEventContext( beforeLimit = params.beforeLimit, From c46f0a7430bd1391e08df762613ad84de635b856 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Thu, 17 Sep 2020 16:08:46 +0300 Subject: [PATCH 08/36] Simple integration test implementation for searching messages in a room. --- .../sdk/session/search/SearchMessagesTest.kt | 114 ++++++++++++++++++ .../src/main/AndroidManifest.xml | 10 ++ 2 files changed, 124 insertions(+) create mode 100644 matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/search/SearchMessagesTest.kt diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/search/SearchMessagesTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/search/SearchMessagesTest.kt new file mode 100644 index 0000000000..3e2960af43 --- /dev/null +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/search/SearchMessagesTest.kt @@ -0,0 +1,114 @@ +/* + * Copyright 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.session.search + +import org.junit.Assert.assertTrue +import org.junit.Assert.fail +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.junit.runners.MethodSorters +import org.matrix.android.sdk.InstrumentedTest +import org.matrix.android.sdk.api.MatrixCallback +import org.matrix.android.sdk.api.extensions.orFalse +import org.matrix.android.sdk.api.session.events.model.toModel +import org.matrix.android.sdk.api.session.room.model.message.MessageContent +import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings +import org.matrix.android.sdk.common.CommonTestHelper +import org.matrix.android.sdk.common.CryptoTestHelper +import org.matrix.android.sdk.common.TestConstants +import org.matrix.android.sdk.internal.session.search.response.SearchResponse +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit + +@RunWith(JUnit4::class) +@FixMethodOrder(MethodSorters.JVM) +class SearchMessagesTest : InstrumentedTest { + + private val MESSAGE = "Lorem ipsum dolor sit amet" + + private val commonTestHelper = CommonTestHelper(context()) + private val cryptoTestHelper = CryptoTestHelper(commonTestHelper) + + @Test + fun sendTextMessageAndSearchPartOfIt() { + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(false) + val aliceSession = cryptoTestData.firstSession + val aliceRoomId = cryptoTestData.roomId + aliceSession.cryptoService().setWarnOnUnknownDevices(false) + val roomFromAlicePOV = aliceSession.getRoom(aliceRoomId)!! + val aliceTimeline = roomFromAlicePOV.createTimeline(null, TimelineSettings(10)) + aliceTimeline.start() + + commonTestHelper.sendTextMessage( + roomFromAlicePOV, + MESSAGE, + 2) + + run { + var lock = CountDownLatch(1) + + val eventListener = commonTestHelper.createEventListener(lock) { snapshot -> + snapshot.count { it.root.content.toModel()?.body?.startsWith(MESSAGE).orFalse() } == 2 + } + + aliceTimeline.addListener(eventListener) + commonTestHelper.await(lock) + + lock = CountDownLatch(1) + aliceSession + .searchService() + .search( + searchTerm = "lore", + limit = 10, + includeProfile = true, + afterLimit = 0, + beforeLimit = 10, + orderByRecent = true, + nextBatch = null, + roomId = aliceRoomId, + callback = object : MatrixCallback { + override fun onSuccess(data: SearchResponse) { + super.onSuccess(data) + assertTrue(data.searchCategories.roomEvents?.results?.size == 2) + assertTrue( + data.searchCategories.roomEvents?.results + ?.all { + (it.event.content?.get("body") as? String)?.startsWith(MESSAGE).orFalse() + }.orFalse() + ) + lock.countDown() + } + + override fun onFailure(failure: Throwable) { + super.onFailure(failure) + fail(failure.localizedMessage) + lock.countDown() + } + } + ) + lock.await(TestConstants.timeOutMillis, TimeUnit.MILLISECONDS) + + aliceTimeline.removeAllListeners() + cryptoTestData.cleanUp(commonTestHelper) + } + + aliceSession.startSync(true) +} +} diff --git a/matrix-sdk-android/src/main/AndroidManifest.xml b/matrix-sdk-android/src/main/AndroidManifest.xml index 52238f824c..eeb944d955 100644 --- a/matrix-sdk-android/src/main/AndroidManifest.xml +++ b/matrix-sdk-android/src/main/AndroidManifest.xml @@ -1,4 +1,5 @@ @@ -7,6 +8,15 @@ + + + diff --git a/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt b/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt index 86d59b630b..014a244bf8 100644 --- a/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt +++ b/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt @@ -52,6 +52,7 @@ import im.vector.app.features.home.HomeDrawerFragment import im.vector.app.features.home.LoadingFragment import im.vector.app.features.home.room.breadcrumbs.BreadcrumbsFragment import im.vector.app.features.home.room.detail.RoomDetailFragment +import im.vector.app.features.home.room.detail.search.SearchFragment import im.vector.app.features.home.room.list.RoomListFragment import im.vector.app.features.login.LoginCaptchaFragment import im.vector.app.features.login.LoginFragment @@ -570,4 +571,9 @@ interface FragmentModule { @IntoMap @FragmentKey(RoomBannedMemberListFragment::class) fun bindRoomBannedMemberListFragment(fragment: RoomBannedMemberListFragment): Fragment + + @Binds + @IntoMap + @FragmentKey(SearchFragment::class) + fun bindSearchFragment(fragment: SearchFragment): Fragment } diff --git a/vector/src/main/java/im/vector/app/core/di/ScreenComponent.kt b/vector/src/main/java/im/vector/app/core/di/ScreenComponent.kt index d337ec7977..17ec00952b 100644 --- a/vector/src/main/java/im/vector/app/core/di/ScreenComponent.kt +++ b/vector/src/main/java/im/vector/app/core/di/ScreenComponent.kt @@ -38,6 +38,7 @@ import im.vector.app.features.home.HomeActivity import im.vector.app.features.home.HomeModule import im.vector.app.features.home.room.detail.RoomDetailActivity import im.vector.app.features.home.room.detail.readreceipts.DisplayReadReceiptsBottomSheet +import im.vector.app.features.home.room.detail.search.SearchActivity import im.vector.app.features.home.room.detail.timeline.action.MessageActionsBottomSheet import im.vector.app.features.home.room.detail.timeline.edithistory.ViewEditHistoryBottomSheet import im.vector.app.features.home.room.detail.timeline.reactions.ViewReactionsBottomSheet @@ -142,6 +143,7 @@ interface ScreenComponent { fun inject(activity: VectorCallActivity) fun inject(activity: VectorAttachmentViewerActivity) fun inject(activity: VectorJitsiActivity) + fun inject(activity: SearchActivity) /* ========================================================================================== * BottomSheets diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt index 3b4795b965..34aa6a0831 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt @@ -673,6 +673,10 @@ class RoomDetailFragment @Inject constructor( roomDetailViewModel.handle(RoomDetailAction.EndCall) true } + R.id.search -> { + navigator.openSearch(requireContext(), roomDetailArgs.roomId) + true + } else -> super.onOptionsItemSelected(item) } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchAction.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchAction.kt new file mode 100644 index 0000000000..2774ef4d8e --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchAction.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.home.room.detail.search + +import im.vector.app.core.platform.VectorViewModelAction + +sealed class SearchAction : VectorViewModelAction diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchActivity.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchActivity.kt new file mode 100644 index 0000000000..bedc4cb881 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchActivity.kt @@ -0,0 +1,50 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.home.room.detail.search + +import android.content.Context +import android.content.Intent +import com.airbnb.mvrx.MvRx +import im.vector.app.R +import im.vector.app.core.di.ScreenComponent +import im.vector.app.core.extensions.addFragment +import im.vector.app.core.platform.VectorBaseActivity + +class SearchActivity : VectorBaseActivity() { + + override fun getLayoutRes() = R.layout.activity_simple + + override fun injectWith(injector: ScreenComponent) { + super.injectWith(injector) + injector.inject(this) + } + + override fun initUiAndData() { + if (isFirstCreation()) { + val fragmentArgs: SearchArgs = intent?.extras?.getParcelable(MvRx.KEY_ARG) ?: return + addFragment(R.id.simpleFragmentContainer, SearchFragment::class.java, fragmentArgs) + } + } + + companion object { + fun newIntent(context: Context, args: SearchArgs): Intent { + return Intent(context, SearchActivity::class.java).apply { + putExtra(MvRx.KEY_ARG, args) + } + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt new file mode 100644 index 0000000000..f5b8440b58 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.home.room.detail.search + +import android.os.Bundle +import android.os.Parcelable +import android.view.View +import com.airbnb.mvrx.args +import com.airbnb.mvrx.fragmentViewModel +import im.vector.app.R +import im.vector.app.core.platform.VectorBaseFragment +import kotlinx.android.parcel.Parcelize +import javax.inject.Inject + +@Parcelize +data class SearchArgs( + val roomId: String +) : Parcelable + +class SearchFragment @Inject constructor( + val viewModelFactory: SearchViewModel.Factory +) : VectorBaseFragment() { + + private val fragmentArgs: SearchArgs by args() + private val searchViewModel: SearchViewModel by fragmentViewModel() + + override fun getLayoutResId() = R.layout.fragment_search + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + } +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewEvents.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewEvents.kt new file mode 100644 index 0000000000..a5489e9240 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewEvents.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.home.room.detail.search + +import im.vector.app.core.platform.VectorViewEvents + +sealed class SearchViewEvents : VectorViewEvents diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt new file mode 100644 index 0000000000..de75c0dd20 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.home.room.detail.search + +import com.airbnb.mvrx.FragmentViewModelContext +import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.ViewModelContext +import com.squareup.inject.assisted.Assisted +import com.squareup.inject.assisted.AssistedInject +import im.vector.app.core.platform.VectorViewModel + +class SearchViewModel @AssistedInject constructor( + @Assisted private val initialState: SearchViewState +) : VectorViewModel(initialState) { + + @AssistedInject.Factory + interface Factory { + fun create(initialState: SearchViewState): SearchViewModel + } + + companion object : MvRxViewModelFactory { + + @JvmStatic + override fun create(viewModelContext: ViewModelContext, state: SearchViewState): SearchViewModel? { + val fragment: SearchFragment = (viewModelContext as FragmentViewModelContext).fragment() + return fragment.viewModelFactory.create(state) + } + } + + override fun handle(action: SearchAction) { + } +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewState.kt new file mode 100644 index 0000000000..785d61dc00 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewState.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.home.room.detail.search + +import com.airbnb.mvrx.MvRxState + +data class SearchViewState( + val searchTerm: String = "" +) : MvRxState diff --git a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt index 3a8d302fc7..5ad600dfff 100644 --- a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt +++ b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt @@ -43,6 +43,8 @@ import im.vector.app.features.crypto.verification.VerificationBottomSheet import im.vector.app.features.debug.DebugMenuActivity import im.vector.app.features.home.room.detail.RoomDetailActivity import im.vector.app.features.home.room.detail.RoomDetailArgs +import im.vector.app.features.home.room.detail.search.SearchActivity +import im.vector.app.features.home.room.detail.search.SearchArgs import im.vector.app.features.home.room.detail.widget.WidgetRequestCodes import im.vector.app.features.home.room.filtered.FilteredRoomsActivity import im.vector.app.features.invite.InviteUsersToRoomActivity @@ -329,6 +331,11 @@ class DefaultNavigator @Inject constructor( } } + override fun openSearch(context: Context, roomId: String) { + val intent = SearchActivity.newIntent(context, SearchArgs(roomId)) + context.startActivity(intent) + } + private fun startActivity(context: Context, intent: Intent, buildTask: Boolean) { if (buildTask) { val stackBuilder = TaskStackBuilder.create(context) diff --git a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt index e92fea267f..b2efb1e931 100644 --- a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt +++ b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt @@ -109,4 +109,6 @@ interface Navigator { view: View, inMemory: List = emptyList(), options: ((MutableList>) -> Unit)?) + + fun openSearch(context: Context, roomId: String) } diff --git a/vector/src/main/res/layout/fragment_search.xml b/vector/src/main/res/layout/fragment_search.xml new file mode 100644 index 0000000000..77d9ef65f8 --- /dev/null +++ b/vector/src/main/res/layout/fragment_search.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file From 86b46d14426ec806dad0a29b4ae0388af558cc31 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Mon, 21 Sep 2020 17:01:27 +0300 Subject: [PATCH 14/36] Configure SearchView as activity toolbar. --- .../home/room/detail/search/SearchActivity.kt | 31 ++++++++++++- .../home/room/detail/search/SearchFragment.kt | 5 +++ .../src/main/res/layout/activity_search.xml | 43 +++++++++++++++++++ .../src/main/res/layout/fragment_search.xml | 3 ++ 4 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 vector/src/main/res/layout/activity_search.xml diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchActivity.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchActivity.kt index bedc4cb881..70205eed62 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchActivity.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchActivity.kt @@ -18,29 +18,56 @@ package im.vector.app.features.home.room.detail.search import android.content.Context import android.content.Intent +import android.os.Bundle +import androidx.appcompat.widget.SearchView import com.airbnb.mvrx.MvRx import im.vector.app.R import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.addFragment import im.vector.app.core.platform.VectorBaseActivity +import kotlinx.android.synthetic.main.activity_search.* class SearchActivity : VectorBaseActivity() { - override fun getLayoutRes() = R.layout.activity_simple + private val searchFragment: SearchFragment? + get() { + return supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) as? SearchFragment + } + + override fun getLayoutRes() = R.layout.activity_search override fun injectWith(injector: ScreenComponent) { super.injectWith(injector) injector.inject(this) } + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + configureToolbar(searchToolbar) + } + override fun initUiAndData() { if (isFirstCreation()) { val fragmentArgs: SearchArgs = intent?.extras?.getParcelable(MvRx.KEY_ARG) ?: return - addFragment(R.id.simpleFragmentContainer, SearchFragment::class.java, fragmentArgs) + addFragment(R.id.searchFragmentContainer, SearchFragment::class.java, fragmentArgs, FRAGMENT_TAG) } + searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { + override fun onQueryTextSubmit(query: String): Boolean { + searchFragment?.search(query) + return true + } + + override fun onQueryTextChange(newText: String): Boolean { + return true + } + }) + // Open the keyboard immediately + searchView.requestFocus() } companion object { + private const val FRAGMENT_TAG = "SearchFragment" + fun newIntent(context: Context, args: SearchArgs): Intent { return Intent(context, SearchActivity::class.java).apply { putExtra(MvRx.KEY_ARG, args) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt index f5b8440b58..0a0604eb38 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt @@ -24,6 +24,7 @@ import com.airbnb.mvrx.fragmentViewModel import im.vector.app.R import im.vector.app.core.platform.VectorBaseFragment import kotlinx.android.parcel.Parcelize +import timber.log.Timber import javax.inject.Inject @Parcelize @@ -43,4 +44,8 @@ class SearchFragment @Inject constructor( override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) } + + fun search(query: String) { + Timber.d(query) + } } diff --git a/vector/src/main/res/layout/activity_search.xml b/vector/src/main/res/layout/activity_search.xml new file mode 100644 index 0000000000..a3aa21eecc --- /dev/null +++ b/vector/src/main/res/layout/activity_search.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/layout/fragment_search.xml b/vector/src/main/res/layout/fragment_search.xml index 77d9ef65f8..2916089675 100644 --- a/vector/src/main/res/layout/fragment_search.xml +++ b/vector/src/main/res/layout/fragment_search.xml @@ -1,6 +1,9 @@ + + \ No newline at end of file From 8759bcca84c69e8aab5e7aa365ba37922ae05f32 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Thu, 24 Sep 2020 10:14:36 +0300 Subject: [PATCH 15/36] Map api response to a domain object. --- .../sdk/session/search/SearchMessagesTest.kt | 22 ++++++------- .../android/sdk/api/session/room/Room.kt | 4 +-- .../sdk/api/session/search/SearchResult.kt | 32 +++++++++++++++++++ .../sdk/api/session/search/SearchService.kt | 3 +- .../sdk/internal/session/room/DefaultRoom.kt | 4 +-- .../sdk/internal/session/room/RoomAPI.kt | 2 +- .../session/search/DefaultSearchService.kt | 4 +-- .../sdk/internal/session/search/SearchTask.kt | 17 +++++++--- 8 files changed, 64 insertions(+), 24 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/search/SearchResult.kt diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/search/SearchMessagesTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/search/SearchMessagesTest.kt index b7d253ca00..1d4eb758ed 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/search/SearchMessagesTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/search/SearchMessagesTest.kt @@ -30,10 +30,10 @@ import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.model.message.MessageContent import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings +import org.matrix.android.sdk.api.session.search.SearchResult import org.matrix.android.sdk.common.CommonTestHelper import org.matrix.android.sdk.common.CryptoTestHelper import org.matrix.android.sdk.common.TestConstants -import org.matrix.android.sdk.internal.session.search.response.SearchResponse import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit @@ -83,14 +83,14 @@ class SearchMessagesTest : InstrumentedTest { orderByRecent = true, nextBatch = null, roomId = aliceRoomId, - callback = object : MatrixCallback { - override fun onSuccess(data: SearchResponse) { + callback = object : MatrixCallback { + override fun onSuccess(data: SearchResult) { super.onSuccess(data) - assertTrue(data.searchCategories.roomEvents?.results?.size == 2) + assertTrue(data.results?.size == 2) assertTrue( - data.searchCategories.roomEvents?.results + data.results ?.all { - (it.event.content?.get("body") as? String)?.startsWith(MESSAGE).orFalse() + (it.content?.get("body") as? String)?.startsWith(MESSAGE).orFalse() }.orFalse() ) lock.countDown() @@ -147,14 +147,14 @@ class SearchMessagesTest : InstrumentedTest { beforeLimit = 10, orderByRecent = true, nextBatch = null, - callback = object : MatrixCallback { - override fun onSuccess(data: SearchResponse) { + callback = object : MatrixCallback { + override fun onSuccess(data: SearchResult) { super.onSuccess(data) - assertTrue(data.searchCategories.roomEvents?.results?.size == 2) + assertTrue(data.results?.size == 2) assertTrue( - data.searchCategories.roomEvents?.results + data.results ?.all { - (it.event.content?.get("body") as? String)?.startsWith(MESSAGE).orFalse() + (it.content?.get("body") as? String)?.startsWith(MESSAGE).orFalse() }.orFalse() ) lock.countDown() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/Room.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/Room.kt index d09a42753b..a27cdad551 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/Room.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/Room.kt @@ -34,9 +34,9 @@ import org.matrix.android.sdk.api.session.room.tags.TagsService import org.matrix.android.sdk.api.session.room.timeline.TimelineService import org.matrix.android.sdk.api.session.room.typing.TypingService import org.matrix.android.sdk.api.session.room.uploads.UploadsService +import org.matrix.android.sdk.api.session.search.SearchResult import org.matrix.android.sdk.api.util.Cancelable import org.matrix.android.sdk.api.util.Optional -import org.matrix.android.sdk.internal.session.search.response.SearchResponse /** * This interface defines methods to interact within a room. @@ -90,5 +90,5 @@ interface Room : beforeLimit: Int, afterLimit: Int, includeProfile: Boolean, - callback: MatrixCallback): Cancelable + callback: MatrixCallback): Cancelable } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/search/SearchResult.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/search/SearchResult.kt new file mode 100644 index 0000000000..4529bf6d13 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/search/SearchResult.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.api.session.search + +import org.matrix.android.sdk.api.session.events.model.Event + +/** + * Domain class to represent the response of a search request in a room. + */ +data class SearchResult( + // Token that can be used to get the next batch of results, by passing as the next_batch parameter to the next call. If this field is absent, there are no more results. + val nextBatch: String? = null, + // List of words which should be highlighted, useful for stemming which may change the query terms. + var highlights: List? = null, + // List of results in the requested order. + var results: List? = null +) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/search/SearchService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/search/SearchService.kt index 2087af6a81..b089a597e3 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/search/SearchService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/search/SearchService.kt @@ -19,7 +19,6 @@ package org.matrix.android.sdk.api.session.search import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.util.Cancelable -import org.matrix.android.sdk.internal.session.search.response.SearchResponse /** * This interface defines methods to search messages in rooms. @@ -45,5 +44,5 @@ interface SearchService { beforeLimit: Int, afterLimit: Int, includeProfile: Boolean, - callback: MatrixCallback): Cancelable + callback: MatrixCallback): Cancelable } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoom.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoom.kt index 33fa98f8ce..0eb64af8a0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoom.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoom.kt @@ -36,13 +36,13 @@ import org.matrix.android.sdk.api.session.room.tags.TagsService import org.matrix.android.sdk.api.session.room.timeline.TimelineService import org.matrix.android.sdk.api.session.room.typing.TypingService import org.matrix.android.sdk.api.session.room.uploads.UploadsService +import org.matrix.android.sdk.api.session.search.SearchResult import org.matrix.android.sdk.api.util.Cancelable import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM import org.matrix.android.sdk.internal.session.room.state.SendStateTask import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryDataSource import org.matrix.android.sdk.internal.session.search.SearchTask -import org.matrix.android.sdk.internal.session.search.response.SearchResponse import org.matrix.android.sdk.internal.task.TaskExecutor import org.matrix.android.sdk.internal.task.configureWith import java.security.InvalidParameterException @@ -135,7 +135,7 @@ internal class DefaultRoom @Inject constructor(override val roomId: String, beforeLimit: Int, afterLimit: Int, includeProfile: Boolean, - callback: MatrixCallback): Cancelable { + callback: MatrixCallback): Cancelable { return searchTask .configureWith(SearchTask.Params( searchTerm = searchTerm, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt index 1a23f3a2f5..db99adbafb 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt @@ -36,12 +36,12 @@ import org.matrix.android.sdk.internal.session.room.membership.threepid.ThreePid import org.matrix.android.sdk.internal.session.room.relation.RelationsResponse import org.matrix.android.sdk.internal.session.room.reporting.ReportContentBody import org.matrix.android.sdk.internal.session.search.request.SearchRequestBody -import org.matrix.android.sdk.internal.session.search.response.SearchResponse import org.matrix.android.sdk.internal.session.room.send.SendResponse import org.matrix.android.sdk.internal.session.room.tags.TagBody import org.matrix.android.sdk.internal.session.room.timeline.EventContextResponse import org.matrix.android.sdk.internal.session.room.timeline.PaginationResponse import org.matrix.android.sdk.internal.session.room.typing.TypingBody +import org.matrix.android.sdk.internal.session.search.response.SearchResponse import retrofit2.Call import retrofit2.http.Body import retrofit2.http.DELETE diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/DefaultSearchService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/DefaultSearchService.kt index 1f158ec41b..0b380f4d35 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/DefaultSearchService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/DefaultSearchService.kt @@ -18,9 +18,9 @@ package org.matrix.android.sdk.internal.session.search import org.matrix.android.sdk.api.MatrixCallback +import org.matrix.android.sdk.api.session.search.SearchResult import org.matrix.android.sdk.api.session.search.SearchService import org.matrix.android.sdk.api.util.Cancelable -import org.matrix.android.sdk.internal.session.search.response.SearchResponse import javax.inject.Inject import org.matrix.android.sdk.internal.task.TaskExecutor import org.matrix.android.sdk.internal.task.configureWith @@ -38,7 +38,7 @@ internal class DefaultSearchService @Inject constructor( beforeLimit: Int, afterLimit: Int, includeProfile: Boolean, - callback: MatrixCallback): Cancelable { + callback: MatrixCallback): Cancelable { return searchTask .configureWith(SearchTask.Params( searchTerm = searchTerm, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchTask.kt index 17f980bb77..4502d2a60a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchTask.kt @@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.session.search import org.greenrobot.eventbus.EventBus +import org.matrix.android.sdk.api.session.search.SearchResult import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.session.room.RoomAPI import org.matrix.android.sdk.internal.session.search.request.SearchRequestBody @@ -30,7 +31,7 @@ import org.matrix.android.sdk.internal.session.search.response.SearchResponse import org.matrix.android.sdk.internal.task.Task import javax.inject.Inject -internal interface SearchTask : Task { +internal interface SearchTask : Task { data class Params( val searchTerm: String, @@ -49,8 +50,8 @@ internal class DefaultSearchTask @Inject constructor( private val eventBus: EventBus ) : SearchTask { - override suspend fun execute(params: SearchTask.Params): SearchResponse { - return executeRequest(eventBus) { + override suspend fun execute(params: SearchTask.Params): SearchResult { + return executeRequest(eventBus) { val searchRequestBody = SearchRequestBody( searchCategories = SearchRequestCategories( roomEvents = SearchRequestRoomEvents( @@ -69,6 +70,14 @@ internal class DefaultSearchTask @Inject constructor( ) ) apiCall = roomAPI.search(params.nextBatch, searchRequestBody) - } + }.toDomain().apply { results = results?.reversed() } + } + + private fun SearchResponse.toDomain(): SearchResult { + return SearchResult( + nextBatch = searchCategories.roomEvents?.nextBatch, + highlights = searchCategories.roomEvents?.highlights, + results = searchCategories.roomEvents?.results?.map { it.event } + ) } } From d6fcf63230e291f7e0b68a86266d4afd87d54205 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Thu, 24 Sep 2020 10:19:26 +0300 Subject: [PATCH 16/36] Implement search result controller and item. --- .../detail/search/SearchResultController.kt | 80 +++++++++++++++++++ .../room/detail/search/SearchResultItem.kt | 63 +++++++++++++++ .../src/main/res/layout/fragment_search.xml | 15 ++-- .../main/res/layout/item_search_result.xml | 60 ++++++++++++++ vector/src/main/res/values/strings.xml | 1 + 5 files changed, 214 insertions(+), 5 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultController.kt create mode 100644 vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultItem.kt create mode 100644 vector/src/main/res/layout/item_search_result.xml diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultController.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultController.kt new file mode 100644 index 0000000000..82d3120311 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultController.kt @@ -0,0 +1,80 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.home.room.detail.search + +import com.airbnb.epoxy.TypedEpoxyController +import im.vector.app.core.date.DateFormatKind +import im.vector.app.core.date.VectorDateFormatter +import im.vector.app.core.ui.list.genericItemHeader +import im.vector.app.features.home.AvatarRenderer +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.events.model.Event +import java.util.Calendar +import javax.inject.Inject + +class SearchResultController @Inject constructor( + private val session: Session, + private val avatarRenderer: AvatarRenderer, + private val dateFormatter: VectorDateFormatter +) : TypedEpoxyController() { + + var listener: Listener? = null + + interface Listener { + fun onItemClicked(event: Event) + } + + init { + setData(null) + } + + override fun buildModels(data: SearchViewState?) { + data?.searchResult?.results ?: return + + buildSearchResultItems(data.searchResult.results!!) + } + + private fun buildSearchResultItems(events: List) { + var lastDate: Calendar? = null + + events.forEach { event -> + val eventDate = Calendar.getInstance().apply { + timeInMillis = event.originServerTs ?: System.currentTimeMillis() + } + if (lastDate?.get(Calendar.DAY_OF_YEAR) != eventDate.get(Calendar.DAY_OF_YEAR)) { + genericItemHeader { + id(eventDate.hashCode()) + text(dateFormatter.format(eventDate.timeInMillis, DateFormatKind.EDIT_HISTORY_HEADER)) + } + } + lastDate = eventDate + + searchResultItem { + id(event.eventId) + avatarRenderer(avatarRenderer) + dateFormatter(dateFormatter) + event(event) + sender(event.senderId?.let { session.getUser(it) }) + listener(object : SearchResultItem.Listener { + override fun onItemClicked() { + listener?.onItemClicked(event) + } + }) + } + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultItem.kt new file mode 100644 index 0000000000..fb0121cbcf --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultItem.kt @@ -0,0 +1,63 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.home.room.detail.search + +import android.widget.ImageView +import android.widget.TextView +import com.airbnb.epoxy.EpoxyAttribute +import com.airbnb.epoxy.EpoxyModelClass +import im.vector.app.R +import im.vector.app.core.date.DateFormatKind +import im.vector.app.core.date.VectorDateFormatter +import im.vector.app.core.epoxy.VectorEpoxyHolder +import im.vector.app.core.epoxy.VectorEpoxyModel +import im.vector.app.features.home.AvatarRenderer +import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.api.session.user.model.User +import org.matrix.android.sdk.api.util.toMatrixItem + +@EpoxyModelClass(layout = R.layout.item_search_result) +abstract class SearchResultItem : VectorEpoxyModel() { + + @EpoxyAttribute var avatarRenderer: AvatarRenderer? = null + @EpoxyAttribute var dateFormatter: VectorDateFormatter? = null + @EpoxyAttribute var event: Event? = null + @EpoxyAttribute var sender: User? = null + @EpoxyAttribute var listener: Listener? = null + + override fun bind(holder: Holder) { + super.bind(holder) + event ?: return + + holder.view.setOnClickListener { listener?.onItemClicked() } + sender?.toMatrixItem()?.let { avatarRenderer?.render(it, holder.avatarImageView) } + holder.memberNameView.text = sender?.displayName + holder.timeView.text = dateFormatter?.format(event!!.originServerTs, DateFormatKind.MESSAGE_SIMPLE) + holder.contentView.text = event?.content?.get("body") as? String + } + + class Holder : VectorEpoxyHolder() { + val avatarImageView by bind(R.id.messageAvatarImageView) + val memberNameView by bind(R.id.messageMemberNameView) + val timeView by bind(R.id.messageTimeView) + val contentView by bind(R.id.messageContentView) + } + + interface Listener { + fun onItemClicked() + } +} diff --git a/vector/src/main/res/layout/fragment_search.xml b/vector/src/main/res/layout/fragment_search.xml index 2916089675..5478e1f68c 100644 --- a/vector/src/main/res/layout/fragment_search.xml +++ b/vector/src/main/res/layout/fragment_search.xml @@ -1,9 +1,14 @@ - + android:layout_height="match_parent" + android:background="?riotx_header_panel_background"> + - - \ No newline at end of file + \ No newline at end of file diff --git a/vector/src/main/res/layout/item_search_result.xml b/vector/src/main/res/layout/item_search_result.xml new file mode 100644 index 0000000000..9946a2fb50 --- /dev/null +++ b/vector/src/main/res/layout/item_search_result.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 0edd930498..ee5b8fecb1 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -628,6 +628,7 @@ MESSAGES PEOPLE FILES + Searching in encrypted rooms is not supported yet. JOIN From 6c6d0dbc3d9f907faf4dc85f3054fc17c4bd7d51 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Thu, 24 Sep 2020 10:20:36 +0300 Subject: [PATCH 17/36] Show not supported warning for e2e rooms. --- .../features/home/room/detail/RoomDetailFragment.kt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt index 34aa6a0831..51aeda2aab 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt @@ -674,13 +674,21 @@ class RoomDetailFragment @Inject constructor( true } R.id.search -> { - navigator.openSearch(requireContext(), roomDetailArgs.roomId) + handleSearchAction() true } else -> super.onOptionsItemSelected(item) } } + private fun handleSearchAction() { + if (session.getRoom(roomDetailArgs.roomId)?.isEncrypted() == false) { + navigator.openSearch(requireContext(), roomDetailArgs.roomId) + } else { + showDialogWithMessage(getString(R.string.search_is_not_supported_in_e2e_room)) + } + } + private fun handleCallRequest(item: MenuItem) = withState(roomDetailViewModel) { state -> val roomSummary = state.asyncRoomSummary.invoke() ?: return@withState val isVideoCall = item.itemId == R.id.video_call From 5e56e7cf829464d429ed06cc2ffe8013bfe10ef8 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Thu, 24 Sep 2020 10:21:26 +0300 Subject: [PATCH 18/36] Implement search state, action and view events. --- .../app/features/home/room/detail/search/SearchAction.kt | 6 +++++- .../features/home/room/detail/search/SearchViewEvents.kt | 5 ++++- .../features/home/room/detail/search/SearchViewState.kt | 9 ++++++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchAction.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchAction.kt index 2774ef4d8e..d295784a8c 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchAction.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchAction.kt @@ -18,4 +18,8 @@ package im.vector.app.features.home.room.detail.search import im.vector.app.core.platform.VectorViewModelAction -sealed class SearchAction : VectorViewModelAction +sealed class SearchAction : VectorViewModelAction { + data class SearchWith(val roomId: String, val searchTerm: String) : SearchAction() + object ScrolledToTop : SearchAction() + object Retry : SearchAction() +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewEvents.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewEvents.kt index a5489e9240..41dabd8686 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewEvents.kt @@ -18,4 +18,7 @@ package im.vector.app.features.home.room.detail.search import im.vector.app.core.platform.VectorViewEvents -sealed class SearchViewEvents : VectorViewEvents +sealed class SearchViewEvents : VectorViewEvents { + data class Failure(val throwable: Throwable) : SearchViewEvents() + data class Loading(val message: CharSequence? = null) : SearchViewEvents() +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewState.kt index 785d61dc00..02db098e68 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewState.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewState.kt @@ -17,7 +17,14 @@ package im.vector.app.features.home.room.detail.search import com.airbnb.mvrx.MvRxState +import org.matrix.android.sdk.api.session.search.SearchResult data class SearchViewState( - val searchTerm: String = "" + // Accumulated search result + val searchResult: SearchResult? = null, + // Last batch result will help RecyclerView to position itself + val lastBatch: SearchResult? = null, + val searchTerm: String? = null, + val roomId: String? = null, + val isNextBatch: Boolean = false ) : MvRxState From 62449ee543bec0593b060f8cb7181385cb67f846 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Thu, 24 Sep 2020 10:22:26 +0300 Subject: [PATCH 19/36] Support searching and pagination. --- .../home/room/detail/search/SearchFragment.kt | 95 ++++++++++++++++++- .../room/detail/search/SearchViewModel.kt | 92 +++++++++++++++++- 2 files changed, 182 insertions(+), 5 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt index 0a0604eb38..1f8f7fa422 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt @@ -19,12 +19,24 @@ package im.vector.app.features.home.room.detail.search import android.os.Bundle import android.os.Parcelable import android.view.View +import androidx.core.content.ContextCompat +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel +import com.airbnb.mvrx.withState import im.vector.app.R +import im.vector.app.core.extensions.cleanup +import im.vector.app.core.extensions.configureWith +import im.vector.app.core.extensions.exhaustive +import im.vector.app.core.extensions.hideKeyboard +import im.vector.app.core.extensions.trackItemsVisibilityChange +import im.vector.app.core.platform.StateView import im.vector.app.core.platform.VectorBaseFragment +import im.vector.app.core.resources.StringProvider import kotlinx.android.parcel.Parcelize -import timber.log.Timber +import kotlinx.android.synthetic.main.fragment_search.* +import org.matrix.android.sdk.api.session.events.model.Event import javax.inject.Inject @Parcelize @@ -33,19 +45,94 @@ data class SearchArgs( ) : Parcelable class SearchFragment @Inject constructor( - val viewModelFactory: SearchViewModel.Factory -) : VectorBaseFragment() { + val viewModelFactory: SearchViewModel.Factory, + val controller: SearchResultController, + val stringProvider: StringProvider +) : VectorBaseFragment(), StateView.EventCallback, SearchResultController.Listener { private val fragmentArgs: SearchArgs by args() private val searchViewModel: SearchViewModel by fragmentViewModel() + private var pendingScrollToPosition: Int? = null + override fun getLayoutResId() = R.layout.fragment_search override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + + stateView.contentView = searchResultRecycler + stateView.eventCallback = this + + configureRecyclerView() + + searchViewModel.observeViewEvents { + when (it) { + is SearchViewEvents.Failure -> { + stateView.state = StateView.State.Error(errorFormatter.toHumanReadable(it.throwable)) + } + is SearchViewEvents.Loading -> { + stateView.state = StateView.State.Loading + } + }.exhaustive + } + } + + private fun configureRecyclerView() { + searchResultRecycler.trackItemsVisibilityChange() + searchResultRecycler.configureWith(controller, showDivider = false) + controller.listener = this + + controller.addModelBuildListener { + pendingScrollToPosition?.let { + searchResultRecycler.scrollToPosition(it) + } + } + + searchResultRecycler.addOnScrollListener(object : RecyclerView.OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + // Load next batch when scrolled to the top + if (newState == RecyclerView.SCROLL_STATE_IDLE + && (searchResultRecycler.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition() == 0) { + searchViewModel.handle(SearchAction.ScrolledToTop) + } + } + }) + } + + override fun onDestroy() { + super.onDestroy() + searchResultRecycler?.cleanup() + controller.listener = null + } + + override fun invalidate() = withState(searchViewModel) { state -> + if (state.searchResult?.results?.isNotEmpty() == true) { + stateView.state = StateView.State.Content + controller.setData(state) + + val lastBatchSize = state.lastBatch?.results?.size ?: 0 + val scrollPosition = if (lastBatchSize > 0) lastBatchSize - 1 else 0 + pendingScrollToPosition = scrollPosition + } else { + stateView.state = StateView.State.Empty( + title = stringProvider.getString(R.string.search_no_results), + image = ContextCompat.getDrawable(requireContext(), R.drawable.ic_search) + ) + } } fun search(query: String) { - Timber.d(query) + view?.hideKeyboard() + searchViewModel.handle(SearchAction.SearchWith(fragmentArgs.roomId, query)) + } + + override fun onRetryClicked() { + searchViewModel.handle(SearchAction.Retry) + } + + override fun onItemClicked(event: Event) { + event.roomId ?: return + + navigator.openRoom(requireContext(), event.roomId!!, event.eventId) } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt index de75c0dd20..67befc80be 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt @@ -21,10 +21,15 @@ import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject +import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel +import org.matrix.android.sdk.api.MatrixCallback +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.search.SearchResult class SearchViewModel @AssistedInject constructor( - @Assisted private val initialState: SearchViewState + @Assisted private val initialState: SearchViewState, + private val session: Session ) : VectorViewModel(initialState) { @AssistedInject.Factory @@ -42,5 +47,90 @@ class SearchViewModel @AssistedInject constructor( } override fun handle(action: SearchAction) { + when (action) { + is SearchAction.SearchWith -> handleSearchWith(action) + is SearchAction.ScrolledToTop -> handleScrolledToTop() + is SearchAction.Retry -> handleRetry() + }.exhaustive + } + + private fun handleSearchWith(action: SearchAction.SearchWith) { + if (action.searchTerm.length > 1) { + setState { + copy(searchTerm = action.searchTerm, roomId = action.roomId, isNextBatch = false) + } + + startSearching() + } + } + + private fun handleScrolledToTop() { + setState { + copy(isNextBatch = true) + } + startSearching(true) + } + + private fun handleRetry() { + startSearching() + } + + private fun startSearching(scrolledToTop: Boolean = false) = withState { state -> + if (state.roomId == null || state.searchTerm == null) return@withState + + // There is no batch to retrieve + if (scrolledToTop && state.searchResult?.nextBatch == null) return@withState + + _viewEvents.post(SearchViewEvents.Loading()) + + session + .getRoom(state.roomId) + ?.search( + searchTerm = state.searchTerm, + nextBatch = state.searchResult?.nextBatch, + orderByRecent = true, + beforeLimit = 0, + afterLimit = 0, + includeProfile = true, + limit = 20, + callback = object : MatrixCallback { + override fun onFailure(failure: Throwable) { + onSearchFailure(failure) + } + + override fun onSuccess(data: SearchResult) { + onSearchResultSuccess(data) + } + } + ) + } + + private fun onSearchFailure(failure: Throwable) { + setState { + copy(searchResult = null) + } + _viewEvents.post(SearchViewEvents.Failure(failure)) + } + + private fun onSearchResultSuccess(searchResult: SearchResult) = withState { state -> + val accumulatedResult = SearchResult( + nextBatch = searchResult.nextBatch, + results = searchResult.results, + highlights = searchResult.highlights + ) + + // Accumulate results if it is the next batch + if (state.isNextBatch) { + if (state.searchResult != null) { + accumulatedResult.results = accumulatedResult.results?.plus(state.searchResult.results!!) + } + if (state.searchResult?.highlights != null) { + accumulatedResult.highlights = accumulatedResult.highlights?.plus(state.searchResult.highlights!!) + } + } + + setState { + copy(searchResult = accumulatedResult, lastBatch = searchResult) + } } } From 0df3a8ad986b6c712105d8ae8002017178987e98 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Thu, 24 Sep 2020 11:44:17 +0300 Subject: [PATCH 20/36] Changelog added. --- CHANGES.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 026d7af6cd..c357515efa 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,7 @@ Changes in Element 1.0.9 (2020-XX-XX) =================================================== Features ✨: + - Search messages in a room - phase 1 (#2110) - Hide encrypted history (before user is invited). Can be shown if wanted in developer settings Improvements 🙌: @@ -18,7 +19,7 @@ Translations 🗣: - SDK API changes ⚠️: - - + - Search messages in a room by using Session.searchService() or Room.search() Build 🧱: - Use Update Gradle Wrapper Action From 70be853acdbda871fee8f05de2303b151c3e1f9e Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Thu, 24 Sep 2020 12:21:31 +0300 Subject: [PATCH 21/36] Update no results found icon. --- .../home/room/detail/search/SearchFragment.kt | 2 +- .../res/drawable/ic_search_no_results.xml | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 vector/src/main/res/drawable/ic_search_no_results.xml diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt index 1f8f7fa422..fa60d33e64 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt @@ -116,7 +116,7 @@ class SearchFragment @Inject constructor( } else { stateView.state = StateView.State.Empty( title = stringProvider.getString(R.string.search_no_results), - image = ContextCompat.getDrawable(requireContext(), R.drawable.ic_search) + image = ContextCompat.getDrawable(requireContext(), R.drawable.ic_search_no_results) ) } } diff --git a/vector/src/main/res/drawable/ic_search_no_results.xml b/vector/src/main/res/drawable/ic_search_no_results.xml new file mode 100644 index 0000000000..24c0f00131 --- /dev/null +++ b/vector/src/main/res/drawable/ic_search_no_results.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + \ No newline at end of file From 430a0d249221f2830135c5b60de26699554ae5cc Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Mon, 28 Sep 2020 18:51:30 +0300 Subject: [PATCH 22/36] Create separate search module. --- .../sdk/internal/session/SessionComponent.kt | 4 +- .../sdk/internal/session/SessionModule.kt | 10 ----- .../sdk/internal/session/room/RoomAPI.kt | 10 ----- .../sdk/internal/session/search/SearchAPI.kt | 36 +++++++++++++++ .../internal/session/search/SearchModule.kt | 44 +++++++++++++++++++ .../sdk/internal/session/search/SearchTask.kt | 5 +-- 6 files changed, 85 insertions(+), 24 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchAPI.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchModule.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt index 475450837e..123b87748f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt @@ -50,6 +50,7 @@ import org.matrix.android.sdk.internal.session.room.send.EncryptEventWorker import org.matrix.android.sdk.internal.session.room.send.MultipleEventSendingDispatcherWorker import org.matrix.android.sdk.internal.session.room.send.RedactEventWorker import org.matrix.android.sdk.internal.session.room.send.SendEventWorker +import org.matrix.android.sdk.internal.session.search.SearchModule import org.matrix.android.sdk.internal.session.signout.SignOutModule import org.matrix.android.sdk.internal.session.sync.SyncModule import org.matrix.android.sdk.internal.session.sync.SyncTask @@ -86,7 +87,8 @@ import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers ProfileModule::class, SessionAssistedInjectModule::class, AccountModule::class, - CallModule::class + CallModule::class, + SearchModule::class ] ) @SessionScope diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt index 54478cdc02..5397b8d9bd 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt @@ -39,7 +39,6 @@ import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.accountdata.AccountDataService import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilitiesService import org.matrix.android.sdk.api.session.permalinks.PermalinkService -import org.matrix.android.sdk.api.session.search.SearchService import org.matrix.android.sdk.api.session.securestorage.SecureStorageService import org.matrix.android.sdk.api.session.securestorage.SharedSecretStorageService import org.matrix.android.sdk.api.session.typing.TypingUsersTracker @@ -82,10 +81,7 @@ import org.matrix.android.sdk.internal.session.permalinks.DefaultPermalinkServic import org.matrix.android.sdk.internal.session.room.EventRelationsAggregationProcessor import org.matrix.android.sdk.internal.session.room.create.RoomCreateEventProcessor import org.matrix.android.sdk.internal.session.room.prune.RedactionEventProcessor -import org.matrix.android.sdk.internal.session.search.DefaultSearchService import org.matrix.android.sdk.internal.session.room.tombstone.RoomTombstoneEventProcessor -import org.matrix.android.sdk.internal.session.search.DefaultSearchTask -import org.matrix.android.sdk.internal.session.search.SearchTask import org.matrix.android.sdk.internal.session.securestorage.DefaultSecureStorageService import org.matrix.android.sdk.internal.session.typing.DefaultTypingUsersTracker import org.matrix.android.sdk.internal.session.user.accountdata.DefaultAccountDataService @@ -372,10 +368,4 @@ internal abstract class SessionModule { @Binds abstract fun bindTypingUsersTracker(tracker: DefaultTypingUsersTracker): TypingUsersTracker - - @Binds - abstract fun bindSearchService(service: DefaultSearchService): SearchService - - @Binds - abstract fun bindSearchTask(task: DefaultSearchTask): SearchTask } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt index db99adbafb..35c20cf5cb 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt @@ -35,13 +35,11 @@ import org.matrix.android.sdk.internal.session.room.membership.joining.InviteBod import org.matrix.android.sdk.internal.session.room.membership.threepid.ThreePidInviteBody import org.matrix.android.sdk.internal.session.room.relation.RelationsResponse import org.matrix.android.sdk.internal.session.room.reporting.ReportContentBody -import org.matrix.android.sdk.internal.session.search.request.SearchRequestBody import org.matrix.android.sdk.internal.session.room.send.SendResponse import org.matrix.android.sdk.internal.session.room.tags.TagBody import org.matrix.android.sdk.internal.session.room.timeline.EventContextResponse import org.matrix.android.sdk.internal.session.room.timeline.PaginationResponse import org.matrix.android.sdk.internal.session.room.typing.TypingBody -import org.matrix.android.sdk.internal.session.search.response.SearchResponse import retrofit2.Call import retrofit2.http.Body import retrofit2.http.DELETE @@ -367,12 +365,4 @@ internal interface RoomAPI { fun deleteTag(@Path("userId") userId: String, @Path("roomId") roomId: String, @Path("tag") tag: String): Call - - /** - * Performs a full text search across different categories. - */ - @POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "search") - fun search( - @Query("next_batch") nextBatch: String?, - @Body body: SearchRequestBody): Call } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchAPI.kt new file mode 100644 index 0000000000..3d5dbc306f --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchAPI.kt @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.session.search + +import org.matrix.android.sdk.internal.network.NetworkConstants +import org.matrix.android.sdk.internal.session.search.request.SearchRequestBody +import org.matrix.android.sdk.internal.session.search.response.SearchResponse +import retrofit2.Call +import retrofit2.http.Body +import retrofit2.http.POST +import retrofit2.http.Query + +internal interface SearchAPI { + + /** + * Performs a full text search across different categories. + */ + @POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "search") + fun search( + @Query("next_batch") nextBatch: String?, + @Body body: SearchRequestBody): Call +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchModule.kt new file mode 100644 index 0000000000..50cf8b8d84 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchModule.kt @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.session.search + +import dagger.Binds +import dagger.Module +import dagger.Provides +import org.matrix.android.sdk.api.session.search.SearchService +import org.matrix.android.sdk.internal.session.SessionScope +import retrofit2.Retrofit + +@Module +internal abstract class SearchModule { + + @Module + companion object { + @Provides + @JvmStatic + @SessionScope + fun providesSearchAPI(retrofit: Retrofit): SearchAPI { + return retrofit.create(SearchAPI::class.java) + } + } + + @Binds + abstract fun bindSearchService(service: DefaultSearchService): SearchService + + @Binds + abstract fun bindSearchTask(task: DefaultSearchTask): SearchTask +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchTask.kt index 4502d2a60a..d36fc80d5c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchTask.kt @@ -20,7 +20,6 @@ package org.matrix.android.sdk.internal.session.search import org.greenrobot.eventbus.EventBus import org.matrix.android.sdk.api.session.search.SearchResult import org.matrix.android.sdk.internal.network.executeRequest -import org.matrix.android.sdk.internal.session.room.RoomAPI import org.matrix.android.sdk.internal.session.search.request.SearchRequestBody import org.matrix.android.sdk.internal.session.search.request.SearchRequestCategories import org.matrix.android.sdk.internal.session.search.request.SearchRequestEventContext @@ -46,7 +45,7 @@ internal interface SearchTask : Task { } internal class DefaultSearchTask @Inject constructor( - private val roomAPI: RoomAPI, + private val searchAPI: SearchAPI, private val eventBus: EventBus ) : SearchTask { @@ -69,7 +68,7 @@ internal class DefaultSearchTask @Inject constructor( ) ) ) - apiCall = roomAPI.search(params.nextBatch, searchRequestBody) + apiCall = searchAPI.search(params.nextBatch, searchRequestBody) }.toDomain().apply { results = results?.reversed() } } From e21f306635c9cbc110b0992a41bafce0f1220c21 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Mon, 28 Sep 2020 19:05:33 +0300 Subject: [PATCH 23/36] Use enum directly instead of its value. --- .../android/sdk/internal/session/search/SearchTask.kt | 2 +- .../internal/session/search/request/SearchRequestOrder.kt | 7 ++++--- .../session/search/request/SearchRequestRoomEvents.kt | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchTask.kt index d36fc80d5c..a2cad6bb2f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchTask.kt @@ -55,7 +55,7 @@ internal class DefaultSearchTask @Inject constructor( searchCategories = SearchRequestCategories( roomEvents = SearchRequestRoomEvents( searchTerm = params.searchTerm, - orderBy = if (params.orderByRecent) SearchRequestOrder.RECENT.value else SearchRequestOrder.RANK.value, + orderBy = if (params.orderByRecent) SearchRequestOrder.RECENT else SearchRequestOrder.RANK, filter = SearchRequestFilter( limit = params.limit, rooms = listOf(params.roomId) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/request/SearchRequestOrder.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/request/SearchRequestOrder.kt index eac51ad2df..f0b6f7bc31 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/request/SearchRequestOrder.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/request/SearchRequestOrder.kt @@ -17,13 +17,14 @@ package org.matrix.android.sdk.internal.session.search.request +import com.squareup.moshi.Json import com.squareup.moshi.JsonClass /** * Represents the order in which to search for results. */ @JsonClass(generateAdapter = false) -internal enum class SearchRequestOrder(val value: String) { - RANK("rank"), - RECENT("recent") +internal enum class SearchRequestOrder { + @Json(name = "rank") RANK, + @Json(name = "recent") RECENT } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/request/SearchRequestRoomEvents.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/request/SearchRequestRoomEvents.kt index 26b11e93bd..fef1baa990 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/request/SearchRequestRoomEvents.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/request/SearchRequestRoomEvents.kt @@ -29,7 +29,7 @@ internal data class SearchRequestRoomEvents( val filter: SearchRequestFilter? = null, // By default, this is "rank". One of: ["recent", "rank"] @Json(name = "order_by") - val orderBy: String? = null, + val orderBy: SearchRequestOrder? = null, // Configures whether any context for the events returned are included in the response. @Json(name = "event_context") val eventContext: SearchRequestEventContext? = null From 4683dc3f21bc0048f2a8d8f94d7adf9a078f1efc Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Mon, 28 Sep 2020 19:55:56 +0300 Subject: [PATCH 24/36] Documentation added to orderByRecent parameter of the search request. --- .../main/java/org/matrix/android/sdk/api/session/room/Room.kt | 1 + .../org/matrix/android/sdk/api/session/search/SearchService.kt | 1 + 2 files changed, 2 insertions(+) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/Room.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/Room.kt index a27cdad551..86029e5419 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/Room.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/Room.kt @@ -78,6 +78,7 @@ interface Room : * Ref: https://matrix.org/docs/spec/client_server/latest#module-search * @param searchTerm the term to search * @param nextBatch the token that retrieved from the previous response. Should be provided to get the next batch of results + * @param orderByRecent if true, the most recent message events will return in the first places of the list * @param limit the maximum number of events to return. * @param beforeLimit how many events before the result are returned. * @param afterLimit how many events after the result are returned. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/search/SearchService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/search/SearchService.kt index b089a597e3..0abbdfd958 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/search/SearchService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/search/SearchService.kt @@ -31,6 +31,7 @@ interface SearchService { * @param searchTerm the term to search * @param roomId the roomId to search term inside * @param nextBatch the token that retrieved from the previous response. Should be provided to get the next batch of results + * @param orderByRecent if true, the most recent message events will return in the first places of the list * @param limit the maximum number of events to return. * @param beforeLimit how many events before the result are returned. * @param afterLimit how many events after the result are returned. From da344fae5190fac24fad4d2ff66a7d92fbe2eb43 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Mon, 28 Sep 2020 20:06:39 +0300 Subject: [PATCH 25/36] One case per line. --- .../home/room/detail/RoomDetailViewModel.kt | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt index f8229731d8..1b5e928843 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt @@ -529,16 +529,17 @@ class RoomDetailViewModel @AssistedInject constructor( return@withState false } when (itemId) { - R.id.clear_message_queue -> + R.id.clear_message_queue -> // For now always disable when not in developer mode, worker cancellation is not working properly timeline.pendingEventCount() > 0 && vectorPreferences.developerMode() - R.id.resend_all -> state.asyncRoomSummary()?.hasFailedSending == true - R.id.clear_all -> state.asyncRoomSummary()?.hasFailedSending == true - R.id.open_matrix_apps, R.id.search -> true + R.id.resend_all -> state.asyncRoomSummary()?.hasFailedSending == true + R.id.clear_all -> state.asyncRoomSummary()?.hasFailedSending == true + R.id.open_matrix_apps -> true R.id.voice_call, - R.id.video_call -> true // always show for discoverability - R.id.hangup_call -> webRtcPeerConnectionManager.currentCall != null - else -> false + R.id.video_call -> true // always show for discoverability + R.id.hangup_call -> webRtcPeerConnectionManager.currentCall != null + R.id.search -> true + else -> false } } From 88ca909689e018bb2ad3098378912afa8c6a9109 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Tue, 29 Sep 2020 12:37:44 +0300 Subject: [PATCH 26/36] Remove roomId from SearchAction. --- .../app/features/home/room/detail/search/SearchAction.kt | 2 +- .../app/features/home/room/detail/search/SearchFragment.kt | 2 +- .../app/features/home/room/detail/search/SearchViewModel.kt | 2 +- .../app/features/home/room/detail/search/SearchViewState.kt | 5 ++++- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchAction.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchAction.kt index d295784a8c..4a2c84e258 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchAction.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchAction.kt @@ -19,7 +19,7 @@ package im.vector.app.features.home.room.detail.search import im.vector.app.core.platform.VectorViewModelAction sealed class SearchAction : VectorViewModelAction { - data class SearchWith(val roomId: String, val searchTerm: String) : SearchAction() + data class SearchWith(val searchTerm: String) : SearchAction() object ScrolledToTop : SearchAction() object Retry : SearchAction() } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt index fa60d33e64..360a6b6fcc 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt @@ -123,7 +123,7 @@ class SearchFragment @Inject constructor( fun search(query: String) { view?.hideKeyboard() - searchViewModel.handle(SearchAction.SearchWith(fragmentArgs.roomId, query)) + searchViewModel.handle(SearchAction.SearchWith(query)) } override fun onRetryClicked() { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt index 67befc80be..5747e83cad 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt @@ -57,7 +57,7 @@ class SearchViewModel @AssistedInject constructor( private fun handleSearchWith(action: SearchAction.SearchWith) { if (action.searchTerm.length > 1) { setState { - copy(searchTerm = action.searchTerm, roomId = action.roomId, isNextBatch = false) + copy(searchTerm = action.searchTerm, isNextBatch = false) } startSearching() diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewState.kt index 02db098e68..8c2eb82ad1 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewState.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewState.kt @@ -27,4 +27,7 @@ data class SearchViewState( val searchTerm: String? = null, val roomId: String? = null, val isNextBatch: Boolean = false -) : MvRxState +) : MvRxState { + + constructor(args: SearchArgs) : this(roomId = args.roomId) +} From 0d16fe019efd8c1997951ea3a9d6a05f8bf02c68 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Tue, 29 Sep 2020 13:00:02 +0300 Subject: [PATCH 27/36] Do not use string provider in fragment. --- .../app/features/home/room/detail/search/SearchFragment.kt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt index 360a6b6fcc..1c355bfbfe 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt @@ -33,7 +33,6 @@ import im.vector.app.core.extensions.hideKeyboard import im.vector.app.core.extensions.trackItemsVisibilityChange import im.vector.app.core.platform.StateView import im.vector.app.core.platform.VectorBaseFragment -import im.vector.app.core.resources.StringProvider import kotlinx.android.parcel.Parcelize import kotlinx.android.synthetic.main.fragment_search.* import org.matrix.android.sdk.api.session.events.model.Event @@ -46,8 +45,7 @@ data class SearchArgs( class SearchFragment @Inject constructor( val viewModelFactory: SearchViewModel.Factory, - val controller: SearchResultController, - val stringProvider: StringProvider + val controller: SearchResultController ) : VectorBaseFragment(), StateView.EventCallback, SearchResultController.Listener { private val fragmentArgs: SearchArgs by args() @@ -115,7 +113,7 @@ class SearchFragment @Inject constructor( pendingScrollToPosition = scrollPosition } else { stateView.state = StateView.State.Empty( - title = stringProvider.getString(R.string.search_no_results), + title = getString(R.string.search_no_results), image = ContextCompat.getDrawable(requireContext(), R.drawable.ic_search_no_results) ) } From 5d190a81379f7631de18b328c18448fe7f114be0 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Tue, 29 Sep 2020 20:38:58 +0300 Subject: [PATCH 28/36] Use loading item instead of full screen loading. --- .../home/room/detail/search/SearchAction.kt | 2 +- .../home/room/detail/search/SearchFragment.kt | 61 ++++++------ .../detail/search/SearchResultController.kt | 17 ++++ .../room/detail/search/SearchViewEvents.kt | 1 - .../room/detail/search/SearchViewModel.kt | 96 +++++++++++-------- .../room/detail/search/SearchViewState.kt | 5 +- .../src/main/res/layout/fragment_search.xml | 3 +- 7 files changed, 104 insertions(+), 81 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchAction.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchAction.kt index 4a2c84e258..36d22f1914 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchAction.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchAction.kt @@ -20,6 +20,6 @@ import im.vector.app.core.platform.VectorViewModelAction sealed class SearchAction : VectorViewModelAction { data class SearchWith(val searchTerm: String) : SearchAction() - object ScrolledToTop : SearchAction() + object LoadMore : SearchAction() object Retry : SearchAction() } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt index 1c355bfbfe..5f943c6031 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt @@ -21,14 +21,15 @@ import android.os.Parcelable import android.view.View import androidx.core.content.ContextCompat import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView +import com.airbnb.mvrx.Fail +import com.airbnb.mvrx.Loading +import com.airbnb.mvrx.Success import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import im.vector.app.R import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.configureWith -import im.vector.app.core.extensions.exhaustive import im.vector.app.core.extensions.hideKeyboard import im.vector.app.core.extensions.trackItemsVisibilityChange import im.vector.app.core.platform.StateView @@ -62,39 +63,19 @@ class SearchFragment @Inject constructor( stateView.eventCallback = this configureRecyclerView() - - searchViewModel.observeViewEvents { - when (it) { - is SearchViewEvents.Failure -> { - stateView.state = StateView.State.Error(errorFormatter.toHumanReadable(it.throwable)) - } - is SearchViewEvents.Loading -> { - stateView.state = StateView.State.Loading - } - }.exhaustive - } } private fun configureRecyclerView() { searchResultRecycler.trackItemsVisibilityChange() searchResultRecycler.configureWith(controller, showDivider = false) + (searchResultRecycler.layoutManager as? LinearLayoutManager)?.stackFromEnd = true controller.listener = this controller.addModelBuildListener { pendingScrollToPosition?.let { - searchResultRecycler.scrollToPosition(it) + searchResultRecycler.smoothScrollToPosition(it) } } - - searchResultRecycler.addOnScrollListener(object : RecyclerView.OnScrollListener() { - override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { - // Load next batch when scrolled to the top - if (newState == RecyclerView.SCROLL_STATE_IDLE - && (searchResultRecycler.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition() == 0) { - searchViewModel.handle(SearchAction.ScrolledToTop) - } - } - }) } override fun onDestroy() { @@ -104,18 +85,26 @@ class SearchFragment @Inject constructor( } override fun invalidate() = withState(searchViewModel) { state -> - if (state.searchResult?.results?.isNotEmpty() == true) { + if (state.searchResult?.results.isNullOrEmpty()) { + when (state.asyncEventsRequest) { + is Loading -> { + stateView.state = StateView.State.Loading + } + is Fail -> { + stateView.state = StateView.State.Error(errorFormatter.toHumanReadable(state.asyncEventsRequest.error)) + } + is Success -> { + stateView.state = StateView.State.Empty( + title = getString(R.string.search_no_results), + image = ContextCompat.getDrawable(requireContext(), R.drawable.ic_search_no_results)) + } + } + } else { + val lastBatchSize = state.lastBatch?.results?.size ?: 0 + pendingScrollToPosition = if (lastBatchSize > 0) lastBatchSize - 1 else 0 + stateView.state = StateView.State.Content controller.setData(state) - - val lastBatchSize = state.lastBatch?.results?.size ?: 0 - val scrollPosition = if (lastBatchSize > 0) lastBatchSize - 1 else 0 - pendingScrollToPosition = scrollPosition - } else { - stateView.state = StateView.State.Empty( - title = getString(R.string.search_no_results), - image = ContextCompat.getDrawable(requireContext(), R.drawable.ic_search_no_results) - ) } } @@ -133,4 +122,8 @@ class SearchFragment @Inject constructor( navigator.openRoom(requireContext(), event.roomId!!, event.eventId) } + + override fun loadMore() { + searchViewModel.handle(SearchAction.LoadMore) + } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultController.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultController.kt index 82d3120311..39e197e54f 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultController.kt @@ -17,8 +17,10 @@ package im.vector.app.features.home.room.detail.search import com.airbnb.epoxy.TypedEpoxyController +import com.airbnb.epoxy.VisibilityState import im.vector.app.core.date.DateFormatKind import im.vector.app.core.date.VectorDateFormatter +import im.vector.app.core.epoxy.loadingItem import im.vector.app.core.ui.list.genericItemHeader import im.vector.app.features.home.AvatarRenderer import org.matrix.android.sdk.api.session.Session @@ -34,8 +36,11 @@ class SearchResultController @Inject constructor( var listener: Listener? = null + private var idx = 0 + interface Listener { fun onItemClicked(event: Event) + fun loadMore() } init { @@ -45,6 +50,18 @@ class SearchResultController @Inject constructor( override fun buildModels(data: SearchViewState?) { data?.searchResult?.results ?: return + if (!data.searchResult.nextBatch.isNullOrEmpty()) { + loadingItem { + // Always use a different id, because we can be notified several times of visibility state changed + id("loadMore${idx++}") + onVisibilityStateChanged { _, _, visibilityState -> + if (visibilityState == VisibilityState.VISIBLE) { + listener?.loadMore() + } + } + } + } + buildSearchResultItems(data.searchResult.results!!) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewEvents.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewEvents.kt index 41dabd8686..6f07cb765c 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewEvents.kt @@ -20,5 +20,4 @@ import im.vector.app.core.platform.VectorViewEvents sealed class SearchViewEvents : VectorViewEvents { data class Failure(val throwable: Throwable) : SearchViewEvents() - data class Loading(val message: CharSequence? = null) : SearchViewEvents() } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt index 5747e83cad..9786560d19 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt @@ -16,16 +16,21 @@ package im.vector.app.features.home.room.detail.search +import androidx.lifecycle.viewModelScope +import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext +import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.Success import com.airbnb.mvrx.ViewModelContext import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel -import org.matrix.android.sdk.api.MatrixCallback +import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.search.SearchResult +import org.matrix.android.sdk.internal.util.awaitCallback class SearchViewModel @AssistedInject constructor( @Assisted private val initialState: SearchViewState, @@ -48,26 +53,22 @@ class SearchViewModel @AssistedInject constructor( override fun handle(action: SearchAction) { when (action) { - is SearchAction.SearchWith -> handleSearchWith(action) - is SearchAction.ScrolledToTop -> handleScrolledToTop() - is SearchAction.Retry -> handleRetry() + is SearchAction.SearchWith -> handleSearchWith(action) + is SearchAction.LoadMore -> handleLoadMore() + is SearchAction.Retry -> handleRetry() }.exhaustive } private fun handleSearchWith(action: SearchAction.SearchWith) { if (action.searchTerm.length > 1) { setState { - copy(searchTerm = action.searchTerm, isNextBatch = false) + copy(searchTerm = action.searchTerm) } - startSearching() } } - private fun handleScrolledToTop() { - setState { - copy(isNextBatch = true) - } + private fun handleLoadMore() { startSearching(true) } @@ -75,44 +76,51 @@ class SearchViewModel @AssistedInject constructor( startSearching() } - private fun startSearching(scrolledToTop: Boolean = false) = withState { state -> + private fun startSearching(isNextBatch: Boolean = false) = withState { state -> if (state.roomId == null || state.searchTerm == null) return@withState // There is no batch to retrieve - if (scrolledToTop && state.searchResult?.nextBatch == null) return@withState + if (isNextBatch && state.searchResult?.nextBatch == null) return@withState - _viewEvents.post(SearchViewEvents.Loading()) - - session - .getRoom(state.roomId) - ?.search( - searchTerm = state.searchTerm, - nextBatch = state.searchResult?.nextBatch, - orderByRecent = true, - beforeLimit = 0, - afterLimit = 0, - includeProfile = true, - limit = 20, - callback = object : MatrixCallback { - override fun onFailure(failure: Throwable) { - onSearchFailure(failure) - } - - override fun onSuccess(data: SearchResult) { - onSearchResultSuccess(data) - } - } + // Show full screen loading just for the clean search + if (!isNextBatch) { + setState { + copy( + asyncEventsRequest = Loading() ) - } - - private fun onSearchFailure(failure: Throwable) { - setState { - copy(searchResult = null) + } + } + + viewModelScope.launch { + try { + val result = awaitCallback { + session + .getRoom(state.roomId) + ?.search( + searchTerm = state.searchTerm, + nextBatch = state.searchResult?.nextBatch, + orderByRecent = true, + beforeLimit = 0, + afterLimit = 0, + includeProfile = true, + limit = 20, + callback = it + ) + } + onSearchResultSuccess(result, isNextBatch) + } catch (failure: Throwable) { + _viewEvents.post(SearchViewEvents.Failure(failure)) + setState { + copy( + asyncEventsRequest = Fail(failure), + searchResult = null + ) + } + } } - _viewEvents.post(SearchViewEvents.Failure(failure)) } - private fun onSearchResultSuccess(searchResult: SearchResult) = withState { state -> + private fun onSearchResultSuccess(searchResult: SearchResult, isNextBatch: Boolean) = withState { state -> val accumulatedResult = SearchResult( nextBatch = searchResult.nextBatch, results = searchResult.results, @@ -120,7 +128,7 @@ class SearchViewModel @AssistedInject constructor( ) // Accumulate results if it is the next batch - if (state.isNextBatch) { + if (isNextBatch) { if (state.searchResult != null) { accumulatedResult.results = accumulatedResult.results?.plus(state.searchResult.results!!) } @@ -130,7 +138,11 @@ class SearchViewModel @AssistedInject constructor( } setState { - copy(searchResult = accumulatedResult, lastBatch = searchResult) + copy( + searchResult = accumulatedResult, + lastBatch = searchResult, + asyncEventsRequest = Success(Unit) + ) } } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewState.kt index 8c2eb82ad1..3873769c44 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewState.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewState.kt @@ -16,7 +16,9 @@ package im.vector.app.features.home.room.detail.search +import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.Uninitialized import org.matrix.android.sdk.api.session.search.SearchResult data class SearchViewState( @@ -26,7 +28,8 @@ data class SearchViewState( val lastBatch: SearchResult? = null, val searchTerm: String? = null, val roomId: String? = null, - val isNextBatch: Boolean = false + // Current pagination request + val asyncEventsRequest: Async = Uninitialized ) : MvRxState { constructor(args: SearchArgs) : this(roomId = args.roomId) diff --git a/vector/src/main/res/layout/fragment_search.xml b/vector/src/main/res/layout/fragment_search.xml index 5478e1f68c..757168850b 100644 --- a/vector/src/main/res/layout/fragment_search.xml +++ b/vector/src/main/res/layout/fragment_search.xml @@ -2,8 +2,7 @@ + android:layout_height="match_parent"> Date: Tue, 29 Sep 2020 20:46:47 +0300 Subject: [PATCH 29/36] Do not use hard wrapping. --- .../app/features/home/room/detail/search/SearchFragment.kt | 6 +++--- .../home/room/detail/search/SearchResultController.kt | 6 ++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt index 5f943c6031..9f024f50f3 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt @@ -118,9 +118,9 @@ class SearchFragment @Inject constructor( } override fun onItemClicked(event: Event) { - event.roomId ?: return - - navigator.openRoom(requireContext(), event.roomId!!, event.eventId) + event.roomId?.let { + navigator.openRoom(requireContext(), it, event.eventId) + } } override fun loadMore() { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultController.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultController.kt index 39e197e54f..cf79ed0ea6 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultController.kt @@ -48,9 +48,7 @@ class SearchResultController @Inject constructor( } override fun buildModels(data: SearchViewState?) { - data?.searchResult?.results ?: return - - if (!data.searchResult.nextBatch.isNullOrEmpty()) { + if (!data?.searchResult?.nextBatch.isNullOrEmpty()) { loadingItem { // Always use a different id, because we can be notified several times of visibility state changed id("loadMore${idx++}") @@ -62,7 +60,7 @@ class SearchResultController @Inject constructor( } } - buildSearchResultItems(data.searchResult.results!!) + buildSearchResultItems(data?.searchResult?.results.orEmpty()) } private fun buildSearchResultItems(events: List) { From d4a0659483e8ee12ada1616639be5d52df0e69e5 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Wed, 30 Sep 2020 10:58:32 +0300 Subject: [PATCH 30/36] Init room in constructor. --- .../room/detail/search/SearchViewModel.kt | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt index 9786560d19..62fb9c8ae9 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt @@ -29,6 +29,7 @@ import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.room.Room import org.matrix.android.sdk.api.session.search.SearchResult import org.matrix.android.sdk.internal.util.awaitCallback @@ -37,6 +38,12 @@ class SearchViewModel @AssistedInject constructor( private val session: Session ) : VectorViewModel(initialState) { + private var room: Room? = null + + init { + room = initialState.roomId?.let { session.getRoom(it) } + } + @AssistedInject.Factory interface Factory { fun create(initialState: SearchViewState): SearchViewModel @@ -94,18 +101,16 @@ class SearchViewModel @AssistedInject constructor( viewModelScope.launch { try { val result = awaitCallback { - session - .getRoom(state.roomId) - ?.search( - searchTerm = state.searchTerm, - nextBatch = state.searchResult?.nextBatch, - orderByRecent = true, - beforeLimit = 0, - afterLimit = 0, - includeProfile = true, - limit = 20, - callback = it - ) + room?.search( + searchTerm = state.searchTerm, + nextBatch = state.searchResult?.nextBatch, + orderByRecent = true, + beforeLimit = 0, + afterLimit = 0, + includeProfile = true, + limit = 20, + callback = it + ) } onSearchResultSuccess(result, isNextBatch) } catch (failure: Throwable) { From 0838cbaf03d9e075261e449601a7940f608d62a4 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Wed, 30 Sep 2020 11:07:09 +0300 Subject: [PATCH 31/36] UI fixes. --- vector/src/main/res/layout/item_search_result.xml | 4 +++- vector/src/main/res/menu/menu_timeline.xml | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/vector/src/main/res/layout/item_search_result.xml b/vector/src/main/res/layout/item_search_result.xml index 9946a2fb50..73f949f777 100644 --- a/vector/src/main/res/layout/item_search_result.xml +++ b/vector/src/main/res/layout/item_search_result.xml @@ -49,9 +49,11 @@ + app:showAsAction="never" /> Date: Wed, 30 Sep 2020 11:45:46 +0300 Subject: [PATCH 32/36] Cancel previous search task before starting new one. --- .../home/room/detail/search/SearchViewModel.kt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt index 62fb9c8ae9..e9cca86e30 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt @@ -28,9 +28,11 @@ import com.squareup.inject.assisted.AssistedInject import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.room.Room import org.matrix.android.sdk.api.session.search.SearchResult +import org.matrix.android.sdk.api.util.Cancelable import org.matrix.android.sdk.internal.util.awaitCallback class SearchViewModel @AssistedInject constructor( @@ -39,6 +41,7 @@ class SearchViewModel @AssistedInject constructor( ) : VectorViewModel(initialState) { private var room: Room? = null + private var currentTask: Cancelable? = null init { room = initialState.roomId?.let { session.getRoom(it) } @@ -98,10 +101,14 @@ class SearchViewModel @AssistedInject constructor( } } + if (state.asyncEventsRequest is Loading) { + currentTask?.cancel() + } + viewModelScope.launch { try { val result = awaitCallback { - room?.search( + currentTask = room?.search( searchTerm = state.searchTerm, nextBatch = state.searchResult?.nextBatch, orderByRecent = true, @@ -114,6 +121,8 @@ class SearchViewModel @AssistedInject constructor( } onSearchResultSuccess(result, isNextBatch) } catch (failure: Throwable) { + if (failure is Failure.Cancelled) return@launch + _viewEvents.post(SearchViewEvents.Failure(failure)) setState { copy( From 6e814678081afc535c8eb32fbf4f0bd31ba24ad0 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Wed, 30 Sep 2020 12:39:42 +0300 Subject: [PATCH 33/36] If SearchActivity is in the stack, use it. --- .../app/features/home/room/detail/search/SearchActivity.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchActivity.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchActivity.kt index 70205eed62..24069a37c3 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchActivity.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchActivity.kt @@ -70,6 +70,7 @@ class SearchActivity : VectorBaseActivity() { fun newIntent(context: Context, args: SearchArgs): Intent { return Intent(context, SearchActivity::class.java).apply { + flags = Intent.FLAG_ACTIVITY_REORDER_TO_FRONT putExtra(MvRx.KEY_ARG, args) } } From ae346646e4f0fda00eb0810d8f30bd6c0437cc2c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 1 Oct 2020 15:05:42 +0200 Subject: [PATCH 34/36] Cleanup --- .../roomprofile/uploads/RoomUploadsFragment.kt | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsFragment.kt index b3f5047df4..876b585a59 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsFragment.kt @@ -23,23 +23,21 @@ import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import com.google.android.material.tabs.TabLayoutMediator -import org.matrix.android.sdk.api.util.toMatrixItem import im.vector.app.R import im.vector.app.core.extensions.exhaustive import im.vector.app.core.intent.getMimeTypeFromUri import im.vector.app.core.platform.VectorBaseFragment -import im.vector.app.core.resources.StringProvider import im.vector.app.core.utils.saveMedia import im.vector.app.core.utils.shareMedia import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.notifications.NotificationUtils import im.vector.app.features.roomprofile.RoomProfileArgs import kotlinx.android.synthetic.main.fragment_room_uploads.* +import org.matrix.android.sdk.api.util.toMatrixItem import javax.inject.Inject class RoomUploadsFragment @Inject constructor( private val viewModelFactory: RoomUploadsViewModel.Factory, - private val stringProvider: StringProvider, private val avatarRenderer: AvatarRenderer, private val notificationUtils: NotificationUtils ) : VectorBaseFragment(), RoomUploadsViewModel.Factory by viewModelFactory { @@ -58,8 +56,8 @@ class RoomUploadsFragment @Inject constructor( TabLayoutMediator(roomUploadsTabs, roomUploadsViewPager) { tab, position -> when (position) { - 0 -> tab.text = stringProvider.getString(R.string.uploads_media_title) - 1 -> tab.text = stringProvider.getString(R.string.uploads_files_title) + 0 -> tab.text = getString(R.string.uploads_media_title) + 1 -> tab.text = getString(R.string.uploads_files_title) } }.attach() @@ -70,7 +68,7 @@ class RoomUploadsFragment @Inject constructor( is RoomUploadsViewEvents.FileReadyForSharing -> { shareMedia(requireContext(), it.file, getMimeTypeFromUri(requireContext(), it.file.toUri())) } - is RoomUploadsViewEvents.FileReadyForSaving -> { + is RoomUploadsViewEvents.FileReadyForSaving -> { saveMedia( context = requireContext(), file = it.file, @@ -79,7 +77,7 @@ class RoomUploadsFragment @Inject constructor( notificationUtils = notificationUtils ) } - is RoomUploadsViewEvents.Failure -> showFailure(it.throwable) + is RoomUploadsViewEvents.Failure -> showFailure(it.throwable) }.exhaustive } } From 4649b2ac1daad64e04c2c1a290f7c3e521653297 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 1 Oct 2020 16:09:06 +0200 Subject: [PATCH 35/36] Code review --- .../sdk/api/session/search/SearchResult.kt | 17 ++++-- .../sdk/internal/session/DefaultSession.kt | 4 +- .../sdk/internal/session/search/SearchAPI.kt | 6 +- .../sdk/internal/session/search/SearchTask.kt | 4 +- .../search/request/SearchRequestBody.kt | 3 + .../search/request/SearchRequestCategories.kt | 4 +- .../search/request/SearchRequestRoomEvents.kt | 37 +++++++++++-- .../session/search/response/SearchResponse.kt | 6 +- .../response/SearchResponseCategories.kt | 5 +- .../search/response/SearchResponseItem.kt | 19 +++++-- .../response/SearchResponseRoomEvents.kt | 2 +- .../home/room/detail/search/SearchFragment.kt | 10 ++-- .../detail/search/SearchResultController.kt | 13 ++--- .../room/detail/search/SearchResultItem.kt | 24 ++++---- .../room/detail/search/SearchViewModel.kt | 55 ++++++++----------- .../room/detail/search/SearchViewState.kt | 11 ++-- .../src/main/res/layout/activity_search.xml | 4 +- .../src/main/res/layout/fragment_search.xml | 4 +- 18 files changed, 138 insertions(+), 90 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/search/SearchResult.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/search/SearchResult.kt index 4529bf6d13..59d1c4d46d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/search/SearchResult.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/search/SearchResult.kt @@ -23,10 +23,17 @@ import org.matrix.android.sdk.api.session.events.model.Event * Domain class to represent the response of a search request in a room. */ data class SearchResult( - // Token that can be used to get the next batch of results, by passing as the next_batch parameter to the next call. If this field is absent, there are no more results. + /** + * Token that can be used to get the next batch of results, by passing as the next_batch parameter to the next call. + * If this field is null, there are no more results. + */ val nextBatch: String? = null, - // List of words which should be highlighted, useful for stemming which may change the query terms. - var highlights: List? = null, - // List of results in the requested order. - var results: List? = null + /** + * List of words which should be highlighted, useful for stemming which may change the query terms. + */ + val highlights: List? = null, + /** + * List of results in the requested order. + */ + val results: List? = null ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt index 34b6be225b..d1c0844caa 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt @@ -96,6 +96,7 @@ internal class DefaultSession @Inject constructor( private val pushRuleService: Lazy, private val pushersService: Lazy, private val termsService: Lazy, + private val searchService: Lazy, private val cryptoService: Lazy, private val defaultFileService: Lazy, private val permalinkService: Lazy, @@ -121,8 +122,7 @@ internal class DefaultSession @Inject constructor( private val taskExecutor: TaskExecutor, private val callSignalingService: Lazy, @UnauthenticatedWithCertificate - private val unauthenticatedWithCertificateOkHttpClient: Lazy, - private val searchService: Lazy + private val unauthenticatedWithCertificateOkHttpClient: Lazy ) : Session, RoomService by roomService.get(), RoomDirectoryService by roomDirectoryService.get(), diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchAPI.kt index 3d5dbc306f..a903cd8cc8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchAPI.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchAPI.kt @@ -28,9 +28,9 @@ internal interface SearchAPI { /** * Performs a full text search across different categories. + * Ref: https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-search */ @POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "search") - fun search( - @Query("next_batch") nextBatch: String?, - @Body body: SearchRequestBody): Call + fun search(@Query("next_batch") nextBatch: String?, + @Body body: SearchRequestBody): Call } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchTask.kt index a2cad6bb2f..3bb65fb8da 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchTask.kt @@ -69,14 +69,14 @@ internal class DefaultSearchTask @Inject constructor( ) ) apiCall = searchAPI.search(params.nextBatch, searchRequestBody) - }.toDomain().apply { results = results?.reversed() } + }.toDomain() } private fun SearchResponse.toDomain(): SearchResult { return SearchResult( nextBatch = searchCategories.roomEvents?.nextBatch, highlights = searchCategories.roomEvents?.highlights, - results = searchCategories.roomEvents?.results?.map { it.event } + results = searchCategories.roomEvents?.results?.map { it.event }?.reversed() ) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/request/SearchRequestBody.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/request/SearchRequestBody.kt index 000f89e751..7cf86457d6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/request/SearchRequestBody.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/request/SearchRequestBody.kt @@ -22,6 +22,9 @@ import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) internal data class SearchRequestBody( + /** + * Required. Describes which categories to search in and their criteria. + */ @Json(name = "search_categories") val searchCategories: SearchRequestCategories ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/request/SearchRequestCategories.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/request/SearchRequestCategories.kt index 606b7320e5..9d87114237 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/request/SearchRequestCategories.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/request/SearchRequestCategories.kt @@ -22,7 +22,9 @@ import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) internal data class SearchRequestCategories( - // Mapping of category name to search criteria. + /** + * Mapping of category name to search criteria. + */ @Json(name = "room_events") val roomEvents: SearchRequestRoomEvents? = null ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/request/SearchRequestRoomEvents.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/request/SearchRequestRoomEvents.kt index fef1baa990..c5234b1052 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/request/SearchRequestRoomEvents.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/request/SearchRequestRoomEvents.kt @@ -22,15 +22,44 @@ import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) internal data class SearchRequestRoomEvents( - // Required. The string to search events for. + /** + * Required. The string to search events for. + */ @Json(name = "search_term") val searchTerm: String, + + /** + * The keys to search. Defaults to all. One of: ["content.body", "content.name", "content.topic"] + */ + @Json(name = "keys") + val keys: Any? = null, + + /** + * This takes a filter. + */ @Json(name = "filter") val filter: SearchRequestFilter? = null, - // By default, this is "rank". One of: ["recent", "rank"] + + /** + * The order in which to search for results. By default, this is "rank". One of: ["recent", "rank"] + */ @Json(name = "order_by") val orderBy: SearchRequestOrder? = null, - // Configures whether any context for the events returned are included in the response. + + /** + * Configures whether any context for the events returned are included in the response. + */ @Json(name = "event_context") - val eventContext: SearchRequestEventContext? = null + val eventContext: SearchRequestEventContext? = null, + + /** + * Requests the server return the current state for each room returned. + */ + @Json(name = "include_state") + val include_state: Boolean? = null + + /** + * Requests that the server partitions the result set based on the provided list of keys. + */ + // val groupings: SearchRequestGroupings? = null ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/response/SearchResponse.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/response/SearchResponse.kt index b7debb6b1e..8e11ba1468 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/response/SearchResponse.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/response/SearchResponse.kt @@ -21,8 +21,10 @@ import com.squareup.moshi.Json import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) -data class SearchResponse( - // Required. Describes which categories to search in and their criteria. +internal data class SearchResponse( + /** + * Required. Describes which categories to search in and their criteria. + */ @Json(name = "search_categories") val searchCategories: SearchResponseCategories ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/response/SearchResponseCategories.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/response/SearchResponseCategories.kt index c931b23826..8e46502e27 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/response/SearchResponseCategories.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/response/SearchResponseCategories.kt @@ -21,7 +21,10 @@ import com.squareup.moshi.Json import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) -data class SearchResponseCategories( +internal data class SearchResponseCategories( + /** + * Mapping of category name to search criteria. + */ @Json(name = "room_events") val roomEvents: SearchResponseRoomEvents? = null ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/response/SearchResponseItem.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/response/SearchResponseItem.kt index 7c675f0593..5b0806ce85 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/response/SearchResponseItem.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/response/SearchResponseItem.kt @@ -22,11 +22,22 @@ import com.squareup.moshi.JsonClass import org.matrix.android.sdk.api.session.events.model.Event @JsonClass(generateAdapter = true) -data class SearchResponseItem( - // A number that describes how closely this result matches the search. Higher is closer. +internal data class SearchResponseItem( + /** + * A number that describes how closely this result matches the search. Higher is closer. + */ @Json(name = "rank") val rank: Double? = null, - // The event that matched. + + /** + * The event that matched. + */ @Json(name = "result") - val event: Event + val event: Event, + + /** + * Context for result, if requested. + */ + @Json(name = "context") + val context: SearchResponseEventContext? = null ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/response/SearchResponseRoomEvents.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/response/SearchResponseRoomEvents.kt index 61ed2bf3cf..1078b9a9f9 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/response/SearchResponseRoomEvents.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/response/SearchResponseRoomEvents.kt @@ -21,7 +21,7 @@ import com.squareup.moshi.Json import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) -class SearchResponseRoomEvents( +internal data class SearchResponseRoomEvents( // List of results in the requested order. @Json(name = "results") val results: List? = null, diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt index 9f024f50f3..ea7ba8f464 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt @@ -46,7 +46,7 @@ data class SearchArgs( class SearchFragment @Inject constructor( val viewModelFactory: SearchViewModel.Factory, - val controller: SearchResultController + private val controller: SearchResultController ) : VectorBaseFragment(), StateView.EventCallback, SearchResultController.Listener { private val fragmentArgs: SearchArgs by args() @@ -85,13 +85,13 @@ class SearchFragment @Inject constructor( } override fun invalidate() = withState(searchViewModel) { state -> - if (state.searchResult?.results.isNullOrEmpty()) { - when (state.asyncEventsRequest) { + if (state.searchResult.isNullOrEmpty()) { + when (state.asyncSearchRequest) { is Loading -> { stateView.state = StateView.State.Loading } is Fail -> { - stateView.state = StateView.State.Error(errorFormatter.toHumanReadable(state.asyncEventsRequest.error)) + stateView.state = StateView.State.Error(errorFormatter.toHumanReadable(state.asyncSearchRequest.error)) } is Success -> { stateView.state = StateView.State.Empty( @@ -100,7 +100,7 @@ class SearchFragment @Inject constructor( } } } else { - val lastBatchSize = state.lastBatch?.results?.size ?: 0 + val lastBatchSize = state.lastBatch?.size ?: 0 pendingScrollToPosition = if (lastBatchSize > 0) lastBatchSize - 1 else 0 stateView.state = StateView.State.Content diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultController.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultController.kt index cf79ed0ea6..8daa4f60e4 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultController.kt @@ -48,7 +48,9 @@ class SearchResultController @Inject constructor( } override fun buildModels(data: SearchViewState?) { - if (!data?.searchResult?.nextBatch.isNullOrEmpty()) { + data ?: return + + if (data.hasMoreResult) { loadingItem { // Always use a different id, because we can be notified several times of visibility state changed id("loadMore${idx++}") @@ -60,7 +62,7 @@ class SearchResultController @Inject constructor( } } - buildSearchResultItems(data?.searchResult?.results.orEmpty()) + buildSearchResultItems(data.searchResult.orEmpty()) } private fun buildSearchResultItems(events: List) { @@ -83,12 +85,9 @@ class SearchResultController @Inject constructor( avatarRenderer(avatarRenderer) dateFormatter(dateFormatter) event(event) + // I think we should use the data returned by the server? sender(event.senderId?.let { session.getUser(it) }) - listener(object : SearchResultItem.Listener { - override fun onItemClicked() { - listener?.onItemClicked(event) - } - }) + listener { listener?.onItemClicked(event) } } } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultItem.kt index fb0121cbcf..2d569c1c6a 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultItem.kt @@ -23,8 +23,10 @@ import com.airbnb.epoxy.EpoxyModelClass import im.vector.app.R import im.vector.app.core.date.DateFormatKind import im.vector.app.core.date.VectorDateFormatter +import im.vector.app.core.epoxy.ClickListener import im.vector.app.core.epoxy.VectorEpoxyHolder import im.vector.app.core.epoxy.VectorEpoxyModel +import im.vector.app.core.epoxy.onClick import im.vector.app.features.home.AvatarRenderer import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.user.model.User @@ -33,21 +35,21 @@ import org.matrix.android.sdk.api.util.toMatrixItem @EpoxyModelClass(layout = R.layout.item_search_result) abstract class SearchResultItem : VectorEpoxyModel() { - @EpoxyAttribute var avatarRenderer: AvatarRenderer? = null + @EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer @EpoxyAttribute var dateFormatter: VectorDateFormatter? = null - @EpoxyAttribute var event: Event? = null + @EpoxyAttribute lateinit var event: Event @EpoxyAttribute var sender: User? = null - @EpoxyAttribute var listener: Listener? = null + @EpoxyAttribute var listener: ClickListener? = null override fun bind(holder: Holder) { super.bind(holder) - event ?: return - holder.view.setOnClickListener { listener?.onItemClicked() } - sender?.toMatrixItem()?.let { avatarRenderer?.render(it, holder.avatarImageView) } - holder.memberNameView.text = sender?.displayName - holder.timeView.text = dateFormatter?.format(event!!.originServerTs, DateFormatKind.MESSAGE_SIMPLE) - holder.contentView.text = event?.content?.get("body") as? String + holder.view.onClick(listener) + sender?.toMatrixItem()?.let { avatarRenderer.render(it, holder.avatarImageView) } + holder.memberNameView.text = sender?.getBestName() + holder.timeView.text = dateFormatter?.format(event.originServerTs, DateFormatKind.MESSAGE_SIMPLE) + // TODO Improve that (use formattedBody, etc.) + holder.contentView.text = event.content?.get("body") as? String } class Holder : VectorEpoxyHolder() { @@ -56,8 +58,4 @@ abstract class SearchResultItem : VectorEpoxyModel() { val timeView by bind(R.id.messageTimeView) val contentView by bind(R.id.messageContentView) } - - interface Listener { - fun onItemClicked() - } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt index e9cca86e30..6703815f57 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt @@ -37,15 +37,14 @@ import org.matrix.android.sdk.internal.util.awaitCallback class SearchViewModel @AssistedInject constructor( @Assisted private val initialState: SearchViewState, - private val session: Session + session: Session ) : VectorViewModel(initialState) { - private var room: Room? = null + private var room: Room? = session.getRoom(initialState.roomId) + private var currentTask: Cancelable? = null - init { - room = initialState.roomId?.let { session.getRoom(it) } - } + private var nextBatch: String? = null @AssistedInject.Factory interface Factory { @@ -64,8 +63,8 @@ class SearchViewModel @AssistedInject constructor( override fun handle(action: SearchAction) { when (action) { is SearchAction.SearchWith -> handleSearchWith(action) - is SearchAction.LoadMore -> handleLoadMore() - is SearchAction.Retry -> handleRetry() + is SearchAction.LoadMore -> handleLoadMore() + is SearchAction.Retry -> handleRetry() }.exhaustive } @@ -74,7 +73,7 @@ class SearchViewModel @AssistedInject constructor( setState { copy(searchTerm = action.searchTerm) } - startSearching() + startSearching(false) } } @@ -83,25 +82,25 @@ class SearchViewModel @AssistedInject constructor( } private fun handleRetry() { - startSearching() + startSearching(false) } - private fun startSearching(isNextBatch: Boolean = false) = withState { state -> - if (state.roomId == null || state.searchTerm == null) return@withState + private fun startSearching(isNextBatch: Boolean) = withState { state -> + if (state.searchTerm == null) return@withState // There is no batch to retrieve - if (isNextBatch && state.searchResult?.nextBatch == null) return@withState + if (isNextBatch && nextBatch == null) return@withState // Show full screen loading just for the clean search if (!isNextBatch) { setState { copy( - asyncEventsRequest = Loading() + asyncSearchRequest = Loading() ) } } - if (state.asyncEventsRequest is Loading) { + if (state.asyncSearchRequest is Loading) { currentTask?.cancel() } @@ -110,7 +109,7 @@ class SearchViewModel @AssistedInject constructor( val result = awaitCallback { currentTask = room?.search( searchTerm = state.searchTerm, - nextBatch = state.searchResult?.nextBatch, + nextBatch = nextBatch, orderByRecent = true, beforeLimit = 0, afterLimit = 0, @@ -126,7 +125,7 @@ class SearchViewModel @AssistedInject constructor( _viewEvents.post(SearchViewEvents.Failure(failure)) setState { copy( - asyncEventsRequest = Fail(failure), + asyncSearchRequest = Fail(failure), searchResult = null ) } @@ -135,27 +134,19 @@ class SearchViewModel @AssistedInject constructor( } private fun onSearchResultSuccess(searchResult: SearchResult, isNextBatch: Boolean) = withState { state -> - val accumulatedResult = SearchResult( - nextBatch = searchResult.nextBatch, - results = searchResult.results, - highlights = searchResult.highlights - ) - // Accumulate results if it is the next batch - if (isNextBatch) { - if (state.searchResult != null) { - accumulatedResult.results = accumulatedResult.results?.plus(state.searchResult.results!!) - } - if (state.searchResult?.highlights != null) { - accumulatedResult.highlights = accumulatedResult.highlights?.plus(state.searchResult.highlights!!) - } - } + val accumulatedResult = searchResult.results.orEmpty().plus(state.searchResult?.takeIf { isNextBatch }.orEmpty()) + + // Note: We do not care about the highlights for the moment, but it will be the same algorithm + + nextBatch = searchResult.nextBatch setState { copy( searchResult = accumulatedResult, - lastBatch = searchResult, - asyncEventsRequest = Success(Unit) + hasMoreResult = !nextBatch.isNullOrEmpty(), + lastBatch = searchResult.results, + asyncSearchRequest = Success(Unit) ) } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewState.kt index 3873769c44..72da1ca940 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewState.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewState.kt @@ -19,17 +19,18 @@ package im.vector.app.features.home.room.detail.search import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized -import org.matrix.android.sdk.api.session.search.SearchResult +import org.matrix.android.sdk.api.session.events.model.Event data class SearchViewState( // Accumulated search result - val searchResult: SearchResult? = null, + val searchResult: List? = null, + val hasMoreResult: Boolean = false, // Last batch result will help RecyclerView to position itself - val lastBatch: SearchResult? = null, + val lastBatch: List? = null, val searchTerm: String? = null, - val roomId: String? = null, + val roomId: String = "", // Current pagination request - val asyncEventsRequest: Async = Uninitialized + val asyncSearchRequest: Async = Uninitialized ) : MvRxState { constructor(args: SearchArgs) : this(roomId = args.roomId) diff --git a/vector/src/main/res/layout/activity_search.xml b/vector/src/main/res/layout/activity_search.xml index a3aa21eecc..2268bf932c 100644 --- a/vector/src/main/res/layout/activity_search.xml +++ b/vector/src/main/res/layout/activity_search.xml @@ -24,8 +24,8 @@ style="@style/VectorSearchView" android:layout_width="match_parent" android:layout_height="wrap_content" - app:queryHint="@string/search_hint" - android:backgroundTint="@color/base_color" /> + android:backgroundTint="@color/base_color" + app:queryHint="@string/search_hint" /> diff --git a/vector/src/main/res/layout/fragment_search.xml b/vector/src/main/res/layout/fragment_search.xml index 757168850b..330e70d86b 100644 --- a/vector/src/main/res/layout/fragment_search.xml +++ b/vector/src/main/res/layout/fragment_search.xml @@ -1,5 +1,6 @@ @@ -8,6 +9,7 @@ android:id="@+id/searchResultRecycler" android:layout_width="match_parent" android:layout_height="match_parent" - android:overScrollMode="always" /> + android:overScrollMode="always" + tools:listitem="@layout/item_search_result" /> \ No newline at end of file From 3705fa14bd863108e48b9e820f23ff00ad71cba8 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 1 Oct 2020 17:03:53 +0200 Subject: [PATCH 36/36] Use sender data return from search result --- .../android/sdk/api/session/room/Room.kt | 1 + .../sdk/api/session/search/SearchResult.kt | 8 ++++- .../sdk/api/session/search/SearchService.kt | 1 + .../sdk/internal/session/search/SearchTask.kt | 18 ++++++++++- .../response/SearchResponseEventContext.kt | 2 +- .../home/room/detail/search/SearchActivity.kt | 3 +- .../home/room/detail/search/SearchFragment.kt | 5 ++-- .../detail/search/SearchResultController.kt | 20 +++++++------ .../room/detail/search/SearchResultItem.kt | 10 +++---- .../room/detail/search/SearchViewModel.kt | 30 +++++++++++-------- .../room/detail/search/SearchViewState.kt | 8 ++--- 11 files changed, 69 insertions(+), 37 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/Room.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/Room.kt index 86029e5419..9afff5f59c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/Room.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/Room.kt @@ -83,6 +83,7 @@ interface Room : * @param beforeLimit how many events before the result are returned. * @param afterLimit how many events after the result are returned. * @param includeProfile requests that the server returns the historic profile information for the users that sent the events that were returned. + * @param callback Callback to get the search result */ fun search(searchTerm: String, nextBatch: String?, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/search/SearchResult.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/search/SearchResult.kt index 59d1c4d46d..e95d7fab19 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/search/SearchResult.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/search/SearchResult.kt @@ -18,6 +18,7 @@ package org.matrix.android.sdk.api.session.search import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.api.util.MatrixItem /** * Domain class to represent the response of a search request in a room. @@ -35,5 +36,10 @@ data class SearchResult( /** * List of results in the requested order. */ - val results: List? = null + val results: List? = null +) + +data class EventAndSender( + val event: Event, + val sender: MatrixItem.UserItem? ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/search/SearchService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/search/SearchService.kt index 0abbdfd958..a8c9e79ba9 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/search/SearchService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/search/SearchService.kt @@ -36,6 +36,7 @@ interface SearchService { * @param beforeLimit how many events before the result are returned. * @param afterLimit how many events after the result are returned. * @param includeProfile requests that the server returns the historic profile information for the users that sent the events that were returned. + * @param callback Callback to get the search result */ fun search(searchTerm: String, roomId: String, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchTask.kt index 3bb65fb8da..c90068e507 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchTask.kt @@ -18,7 +18,9 @@ package org.matrix.android.sdk.internal.session.search import org.greenrobot.eventbus.EventBus +import org.matrix.android.sdk.api.session.search.EventAndSender import org.matrix.android.sdk.api.session.search.SearchResult +import org.matrix.android.sdk.api.util.MatrixItem import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.session.search.request.SearchRequestBody import org.matrix.android.sdk.internal.session.search.request.SearchRequestCategories @@ -76,7 +78,21 @@ internal class DefaultSearchTask @Inject constructor( return SearchResult( nextBatch = searchCategories.roomEvents?.nextBatch, highlights = searchCategories.roomEvents?.highlights, - results = searchCategories.roomEvents?.results?.map { it.event }?.reversed() + results = searchCategories.roomEvents?.results?.map { searchResponseItem -> + EventAndSender( + searchResponseItem.event, + searchResponseItem.event.senderId?.let { senderId -> + searchResponseItem.context?.profileInfo?.get(senderId) + ?.let { + MatrixItem.UserItem( + senderId, + it["displayname"] as? String, + it["avatar_url"] as? String + ) + } + } + ) + }?.reversed() ) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/response/SearchResponseEventContext.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/response/SearchResponseEventContext.kt index 596645c355..f4d8699b2d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/response/SearchResponseEventContext.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/response/SearchResponseEventContext.kt @@ -38,5 +38,5 @@ internal data class SearchResponseEventContext( val end: String? = null, // The historic profile information of the users that sent the events returned. The string key is the user ID for which the profile belongs to. @Json(name = "profile_info") - val profileInfo: JsonDict? = null + val profileInfo: Map? = null ) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchActivity.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchActivity.kt index 24069a37c3..f85dccbb27 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchActivity.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchActivity.kt @@ -70,7 +70,8 @@ class SearchActivity : VectorBaseActivity() { fun newIntent(context: Context, args: SearchArgs): Intent { return Intent(context, SearchActivity::class.java).apply { - flags = Intent.FLAG_ACTIVITY_REORDER_TO_FRONT + // If we do that we will have the same room two times on the stack. Let's allow infinite stack for the moment. + // flags = Intent.FLAG_ACTIVITY_REORDER_TO_FRONT putExtra(MvRx.KEY_ARG, args) } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt index ea7ba8f464..666f5b3d38 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt @@ -90,7 +90,7 @@ class SearchFragment @Inject constructor( is Loading -> { stateView.state = StateView.State.Loading } - is Fail -> { + is Fail -> { stateView.state = StateView.State.Error(errorFormatter.toHumanReadable(state.asyncSearchRequest.error)) } is Success -> { @@ -100,8 +100,7 @@ class SearchFragment @Inject constructor( } } } else { - val lastBatchSize = state.lastBatch?.size ?: 0 - pendingScrollToPosition = if (lastBatchSize > 0) lastBatchSize - 1 else 0 + pendingScrollToPosition = (state.lastBatchSize - 1).coerceAtLeast(0) stateView.state = StateView.State.Content controller.setData(state) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultController.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultController.kt index 8daa4f60e4..c917c4557d 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultController.kt @@ -25,6 +25,8 @@ import im.vector.app.core.ui.list.genericItemHeader import im.vector.app.features.home.AvatarRenderer import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.api.session.search.EventAndSender +import org.matrix.android.sdk.api.util.toMatrixItem import java.util.Calendar import javax.inject.Inject @@ -62,15 +64,15 @@ class SearchResultController @Inject constructor( } } - buildSearchResultItems(data.searchResult.orEmpty()) + buildSearchResultItems(data.searchResult) } - private fun buildSearchResultItems(events: List) { + private fun buildSearchResultItems(events: List) { var lastDate: Calendar? = null - events.forEach { event -> + events.forEach { eventAndSender -> val eventDate = Calendar.getInstance().apply { - timeInMillis = event.originServerTs ?: System.currentTimeMillis() + timeInMillis = eventAndSender.event.originServerTs ?: System.currentTimeMillis() } if (lastDate?.get(Calendar.DAY_OF_YEAR) != eventDate.get(Calendar.DAY_OF_YEAR)) { genericItemHeader { @@ -81,13 +83,13 @@ class SearchResultController @Inject constructor( lastDate = eventDate searchResultItem { - id(event.eventId) + id(eventAndSender.event.eventId) avatarRenderer(avatarRenderer) dateFormatter(dateFormatter) - event(event) - // I think we should use the data returned by the server? - sender(event.senderId?.let { session.getUser(it) }) - listener { listener?.onItemClicked(event) } + event(eventAndSender.event) + sender(eventAndSender.sender + ?: eventAndSender.event.senderId?.let { session.getUser(it) }?.toMatrixItem()) + listener { listener?.onItemClicked(eventAndSender.event) } } } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultItem.kt index 2d569c1c6a..10407c64e0 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultItem.kt @@ -27,10 +27,10 @@ import im.vector.app.core.epoxy.ClickListener import im.vector.app.core.epoxy.VectorEpoxyHolder import im.vector.app.core.epoxy.VectorEpoxyModel import im.vector.app.core.epoxy.onClick +import im.vector.app.core.extensions.setTextOrHide import im.vector.app.features.home.AvatarRenderer import org.matrix.android.sdk.api.session.events.model.Event -import org.matrix.android.sdk.api.session.user.model.User -import org.matrix.android.sdk.api.util.toMatrixItem +import org.matrix.android.sdk.api.util.MatrixItem @EpoxyModelClass(layout = R.layout.item_search_result) abstract class SearchResultItem : VectorEpoxyModel() { @@ -38,15 +38,15 @@ abstract class SearchResultItem : VectorEpoxyModel() { @EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer @EpoxyAttribute var dateFormatter: VectorDateFormatter? = null @EpoxyAttribute lateinit var event: Event - @EpoxyAttribute var sender: User? = null + @EpoxyAttribute var sender: MatrixItem? = null @EpoxyAttribute var listener: ClickListener? = null override fun bind(holder: Holder) { super.bind(holder) holder.view.onClick(listener) - sender?.toMatrixItem()?.let { avatarRenderer.render(it, holder.avatarImageView) } - holder.memberNameView.text = sender?.getBestName() + sender?.let { avatarRenderer.render(it, holder.avatarImageView) } + holder.memberNameView.setTextOrHide(sender?.getBestName()) holder.timeView.text = dateFormatter?.format(event.originServerTs, DateFormatKind.MESSAGE_SIMPLE) // TODO Improve that (use formattedBody, etc.) holder.contentView.text = event.content?.get("body") as? String diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt index 6703815f57..21a2d18e71 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt @@ -69,9 +69,14 @@ class SearchViewModel @AssistedInject constructor( } private fun handleSearchWith(action: SearchAction.SearchWith) { - if (action.searchTerm.length > 1) { + if (action.searchTerm.isNotEmpty()) { setState { - copy(searchTerm = action.searchTerm) + copy( + searchResult = emptyList(), + hasMoreResult = false, + lastBatchSize = 0, + searchTerm = action.searchTerm + ) } startSearching(false) } @@ -100,9 +105,7 @@ class SearchViewModel @AssistedInject constructor( } } - if (state.asyncSearchRequest is Loading) { - currentTask?.cancel() - } + currentTask?.cancel() viewModelScope.launch { try { @@ -118,24 +121,22 @@ class SearchViewModel @AssistedInject constructor( callback = it ) } - onSearchResultSuccess(result, isNextBatch) + onSearchResultSuccess(result) } catch (failure: Throwable) { if (failure is Failure.Cancelled) return@launch _viewEvents.post(SearchViewEvents.Failure(failure)) setState { copy( - asyncSearchRequest = Fail(failure), - searchResult = null + asyncSearchRequest = Fail(failure) ) } } } } - private fun onSearchResultSuccess(searchResult: SearchResult, isNextBatch: Boolean) = withState { state -> - // Accumulate results if it is the next batch - val accumulatedResult = searchResult.results.orEmpty().plus(state.searchResult?.takeIf { isNextBatch }.orEmpty()) + private fun onSearchResultSuccess(searchResult: SearchResult) = withState { state -> + val accumulatedResult = searchResult.results.orEmpty().plus(state.searchResult) // Note: We do not care about the highlights for the moment, but it will be the same algorithm @@ -145,9 +146,14 @@ class SearchViewModel @AssistedInject constructor( copy( searchResult = accumulatedResult, hasMoreResult = !nextBatch.isNullOrEmpty(), - lastBatch = searchResult.results, + lastBatchSize = searchResult.results.orEmpty().size, asyncSearchRequest = Success(Unit) ) } } + + override fun onCleared() { + currentTask?.cancel() + super.onCleared() + } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewState.kt index 72da1ca940..9f700b6e31 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewState.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewState.kt @@ -19,14 +19,14 @@ package im.vector.app.features.home.room.detail.search import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized -import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.api.session.search.EventAndSender data class SearchViewState( // Accumulated search result - val searchResult: List? = null, + val searchResult: List = emptyList(), val hasMoreResult: Boolean = false, - // Last batch result will help RecyclerView to position itself - val lastBatch: List? = null, + // Last batch size, will help RecyclerView to position itself + val lastBatchSize: Int = 0, val searchTerm: String? = null, val roomId: String = "", // Current pagination request