Add "Select text" context menu option

Mishiranu created

Change summary

src/main/java/eu/siacs/conversations/ui/ConversationFragment.java   | 60 
src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java |  9 
src/main/res/menu/message_context.xml                               |  4 
src/main/res/values-ru/strings.xml                                  |  1 
src/main/res/values/strings.xml                                     |  1 
5 files changed, 75 insertions(+)

Detailed changes

src/main/java/eu/siacs/conversations/ui/ConversationFragment.java 🔗

@@ -12,6 +12,8 @@ import android.content.IntentSender.SendIntentException;
 import android.os.Bundle;
 import android.os.Handler;
 import android.text.InputType;
+import android.text.Selection;
+import android.text.Spannable;
 import android.util.Log;
 import android.util.Pair;
 import android.view.ContextMenu;
@@ -38,6 +40,8 @@ import android.widget.Toast;
 
 import net.java.otr4j.session.SessionStatus;
 
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -536,6 +540,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
 			activity.getMenuInflater().inflate(R.menu.message_context, menu);
 			menu.setHeaderTitle(R.string.message_options);
 			MenuItem copyText = menu.findItem(R.id.copy_text);
+			MenuItem selectText = menu.findItem(R.id.select_text);
 			MenuItem retryDecryption = menu.findItem(R.id.retry_decryption);
 			MenuItem correctMessage = menu.findItem(R.id.correct_message);
 			MenuItem shareWith = menu.findItem(R.id.share_with);
@@ -548,6 +553,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
 					&& !GeoHelper.isGeoUri(m.getBody())
 					&& m.treatAsDownloadable() != Message.Decision.MUST) {
 				copyText.setVisible(true);
+				selectText.setVisible(METHOD_START_SELECTION != null);
 			}
 			if (m.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
 				retryDecryption.setVisible(true);
@@ -597,6 +603,9 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
 			case R.id.copy_text:
 				copyText(selectedMessage);
 				return true;
+			case R.id.select_text:
+				selectText(selectedMessage);
+				return true;
 			case R.id.correct_message:
 				correctMessage(selectedMessage);
 				return true;
@@ -656,6 +665,30 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
 		}
 	}
 
+	private void selectText(Message message) {
+		final int index;
+		synchronized (this.messageList) {
+			index = this.messageList.indexOf(message);
+		}
+		if (index >= 0) {
+			final int first = this.messagesView.getFirstVisiblePosition();
+			final int last = first + this.messagesView.getChildCount();
+			if (index >= first && index < last)	{
+				final View view = this.messagesView.getChildAt(index - first);
+				final TextView messageBody = this.messageListAdapter.getMessageBody(view);
+				if (messageBody != null) {
+					final Spannable text = (Spannable) messageBody.getText();
+					Selection.setSelection(text, 0, text.length());
+					try {
+						Object editor = FIELD_EDITOR != null ? FIELD_EDITOR.get(messageBody) : messageBody;
+						METHOD_START_SELECTION.invoke(editor);
+					} catch (Exception e) {
+					}
+				}
+			}
+		}
+	}
+
 	private void deleteFile(Message message) {
 		if (activity.xmppConnectionService.getFileBackend().deleteFile(message)) {
 			message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED));
@@ -1430,4 +1463,31 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
 		}
 	}
 
+	private static final Field FIELD_EDITOR;
+	private static final Method METHOD_START_SELECTION;
+
+	static {
+		Field editor;
+		try {
+			editor = TextView.class.getDeclaredField("mEditor");
+			editor.setAccessible(true);
+		} catch (Exception e) {
+			editor = null;
+		}
+		FIELD_EDITOR = editor;
+		Class<?> editorClass = editor != null ? editor.getType() : TextView.class;
+		String[] startSelectionNames = {"startSelectionActionMode", "startSelectionActionModeWithSelection"};
+		Method startSelection = null;
+		for (String startSelectionName : startSelectionNames) {
+			try {
+				startSelection = editorClass.getDeclaredMethod(startSelectionName);
+				startSelection.setAccessible(true);
+				break;
+			} catch (Exception e) {
+				startSelection = null;
+			}
+		}
+		METHOD_START_SELECTION = startSelection;
+	}
+
 }

src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java 🔗

@@ -736,6 +736,15 @@ public class MessageAdapter extends ArrayAdapter<Message> {
 		this.mUseGreenBackground = activity.useGreenBackground();
 	}
 
+	public TextView getMessageBody(View view) {
+		final Object tag = view.getTag();
+		if (tag instanceof ViewHolder) {
+			final ViewHolder viewHolder = (ViewHolder) tag;
+			return viewHolder.messageBody;
+		}
+		return null;
+	}
+
 	public interface OnContactPictureClicked {
 		void onContactPictureClicked(Message message);
 	}

src/main/res/menu/message_context.xml 🔗

@@ -5,6 +5,10 @@
         android:id="@+id/copy_text"
         android:title="@string/copy_text"
         android:visible="false"/>
+     <item
+        android:id="@+id/select_text"
+        android:title="@string/select_text"
+        android:visible="false"/>
     <item
         android:id="@+id/retry_decryption"
         android:title="Retry decryption"

src/main/res/values-ru/strings.xml 🔗

@@ -325,6 +325,7 @@
   <string name="check_x_filesize_on_host">Проверьте размер %1$s на %2$s</string>
   <string name="message_options">Опции сообщения</string>
   <string name="copy_text">Копировать текст</string>
+  <string name="select_text">Выбрать текст</string>
   <string name="copy_original_url">Копировать адрес ссылки</string>
   <string name="send_again">Отправить ещё раз</string>
   <string name="file_url">URL файла</string>

src/main/res/values/strings.xml 🔗

@@ -362,6 +362,7 @@
 	<string name="check_x_filesize_on_host">Check %1$s size on %2$s</string>
 	<string name="message_options">Message options</string>
 	<string name="copy_text">Copy text</string>
+	<string name="select_text">Select text</string>
 	<string name="copy_original_url">Copy original URL</string>
 	<string name="send_again">Send again</string>
 	<string name="file_url">File URL</string>