diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 41ae4ce7f..2aa763474 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -172,6 +172,11 @@ android:name=".shareditems.activities.SharedItemsActivity" android:theme="@style/AppTheme"/> + + diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/MessageResultItem.kt b/app/src/main/java/com/nextcloud/talk/adapters/items/MessageResultItem.kt index 725ed5e1c..bc7e25d23 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/MessageResultItem.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/items/MessageResultItem.kt @@ -42,7 +42,7 @@ data class MessageResultItem constructor( private val context: Context, private val currentUser: UserEntity, val messageEntry: SearchMessageEntry, - private val showHeader: Boolean + private val showHeader: Boolean = false ) : AbstractFlexibleItem(), IFilterable, diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt index 5fc0aa2a2..b0c561588 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt @@ -130,6 +130,7 @@ import com.nextcloud.talk.events.UserMentionClickEvent import com.nextcloud.talk.events.WebSocketCommunicationEvent import com.nextcloud.talk.jobs.DownloadFileToCacheWorker import com.nextcloud.talk.jobs.UploadAndShareFilesWorker +import com.nextcloud.talk.messagesearch.MessageSearchActivity import com.nextcloud.talk.models.database.CapabilitiesUtil import com.nextcloud.talk.models.database.UserEntity import com.nextcloud.talk.models.json.chat.ChatMessage @@ -1346,6 +1347,7 @@ class ChatController(args: Bundle) : override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) { if (resultCode != RESULT_OK) { + // TODO for message search, CANCELED is fine Log.e(TAG, "resultCode for received intent was != ok") return } @@ -1452,6 +1454,8 @@ class ChatController(args: Bundle) : Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e) } } + } else if (requestCode == REQUEST_CODE_MESSAGE_SEARCH) { + TODO() } } @@ -2469,28 +2473,32 @@ class ChatController(args: Bundle) : } override fun onOptionsItemSelected(item: MenuItem): Boolean { - when (item.itemId) { + return when (item.itemId) { android.R.id.home -> { (activity as MainActivity).resetConversationsList() - return true + true } R.id.conversation_video_call -> { startACall(false, false) - return true + true } R.id.conversation_voice_call -> { startACall(true, false) - return true + true } R.id.conversation_info -> { showConversationInfoScreen() - return true + true } R.id.shared_items -> { showSharedItems() - return true + true } - else -> return super.onOptionsItemSelected(item) + R.id.conversation_search -> { + startMessageSearch() + true + } + else -> super.onOptionsItemSelected(item) } } @@ -2502,6 +2510,14 @@ class ChatController(args: Bundle) : activity!!.startActivity(intent) } + private fun startMessageSearch() { + val intent = Intent(activity, MessageSearchActivity::class.java) + intent.putExtra(KEY_CONVERSATION_NAME, currentConversation?.displayName) + intent.putExtra(KEY_ROOM_TOKEN, roomToken) + intent.putExtra(KEY_USER_ENTITY, conversationUser as Parcelable) + activity!!.startActivityForResult(intent, REQUEST_CODE_MESSAGE_SEARCH) + } + private fun handleSystemMessages(chatMessageList: List): List { val chatMessageMap = chatMessageList.map { it.id to it }.toMap().toMutableMap() val chatMessageIterator = chatMessageMap.iterator() @@ -3087,6 +3103,7 @@ class ChatController(args: Bundle) : private const val AGE_THREHOLD_FOR_DELETE_MESSAGE: Int = 21600000 // (6 hours in millis = 6 * 3600 * 1000) private const val REQUEST_CODE_CHOOSE_FILE: Int = 555 private const val REQUEST_CODE_SELECT_CONTACT: Int = 666 + private const val REQUEST_CODE_MESSAGE_SEARCH: Int = 777 private const val REQUEST_RECORD_AUDIO_PERMISSION = 222 private const val REQUEST_READ_CONTACT_PERMISSION = 234 private const val REQUEST_CAMERA_PERMISSION = 223 diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.java b/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.java index ec59e4833..c76341bf6 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.java @@ -74,7 +74,7 @@ import com.nextcloud.talk.adapters.items.MessagesTextHeaderItem; import com.nextcloud.talk.api.NcApi; import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.controllers.base.BaseController; -import com.nextcloud.talk.controllers.util.MessageSearchHelper; +import com.nextcloud.talk.messagesearch.MessageSearchHelper; import com.nextcloud.talk.events.ConversationsListFetchDataEvent; import com.nextcloud.talk.events.EventStatus; import com.nextcloud.talk.interfaces.ConversationMenuInterface; diff --git a/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt b/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt index d684db1b2..b0f7170d8 100644 --- a/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt +++ b/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt @@ -23,6 +23,7 @@ package com.nextcloud.talk.dagger.modules import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider +import com.nextcloud.talk.messagesearch.MessageSearchViewModel import com.nextcloud.talk.shareditems.viewmodels.SharedItemsViewModel import dagger.Binds import dagger.MapKey @@ -53,4 +54,9 @@ abstract class ViewModelModule { @IntoMap @ViewModelKey(SharedItemsViewModel::class) abstract fun sharedItemsViewModel(viewModel: SharedItemsViewModel): ViewModel + + @Binds + @IntoMap + @ViewModelKey(MessageSearchViewModel::class) + abstract fun messageSearchViewModel(viewModel: MessageSearchViewModel): ViewModel } diff --git a/app/src/main/java/com/nextcloud/talk/messagesearch/MessageSearchActivity.kt b/app/src/main/java/com/nextcloud/talk/messagesearch/MessageSearchActivity.kt new file mode 100644 index 000000000..5c84c292a --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/messagesearch/MessageSearchActivity.kt @@ -0,0 +1,238 @@ +/* + * Nextcloud Talk application + * + * @author Álvaro Brey + * Copyright (C) 2022 Álvaro Brey + * Copyright (C) 2022 Nextcloud GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.messagesearch + +import android.app.Activity +import android.os.Bundle +import android.text.TextUtils +import android.view.Menu +import android.view.MenuItem +import android.view.View +import android.widget.Toast +import androidx.appcompat.widget.SearchView +import androidx.core.content.res.ResourcesCompat +import androidx.lifecycle.ViewModelProvider +import autodagger.AutoInjector +import com.nextcloud.talk.R +import com.nextcloud.talk.activities.BaseActivity +import com.nextcloud.talk.adapters.items.LoadMoreResultsItem +import com.nextcloud.talk.adapters.items.MessageResultItem +import com.nextcloud.talk.application.NextcloudTalkApplication +import com.nextcloud.talk.controllers.ConversationsListController +import com.nextcloud.talk.databinding.ActivityMessageSearchBinding +import com.nextcloud.talk.models.database.UserEntity +import com.nextcloud.talk.utils.DisplayUtils +import com.nextcloud.talk.utils.bundle.BundleKeys +import com.nextcloud.talk.utils.rx.SearchViewObservable.Companion.observeSearchView +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import eu.davidea.viewholders.FlexibleViewHolder +import io.reactivex.Observable +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.Disposable +import io.reactivex.schedulers.Schedulers +import java.util.concurrent.TimeUnit +import javax.inject.Inject + +@AutoInjector(NextcloudTalkApplication::class) +class MessageSearchActivity : BaseActivity() { + + @Inject + lateinit var viewModelFactory: ViewModelProvider.Factory + + private lateinit var binding: ActivityMessageSearchBinding + private lateinit var searchView: SearchView + + private lateinit var user: UserEntity + + private lateinit var viewModel: MessageSearchViewModel + + private var searchViewDisposable: Disposable? = null + private var adapter: FlexibleAdapter>? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) + + binding = ActivityMessageSearchBinding.inflate(layoutInflater) + setupActionBar() + setupSystemColors() + setContentView(binding.root) + + viewModel = ViewModelProvider(this, viewModelFactory)[MessageSearchViewModel::class.java] + user = intent.getParcelableExtra(BundleKeys.KEY_USER_ENTITY)!! + val roomToken = intent.getStringExtra(BundleKeys.KEY_ROOM_TOKEN)!! + viewModel.initialize(user, roomToken) + setupStateObserver() + } + + private fun setupActionBar() { + setSupportActionBar(binding.messageSearchToolbar) + supportActionBar?.setDisplayHomeAsUpEnabled(true) + val conversationName = intent.getStringExtra(BundleKeys.KEY_CONVERSATION_NAME) + supportActionBar?.title = conversationName + } + + private fun setupSystemColors() { + DisplayUtils.applyColorToStatusBar( + this, + ResourcesCompat.getColor( + resources, R.color.appbar, null + ) + ) + DisplayUtils.applyColorToNavigationBar( + this.window, + ResourcesCompat.getColor(resources, R.color.bg_default, null) + ) + } + + private fun setupStateObserver() { + viewModel.state.observe(this) { state -> + when (state) { + MessageSearchViewModel.EmptyState -> showEmpty() + MessageSearchViewModel.InitialState -> showInitial() + is MessageSearchViewModel.LoadedState -> showLoaded(state) + MessageSearchViewModel.LoadingState -> showLoading() + MessageSearchViewModel.ErrorState -> showError() + } + } + } + + private fun showError() { + Toast.makeText(this, "Error while searching", Toast.LENGTH_SHORT).show() + } + + private fun showLoading() { + // TODO + Toast.makeText(this, "LOADING", Toast.LENGTH_LONG).show() + } + + private fun showLoaded(state: MessageSearchViewModel.LoadedState) { + binding.emptyContainer.emptyListView.visibility = View.GONE + binding.messageSearchRecycler.visibility = View.VISIBLE + setAdapterItems(state) + } + + private fun setAdapterItems(state: MessageSearchViewModel.LoadedState) { + val loadMoreItems = if (state.hasMore) { + listOf(LoadMoreResultsItem) + } else { + emptyList() + } + val newItems = + state.results.map { MessageResultItem(this, user, it) } + loadMoreItems + + if (adapter != null) { + adapter!!.updateDataSet(newItems) + } else { + createAdapter(newItems) + } + } + + private fun createAdapter(items: List>) { + adapter = FlexibleAdapter(items) + binding.messageSearchRecycler.adapter = adapter + adapter!!.addListener(object : FlexibleAdapter.OnItemClickListener { + override fun onItemClick(view: View?, position: Int): Boolean { + val item = adapter!!.getItem(position) + if (item?.itemViewType == LoadMoreResultsItem.VIEW_TYPE) { + viewModel.loadMore() + } + return false + } + }) + } + + private fun showInitial() { + binding.messageSearchRecycler.visibility = View.GONE + binding.emptyContainer.emptyListViewHeadline.text = "Start typing to search..." + binding.emptyContainer.emptyListView.visibility = View.VISIBLE + } + + private fun showEmpty() { + binding.messageSearchRecycler.visibility = View.GONE + binding.emptyContainer.emptyListViewHeadline.text = "No search results" + binding.emptyContainer.emptyListView.visibility = View.VISIBLE + } + + override fun onCreateOptionsMenu(menu: Menu?): Boolean { + menuInflater.inflate(R.menu.menu_search, menu) + return true + } + + override fun onPrepareOptionsMenu(menu: Menu?): Boolean { + val menuItem = menu!!.findItem(R.id.action_search) + searchView = menuItem.actionView as SearchView + setupSearchView() + menuItem.setOnActionExpandListener(object : MenuItem.OnActionExpandListener { + override fun onMenuItemActionExpand(item: MenuItem?): Boolean { + searchView.requestFocus() + return true + } + + override fun onMenuItemActionCollapse(item: MenuItem?): Boolean { + onBackPressed() + return false + } + }) + menuItem.expandActionView() + return true + } + + private fun setupSearchView() { + searchView.queryHint = getString(R.string.nc_search_hint) + searchViewDisposable = observeSearchView(searchView) + .debounce { query -> + when { + TextUtils.isEmpty(query) -> Observable.empty() + else -> Observable.timer( + ConversationsListController.SEARCH_DEBOUNCE_INTERVAL_MS.toLong(), + TimeUnit.MILLISECONDS + ) + } + } + .distinctUntilChanged() + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { newText -> viewModel.onQueryTextChange(newText) } + } + + override fun onBackPressed() { + setResult(Activity.RESULT_CANCELED) + finish() + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return when (item.itemId) { + android.R.id.home -> { + onBackPressed() + true + } + else -> super.onOptionsItemSelected(item) + } + } + + override fun onDestroy() { + super.onDestroy() + searchViewDisposable?.dispose() + } +} diff --git a/app/src/main/java/com/nextcloud/talk/controllers/util/MessageSearchHelper.kt b/app/src/main/java/com/nextcloud/talk/messagesearch/MessageSearchHelper.kt similarity index 77% rename from app/src/main/java/com/nextcloud/talk/controllers/util/MessageSearchHelper.kt rename to app/src/main/java/com/nextcloud/talk/messagesearch/MessageSearchHelper.kt index 8a82398ec..0350a9f73 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/util/MessageSearchHelper.kt +++ b/app/src/main/java/com/nextcloud/talk/messagesearch/MessageSearchHelper.kt @@ -19,7 +19,7 @@ * along with this program. If not, see . */ -package com.nextcloud.talk.controllers.util +package com.nextcloud.talk.messagesearch import android.util.Log import com.nextcloud.talk.models.database.UserEntity @@ -28,9 +28,10 @@ import com.nextcloud.talk.repositories.unifiedsearch.UnifiedSearchRepository import io.reactivex.Observable import io.reactivex.disposables.Disposable -class MessageSearchHelper( +class MessageSearchHelper @JvmOverloads constructor( private val user: UserEntity, private val unifiedSearchRepository: UnifiedSearchRepository, + private val fromRoom: String? = null ) { data class MessageSearchResults(val messages: List, val hasMore: Boolean) @@ -58,7 +59,7 @@ class MessageSearchHelper( private fun doSearch(search: String, cursor: Int = 0): Observable { disposeIfPossible() - return unifiedSearchRepository.searchMessages(user, search, cursor) + return searchCall(search, cursor) .map { results -> previousSearch = search previousCursor = results.cursor @@ -76,6 +77,29 @@ class MessageSearchHelper( .doOnComplete(this::disposeIfPossible) } + private fun searchCall( + search: String, + cursor: Int + ): Observable> { + return when { + fromRoom != null -> { + unifiedSearchRepository.searchInRoom( + userEntity = user, + roomToken = fromRoom, + searchTerm = search, + cursor = cursor + ) + } + else -> { + unifiedSearchRepository.searchMessages( + userEntity = user, + searchTerm = search, + cursor = cursor + ) + } + } + } + private fun resetCachedData() { previousSearch = null previousCursor = 0 diff --git a/app/src/main/java/com/nextcloud/talk/messagesearch/MessageSearchViewModel.kt b/app/src/main/java/com/nextcloud/talk/messagesearch/MessageSearchViewModel.kt new file mode 100644 index 000000000..a7b0acd9e --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/messagesearch/MessageSearchViewModel.kt @@ -0,0 +1,114 @@ +/* + * Nextcloud Talk application + * + * @author Álvaro Brey + * Copyright (C) 2022 Álvaro Brey + * Copyright (C) 2022 Nextcloud GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.messagesearch + +import android.annotation.SuppressLint +import android.util.Log +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.nextcloud.talk.models.database.UserEntity +import com.nextcloud.talk.models.domain.SearchMessageEntry +import com.nextcloud.talk.repositories.unifiedsearch.UnifiedSearchRepository +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.Disposable +import io.reactivex.schedulers.Schedulers +import javax.inject.Inject + +/** + * Install PlantUML plugin to render this state diagram + * @startuml + * hide empty description + * [*] --> InitialState + * InitialState --> LoadingState + * LoadingState --> EmptyState + * LoadingState --> LoadedState + * LoadingState --> LoadingState + * LoadedState --> LoadingState + * EmptyState --> LoadingState + * LoadingState --> ErrorState + * ErrorState --> LoadingState + * @enduml + */ +class MessageSearchViewModel @Inject constructor(private val unifiedSearchRepository: UnifiedSearchRepository) : + ViewModel() { + + sealed class ViewState + object InitialState : ViewState() + object LoadingState : ViewState() + object EmptyState : ViewState() + object ErrorState : ViewState() + class LoadedState(val results: List, val hasMore: Boolean) : ViewState() + + private lateinit var messageSearchHelper: MessageSearchHelper + + private val _state: MutableLiveData = MutableLiveData(InitialState) + val state: LiveData + get() = _state + + private var searchDisposable: Disposable? = null + + fun initialize(user: UserEntity, roomToken: String) { + messageSearchHelper = MessageSearchHelper(user, unifiedSearchRepository, roomToken) + } + + @SuppressLint("CheckResult") // handled by helper + fun onQueryTextChange(newText: String) { + if (newText.length >= MIN_CHARS_FOR_SEARCH) { + _state.value = LoadingState + messageSearchHelper.cancelSearch() + messageSearchHelper.startMessageSearch(newText) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(this::onReceiveResults, this::onError) + } + } + + @SuppressLint("CheckResult") // handled by helper + fun loadMore() { + _state.value = LoadingState + messageSearchHelper.cancelSearch() + messageSearchHelper.loadMore() + ?.subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(this::onReceiveResults) + } + + private fun onReceiveResults(results: MessageSearchHelper.MessageSearchResults) { + if (results.messages.isEmpty()) { + _state.value = EmptyState + } else { + _state.value = LoadedState(results.messages, results.hasMore) + } + } + + private fun onError(throwable: Throwable) { + Log.e(TAG, "onError:", throwable) + messageSearchHelper.cancelSearch() + _state.value = ErrorState + } + + companion object { + private val TAG = MessageSearchViewModel::class.simpleName + private const val MIN_CHARS_FOR_SEARCH = 2 + } +} diff --git a/app/src/main/java/com/nextcloud/talk/repositories/unifiedsearch/UnifiedSearchRepository.kt b/app/src/main/java/com/nextcloud/talk/repositories/unifiedsearch/UnifiedSearchRepository.kt index 43a416395..4d91e1219 100644 --- a/app/src/main/java/com/nextcloud/talk/repositories/unifiedsearch/UnifiedSearchRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/repositories/unifiedsearch/UnifiedSearchRepository.kt @@ -18,7 +18,13 @@ interface UnifiedSearchRepository { limit: Int = DEFAULT_PAGE_SIZE ): Observable> - fun searchInRoom(text: String, roomId: String): Observable> + fun searchInRoom( + userEntity: UserEntity, + roomToken: String, + searchTerm: String, + cursor: Int = 0, + limit: Int = DEFAULT_PAGE_SIZE + ): Observable> companion object { private const val DEFAULT_PAGE_SIZE = 5 diff --git a/app/src/main/java/com/nextcloud/talk/repositories/unifiedsearch/UnifiedSearchRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/repositories/unifiedsearch/UnifiedSearchRepositoryImpl.kt index 430aca3d4..1f0d072c3 100644 --- a/app/src/main/java/com/nextcloud/talk/repositories/unifiedsearch/UnifiedSearchRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/repositories/unifiedsearch/UnifiedSearchRepositoryImpl.kt @@ -48,10 +48,26 @@ class UnifiedSearchRepositoryImpl(private val api: NcApi) : UnifiedSearchReposit return apiObservable.map { mapToMessageResults(it.ocs?.data!!, searchTerm, limit) } } - override fun searchInRoom(text: String, roomId: String): Observable> { - TODO() + override fun searchInRoom( + userEntity: UserEntity, + roomToken: String, + searchTerm: String, + cursor: Int, + limit: Int + ): Observable> { + val apiObservable = api.performUnifiedSearch( + ApiUtils.getCredentials(userEntity.username, userEntity.token), + ApiUtils.getUrlForUnifiedSearch(userEntity.baseUrl, PROVIDER_TALK_MESSAGE_CURRENT), + searchTerm, + fromUrlForRoom(roomToken), + limit, + cursor + ) + return apiObservable.map { mapToMessageResults(it.ocs?.data!!, searchTerm, limit) } } + private fun fromUrlForRoom(roomToken: String) = "/call/$roomToken" + companion object { private const val PROVIDER_TALK_MESSAGE = "talk-message" private const val PROVIDER_TALK_MESSAGE_CURRENT = "talk-message-current" diff --git a/app/src/main/res/layout/activity_message_search.xml b/app/src/main/res/layout/activity_message_search.xml new file mode 100644 index 000000000..5c7ddf74c --- /dev/null +++ b/app/src/main/res/layout/activity_message_search.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/empty_list.xml b/app/src/main/res/layout/empty_list.xml index 40ad3c2c7..f1cc5e8c5 100644 --- a/app/src/main/res/layout/empty_list.xml +++ b/app/src/main/res/layout/empty_list.xml @@ -20,6 +20,7 @@ License along with this program. If not, see . --> diff --git a/app/src/main/res/menu/menu_conversation.xml b/app/src/main/res/menu/menu_conversation.xml index 28fea422b..7b923aea6 100644 --- a/app/src/main/res/menu/menu_conversation.xml +++ b/app/src/main/res/menu/menu_conversation.xml @@ -35,15 +35,22 @@ android:title="@string/nc_conversation_menu_video_call" app:showAsAction="ifRoom" /> + + diff --git a/app/src/main/res/menu/menu_search.xml b/app/src/main/res/menu/menu_search.xml new file mode 100644 index 000000000..dfb61e115 --- /dev/null +++ b/app/src/main/res/menu/menu_search.xml @@ -0,0 +1,30 @@ + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4beee18ec..cce6394d6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -273,14 +273,14 @@ Do not disturb Away Invisible - - 😃 - 👍 - 👎 - ❤️ - 😯 - 😢 - More emojis + + 😃 + 👍 + 👎 + ❤️ + 😯 + 😢 + More emojis Don\'t clear Today 30 minutes @@ -525,5 +525,6 @@ Call without notification Messages Load more results + Search… diff --git a/app/src/test/java/com/nextcloud/talk/controllers/util/MessageSearchHelperTest.kt b/app/src/test/java/com/nextcloud/talk/messagesearch/MessageSearchHelperTest.kt similarity index 99% rename from app/src/test/java/com/nextcloud/talk/controllers/util/MessageSearchHelperTest.kt rename to app/src/test/java/com/nextcloud/talk/messagesearch/MessageSearchHelperTest.kt index 6099a8e10..4dd9a5560 100644 --- a/app/src/test/java/com/nextcloud/talk/controllers/util/MessageSearchHelperTest.kt +++ b/app/src/test/java/com/nextcloud/talk/messagesearch/MessageSearchHelperTest.kt @@ -19,7 +19,7 @@ * along with this program. If not, see . */ -package com.nextcloud.talk.controllers.util +package com.nextcloud.talk.messagesearch import com.nextcloud.talk.models.database.UserEntity import com.nextcloud.talk.models.domain.SearchMessageEntry diff --git a/app/src/test/java/com/nextcloud/talk/test/fakes/FakeUnifiedSearchRepository.kt b/app/src/test/java/com/nextcloud/talk/test/fakes/FakeUnifiedSearchRepository.kt index cf2827934..2a216e69e 100644 --- a/app/src/test/java/com/nextcloud/talk/test/fakes/FakeUnifiedSearchRepository.kt +++ b/app/src/test/java/com/nextcloud/talk/test/fakes/FakeUnifiedSearchRepository.kt @@ -41,7 +41,14 @@ class FakeUnifiedSearchRepository : UnifiedSearchRepository { return Observable.just(response) } - override fun searchInRoom(text: String, roomId: String): Observable> { - TODO("Not yet implemented") + override fun searchInRoom( + userEntity: UserEntity, + roomToken: String, + searchTerm: String, + cursor: Int, + limit: Int + ): Observable> { + lastRequestedCursor = cursor + return Observable.just(response) } }