Browse Source

Add ability to scroll to message selected in search results

Signed-off-by: Álvaro Brey <alvaro.brey@nextcloud.com>
Álvaro Brey 2 years ago
parent
commit
dd55ab5741

+ 119 - 86
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<ChatMessage>): List<ChatMessage> {

+ 16 - 4
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);
+        }
     }
 }

+ 22 - 2
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"
+    }
 }

+ 1 - 0
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"
 }