Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
443 changes: 443 additions & 0 deletions FEATURE_TEXT_SELECTION.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@
import org.thoughtcrime.securesms.util.LongClickMovementMethod;
import org.thoughtcrime.securesms.util.MarkdownUtil;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.Prefs;
import org.thoughtcrime.securesms.util.TextSelectionActionModeCallback;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.views.Stub;
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/org/thoughtcrime/securesms/util/Prefs.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public class Prefs {
private static final String CHAT_RINGTONE = "pref_chat_ringtone_"; // followed by chat-id
public static final String SCREEN_SECURITY_PREF = "pref_screen_security";
private static final String ENTER_SENDS_PREF = "pref_enter_sends";
private static final String TEXT_SELECTION_PREF = "pref_text_selection";
private static final String PROMPTED_DOZE_MSG_ID_PREF = "pref_prompted_doze_msg_id";
private static final String STATS_DEVICE_MSG_ID_PREF = "pref_stats_device_msg_id";
private static final String UPDATE_MSG_PREF = "pref_update_msg";
Expand Down Expand Up @@ -142,6 +143,10 @@ public static boolean isEnterSendsEnabled(Context context) {
return getBooleanPreference(context, ENTER_SENDS_PREF, false);
}

public static boolean isTextSelectionEnabled(Context context) {
return getBooleanPreference(context, TEXT_SELECTION_PREF, false);
}

public static boolean isPasswordDisabled(Context context) {
return getBooleanPreference(context, DISABLE_PASSPHRASE_PREF, false);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Copyright (C) 2026 ArcaneChat contributors
*
* 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.
*/
package org.thoughtcrime.securesms.util;

import android.content.Context;
import android.content.Intent;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.R;

/**
* Custom ActionMode callback shown when the user selects part of a message's text.
*
* <p>It exposes "Copy", "Select all" and "Share" actions for the currently selected text,
* and shows a confirmation toast when text is copied to the clipboard.
*/
public class TextSelectionActionModeCallback implements ActionMode.Callback {

private final Context context;
private final TextView textView;

public TextSelectionActionModeCallback(@NonNull Context context, @NonNull TextView textView) {
this.context = context;
this.textView = textView;
}

@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
mode.getMenuInflater().inflate(R.menu.text_selection_context, menu);
return true;
}

@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}

@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
int id = item.getItemId();
if (id == R.id.menu_context_copy) {
CharSequence selected = textView.getText().subSequence(
textView.getSelectionStart(), textView.getSelectionEnd());
Util.writeTextToClipboard(context, selected.toString());
Toast.makeText(
context,
R.string.copied_to_clipboard,
Toast.LENGTH_SHORT)
.show();
mode.finish();
return true;
} else if (id == R.id.menu_context_select_all) {
textView.selectAll();
return true;
} else if (id == R.id.menu_context_share_text) {
CharSequence selected = textView.getText().subSequence(
textView.getSelectionStart(), textView.getSelectionEnd());
shareText(selected.toString());
mode.finish();
return true;
}
return false;
}

@Override
public void onDestroyActionMode(ActionMode mode) {}

private void shareText(@NonNull String text) {
Intent sendIntent = new Intent(Intent.ACTION_SEND);
sendIntent.setType("text/plain");
sendIntent.putExtra(Intent.EXTRA_TEXT, text);
context.startActivity(Intent.createChooser(sendIntent, context.getString(R.string.share_via)));
}
}
21 changes: 21 additions & 0 deletions src/main/res/menu/text_selection_context.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:title="@string/menu_copy_text_to_clipboard"
android:id="@+id/menu_context_copy"
android:icon="?menu_copy_icon"
app:showAsAction="ifRoom" />

<item
android:title="@string/menu_select_all"
android:id="@+id/menu_context_select_all"
app:showAsAction="never" />

<item
android:title="@string/menu_share"
android:id="@+id/menu_context_share_text"
android:icon="@drawable/ic_share_white_24dp"
app:showAsAction="ifRoom" />
</menu>
2 changes: 2 additions & 0 deletions src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,8 @@
<!-- Option name for changing behaviour of the "Enter key" to sending out a message directly (instead of adding new line). If in doubt, please refer to a similar option in other messengers. -->
<string name="pref_enter_sends">Enter Key Sends</string>
<string name="pref_enter_sends_explain">Pressing the Enter key will send text messages</string>
<string name="pref_text_selection">Text selection in messages</string>
<string name="pref_text_selection_explain">Select and copy parts of message text instead of entire messages only</string>
<string name="pref_outgoing_media_quality">Outgoing Media Quality</string>
<string name="pref_outgoing_media_quality_hint">To send original quality, attach media as a \"File\". This uses significantly more data for you and your recipients.</string>
<string name="pref_outgoing_balanced">Balanced</string>
Expand Down
6 changes: 6 additions & 0 deletions src/main/res/xml/preferences_chats.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@
android:summary="@string/pref_enter_sends_explain"
android:title="@string/pref_enter_sends" />

<org.thoughtcrime.securesms.components.SwitchPreferenceCompat
android:defaultValue="false"
android:key="pref_text_selection"
android:summary="@string/pref_text_selection_explain"
android:title="@string/pref_text_selection" />

<ListPreference
android:key="pref_compression"
android:title="@string/pref_outgoing_media_quality"
Expand Down