From dd55ab5741beec287328b9d54d660d8c5d4ee08e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Brey?= Date: Fri, 27 May 2022 17:17:07 +0200 Subject: [PATCH] Add ability to scroll to message selected in search results MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Álvaro Brey --- .../talk/controllers/ChatController.kt | 205 ++++++++++-------- .../ConversationsListController.java | 20 +- .../messagesearch/MessageSearchActivity.kt | 24 +- .../nextcloud/talk/utils/bundle/BundleKeys.kt | 1 + 4 files changed, 158 insertions(+), 92 deletions(-) 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 b0c561588..dccabdd6c 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt @@ -1346,92 +1346,21 @@ class ChatController(args: Bundle) : } override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) { - if (resultCode != RESULT_OK) { - // TODO for message search, CANCELED is fine + if (resultCode != RESULT_OK && (requestCode != REQUEST_CODE_MESSAGE_SEARCH)) { Log.e(TAG, "resultCode for received intent was != ok") return } - if (requestCode == REQUEST_CODE_CHOOSE_FILE) { - try { - checkNotNull(intent) - filesToUpload.clear() - intent.clipData?.let { - for (index in 0 until it.itemCount) { - filesToUpload.add(it.getItemAt(index).uri.toString()) - } - } ?: run { - checkNotNull(intent.data) - intent.data.let { - filesToUpload.add(intent.data.toString()) - } - } - require(filesToUpload.isNotEmpty()) - - val filenamesWithLinebreaks = StringBuilder("\n") - - for (file in filesToUpload) { - val filename = UriUtils.getFileName(Uri.parse(file), context) - filenamesWithLinebreaks.append(filename).append("\n") - } - - val confirmationQuestion = when (filesToUpload.size) { - 1 -> context?.resources?.getString(R.string.nc_upload_confirm_send_single)?.let { - String.format(it, title) - } - else -> context?.resources?.getString(R.string.nc_upload_confirm_send_multiple)?.let { - String.format(it, title) - } - } - - LovelyStandardDialog(activity) - .setPositiveButtonColorRes(R.color.nc_darkGreen) - .setTitle(confirmationQuestion) - .setMessage(filenamesWithLinebreaks.toString()) - .setPositiveButton(R.string.nc_yes) { v -> - if (UploadAndShareFilesWorker.isStoragePermissionGranted(context!!)) { - uploadFiles(filesToUpload, false) - } else { - UploadAndShareFilesWorker.requestStoragePermission(this) - } - } - .setNegativeButton(R.string.nc_no) { - // unused atm - } - .show() - } catch (e: IllegalStateException) { - Toast.makeText(context, context?.resources?.getString(R.string.nc_upload_failed), Toast.LENGTH_LONG) - .show() - Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e) - } catch (e: IllegalArgumentException) { - Toast.makeText(context, context?.resources?.getString(R.string.nc_upload_failed), Toast.LENGTH_LONG) - .show() - Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e) - } - } else if (requestCode == REQUEST_CODE_SELECT_CONTACT) { - val contactUri = intent?.data ?: return - val cursor: Cursor? = activity?.contentResolver!!.query(contactUri, null, null, null, null) - - if (cursor != null && cursor.moveToFirst()) { - val id = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.Contacts._ID)) - val fileName = ContactUtils.getDisplayNameFromDeviceContact(context!!, id) + ".vcf" - val file = File(context?.cacheDir, fileName) - writeContactToVcfFile(cursor, file) - - val shareUri = FileProvider.getUriForFile( - activity!!, - BuildConfig.APPLICATION_ID, - File(file.absolutePath) - ) - uploadFiles(mutableListOf(shareUri.toString()), false) - } - cursor?.close() - } else if (requestCode == REQUEST_CODE_PICK_CAMERA) { - if (resultCode == RESULT_OK) { + when (requestCode) { + REQUEST_CODE_CHOOSE_FILE -> { try { checkNotNull(intent) filesToUpload.clear() - run { + intent.clipData?.let { + for (index in 0 until it.itemCount) { + filesToUpload.add(it.getItemAt(index).uri.toString()) + } + } ?: run { checkNotNull(intent.data) intent.data.let { filesToUpload.add(intent.data.toString()) @@ -1439,11 +1368,37 @@ class ChatController(args: Bundle) : } require(filesToUpload.isNotEmpty()) - if (UploadAndShareFilesWorker.isStoragePermissionGranted(context!!)) { - uploadFiles(filesToUpload, false) - } else { - UploadAndShareFilesWorker.requestStoragePermission(this) + val filenamesWithLinebreaks = StringBuilder("\n") + + for (file in filesToUpload) { + val filename = UriUtils.getFileName(Uri.parse(file), context) + filenamesWithLinebreaks.append(filename).append("\n") } + + val confirmationQuestion = when (filesToUpload.size) { + 1 -> context?.resources?.getString(R.string.nc_upload_confirm_send_single)?.let { + String.format(it, title) + } + else -> context?.resources?.getString(R.string.nc_upload_confirm_send_multiple)?.let { + String.format(it, title) + } + } + + LovelyStandardDialog(activity) + .setPositiveButtonColorRes(R.color.nc_darkGreen) + .setTitle(confirmationQuestion) + .setMessage(filenamesWithLinebreaks.toString()) + .setPositiveButton(R.string.nc_yes) { v -> + if (UploadAndShareFilesWorker.isStoragePermissionGranted(context!!)) { + uploadFiles(filesToUpload, false) + } else { + UploadAndShareFilesWorker.requestStoragePermission(this) + } + } + .setNegativeButton(R.string.nc_no) { + // unused atm + } + .show() } catch (e: IllegalStateException) { Toast.makeText(context, context?.resources?.getString(R.string.nc_upload_failed), Toast.LENGTH_LONG) .show() @@ -1454,8 +1409,79 @@ 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() + REQUEST_CODE_SELECT_CONTACT -> { + val contactUri = intent?.data ?: return + val cursor: Cursor? = activity?.contentResolver!!.query(contactUri, null, null, null, null) + + if (cursor != null && cursor.moveToFirst()) { + val id = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.Contacts._ID)) + val fileName = ContactUtils.getDisplayNameFromDeviceContact(context!!, id) + ".vcf" + val file = File(context?.cacheDir, fileName) + writeContactToVcfFile(cursor, file) + + val shareUri = FileProvider.getUriForFile( + activity!!, + BuildConfig.APPLICATION_ID, + File(file.absolutePath) + ) + uploadFiles(mutableListOf(shareUri.toString()), false) + } + cursor?.close() + } + REQUEST_CODE_PICK_CAMERA -> { + if (resultCode == RESULT_OK) { + try { + checkNotNull(intent) + filesToUpload.clear() + run { + checkNotNull(intent.data) + intent.data.let { + filesToUpload.add(intent.data.toString()) + } + } + require(filesToUpload.isNotEmpty()) + + if (UploadAndShareFilesWorker.isStoragePermissionGranted(context!!)) { + uploadFiles(filesToUpload, false) + } else { + UploadAndShareFilesWorker.requestStoragePermission(this) + } + } catch (e: IllegalStateException) { + Toast.makeText( + context, + context?.resources?.getString(R.string.nc_upload_failed), + Toast.LENGTH_LONG + ) + .show() + Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e) + } catch (e: IllegalArgumentException) { + Toast.makeText( + context, + context?.resources?.getString(R.string.nc_upload_failed), + Toast.LENGTH_LONG + ) + .show() + Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e) + } + } + } + REQUEST_CODE_MESSAGE_SEARCH -> { + val messageId = intent?.getStringExtra(MessageSearchActivity.RESULT_KEY_MESSAGE_ID) + messageId?.let { id -> + scrollToMessageWithId(id) + } + } + } + } + + private fun scrollToMessageWithId(messageId: String) { + val position = adapter?.items?.indexOfFirst { + it.item is ChatMessage && (it.item as ChatMessage).id == messageId + } + if (position != null && position >= 0) { + binding.messagesListView.smoothScrollToPosition(position) + } else { + // TODO show error that we don't have that message? } } @@ -2283,6 +2309,7 @@ class ChatController(args: Bundle) : if (adapter != null) { adapter?.addToEnd(chatMessageList, false) } + scrollToRequestedMessageIfNeeded() } else { var chatMessage: ChatMessage @@ -2398,6 +2425,12 @@ class ChatController(args: Bundle) : } } + private fun scrollToRequestedMessageIfNeeded() { + args.getString(BundleKeys.KEY_MESSAGE_ID)?.let { + scrollToMessageWithId(it) + } + } + private fun isSameDayNonSystemMessages(messageLeft: ChatMessage, messageRight: ChatMessage): Boolean { return TextUtils.isEmpty(messageLeft.systemMessage) && TextUtils.isEmpty(messageRight.systemMessage) && @@ -2515,7 +2548,7 @@ class ChatController(args: Bundle) : 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) + startActivityForResult(intent, REQUEST_CODE_MESSAGE_SEARCH) } private fun handleSystemMessages(chatMessageList: List): List { 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 c76341bf6..a6065a54e 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,6 @@ 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.messagesearch.MessageSearchHelper; import com.nextcloud.talk.events.ConversationsListFetchDataEvent; import com.nextcloud.talk.events.EventStatus; import com.nextcloud.talk.interfaces.ConversationMenuInterface; @@ -82,6 +81,7 @@ import com.nextcloud.talk.jobs.AccountRemovalWorker; import com.nextcloud.talk.jobs.ContactAddressBookWorker; import com.nextcloud.talk.jobs.DeleteConversationWorker; import com.nextcloud.talk.jobs.UploadAndShareFilesWorker; +import com.nextcloud.talk.messagesearch.MessageSearchHelper; import com.nextcloud.talk.models.database.CapabilitiesUtil; import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.models.domain.SearchMessageEntry; @@ -223,6 +223,7 @@ public class ConversationsListController extends BaseController implements Flexi private Conversation selectedConversation; private String textToPaste = ""; + private String selectedMessageId = null; private boolean forwardMessage; @@ -921,7 +922,9 @@ public class ConversationsListController extends BaseController implements Flexi @SuppressLint("CheckResult") // handled by helper private void startMessageSearch(final String search) { - swipeRefreshLayout.setRefreshing(true); + if (swipeRefreshLayout != null) { + swipeRefreshLayout.setRefreshing(true); + } searchHelper .startMessageSearch(search) .subscribeOn(Schedulers.io()) @@ -958,6 +961,7 @@ public class ConversationsListController extends BaseController implements Flexi } else if (item instanceof MessageResultItem) { MessageResultItem messageItem = (MessageResultItem) item; String conversationToken = messageItem.getMessageEntry().getConversationToken(); + selectedMessageId = messageItem.getMessageEntry().getMessageId(); showConversationByToken(conversationToken); } else if (item instanceof LoadMoreResultsItem) { loadMoreMessages(); @@ -1187,6 +1191,10 @@ public class ConversationsListController extends BaseController implements Flexi bundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN(), selectedConversation.getToken()); bundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_ID(), selectedConversation.getRoomId()); bundle.putString(BundleKeys.INSTANCE.getKEY_SHARED_TEXT(), textToPaste); + if (selectedMessageId != null) { + bundle.putString(BundleKeys.KEY_MESSAGE_ID, selectedMessageId); + selectedMessageId = null; + } ConductorRemapping.INSTANCE.remapChatController(getRouter(), currentUser.getId(), selectedConversation.getToken(), bundle, false); @@ -1394,11 +1402,15 @@ public class ConversationsListController extends BaseController implements Flexi recyclerView.scrollToPosition(0); } } - swipeRefreshLayout.setRefreshing(false); + if (swipeRefreshLayout != null) { + swipeRefreshLayout.setRefreshing(false); + } } public void onMessageSearchError(@NonNull Throwable throwable) { handleHttpExceptions(throwable); - swipeRefreshLayout.setRefreshing(false); + if (swipeRefreshLayout != null) { + swipeRefreshLayout.setRefreshing(false); + } } } diff --git a/app/src/main/java/com/nextcloud/talk/messagesearch/MessageSearchActivity.kt b/app/src/main/java/com/nextcloud/talk/messagesearch/MessageSearchActivity.kt index 5c84c292a..090e36fcb 100644 --- a/app/src/main/java/com/nextcloud/talk/messagesearch/MessageSearchActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/messagesearch/MessageSearchActivity.kt @@ -22,6 +22,7 @@ package com.nextcloud.talk.messagesearch import android.app.Activity +import android.content.Intent import android.os.Bundle import android.text.TextUtils import android.view.Menu @@ -154,14 +155,29 @@ class MessageSearchActivity : BaseActivity() { 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() + when (item?.itemViewType) { + LoadMoreResultsItem.VIEW_TYPE -> { + viewModel.loadMore() + } + MessageResultItem.VIEW_TYPE -> { + // TODO go through viewmodel + val messageItem = item as MessageResultItem + finishWithResult(messageItem.messageEntry.messageId!!) + } } return false } }) } + private fun finishWithResult(messageId: String) { + val resultIntent = Intent().apply { + putExtra(RESULT_KEY_MESSAGE_ID, messageId) + } + setResult(Activity.RESULT_OK, resultIntent) + finish() + } + private fun showInitial() { binding.messageSearchRecycler.visibility = View.GONE binding.emptyContainer.emptyListViewHeadline.text = "Start typing to search..." @@ -235,4 +251,8 @@ class MessageSearchActivity : BaseActivity() { super.onDestroy() searchViewDisposable?.dispose() } + + companion object { + const val RESULT_KEY_MESSAGE_ID = "MessageSearchActivity.result.message" + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt b/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt index eac2cf190..02e1b0abe 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt @@ -73,4 +73,5 @@ object BundleKeys { val KEY_FORWARD_MSG_TEXT = "KEY_FORWARD_MSG_TEXT" val KEY_FORWARD_HIDE_SOURCE_ROOM = "KEY_FORWARD_HIDE_SOURCE_ROOM" val KEY_SYSTEM_NOTIFICATION_ID = "KEY_SYSTEM_NOTIFICATION_ID" + const val KEY_MESSAGE_ID = "KEY_MESSAGE_ID" }