diff --git a/app/src/main/java/com/termux/app/TermuxActivity.java b/app/src/main/java/com/termux/app/TermuxActivity.java
index 1aa35a32e1..a2bb11e8c4 100644
--- a/app/src/main/java/com/termux/app/TermuxActivity.java
+++ b/app/src/main/java/com/termux/app/TermuxActivity.java
@@ -38,6 +38,7 @@
import com.termux.shared.activity.media.AppCompatActivityUtils;
import com.termux.shared.data.IntentUtils;
import com.termux.shared.android.PermissionUtils;
+import com.termux.shared.data.DataUtils;
import com.termux.shared.termux.TermuxConstants;
import com.termux.shared.termux.TermuxConstants.TERMUX_APP.TERMUX_ACTIVITY;
import com.termux.app.activities.HelpActivity;
@@ -179,6 +180,7 @@ public final class TermuxActivity extends AppCompatActivity implements ServiceCo
private static final int CONTEXT_MENU_SELECT_URL_ID = 0;
private static final int CONTEXT_MENU_SHARE_TRANSCRIPT_ID = 1;
+ private static final int CONTEXT_MENU_SHARE_SELECTED_TEXT = 10;
private static final int CONTEXT_MENU_AUTOFILL_ID = 2;
private static final int CONTEXT_MENU_RESET_TERMINAL_ID = 3;
private static final int CONTEXT_MENU_KILL_PROCESS_ID = 4;
@@ -640,7 +642,10 @@ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuIn
menu.add(Menu.NONE, CONTEXT_MENU_SELECT_URL_ID, Menu.NONE, R.string.action_select_url);
menu.add(Menu.NONE, CONTEXT_MENU_SHARE_TRANSCRIPT_ID, Menu.NONE, R.string.action_share_transcript);
- if (addAutoFillMenu) menu.add(Menu.NONE, CONTEXT_MENU_AUTOFILL_ID, Menu.NONE, R.string.action_autofill_password);
+ if (!DataUtils.isNullOrEmpty(mTerminalView.getStoredSelectedText()))
+ menu.add(Menu.NONE, CONTEXT_MENU_SHARE_SELECTED_TEXT, Menu.NONE, R.string.action_share_selected_text);
+ if (addAutoFillMenu)
+ menu.add(Menu.NONE, CONTEXT_MENU_AUTOFILL_ID, Menu.NONE, R.string.action_autofill_password);
menu.add(Menu.NONE, CONTEXT_MENU_RESET_TERMINAL_ID, Menu.NONE, R.string.action_reset_terminal);
menu.add(Menu.NONE, CONTEXT_MENU_KILL_PROCESS_ID, Menu.NONE, getResources().getString(R.string.action_kill_process, getCurrentSession().getPid())).setEnabled(currentSession.isRunning());
menu.add(Menu.NONE, CONTEXT_MENU_STYLING_ID, Menu.NONE, R.string.action_style_terminal);
@@ -668,6 +673,9 @@ public boolean onContextItemSelected(MenuItem item) {
case CONTEXT_MENU_SHARE_TRANSCRIPT_ID:
mTermuxTerminalViewClient.shareSessionTranscript();
return true;
+ case CONTEXT_MENU_SHARE_SELECTED_TEXT:
+ mTermuxTerminalViewClient.shareSelectedText();
+ return true;
case CONTEXT_MENU_AUTOFILL_ID:
requestAutoFill();
return true;
@@ -697,6 +705,13 @@ public boolean onContextItemSelected(MenuItem item) {
}
}
+ @Override
+ public void onContextMenuClosed(Menu menu) {
+ super.onContextMenuClosed(menu);
+ // onContextMenuClosed() is triggered twice if back button is pressed to dismiss instead of tap for some reason
+ mTerminalView.onContextMenuClosed(menu);
+ }
+
private void showKillSessionDialog(TerminalSession session) {
if (session == null) return;
diff --git a/app/src/main/java/com/termux/app/terminal/TermuxTerminalSessionActivityClient.java b/app/src/main/java/com/termux/app/terminal/TermuxTerminalSessionActivityClient.java
index e3143635e9..cd38163116 100644
--- a/app/src/main/java/com/termux/app/terminal/TermuxTerminalSessionActivityClient.java
+++ b/app/src/main/java/com/termux/app/terminal/TermuxTerminalSessionActivityClient.java
@@ -17,6 +17,7 @@
import androidx.annotation.Nullable;
import com.termux.R;
+import com.termux.shared.interact.ShareUtils;
import com.termux.shared.termux.shell.command.runner.terminal.TermuxSession;
import com.termux.shared.termux.interact.TextInputDialogUtils;
import com.termux.app.TermuxActivity;
diff --git a/app/src/main/java/com/termux/app/terminal/TermuxTerminalViewClient.java b/app/src/main/java/com/termux/app/terminal/TermuxTerminalViewClient.java
index 90350a9c1a..a3d09d3d52 100644
--- a/app/src/main/java/com/termux/app/terminal/TermuxTerminalViewClient.java
+++ b/app/src/main/java/com/termux/app/terminal/TermuxTerminalViewClient.java
@@ -684,6 +684,13 @@ public void shareSessionTranscript() {
transcriptText, mActivity.getString(R.string.title_share_transcript_with));
}
+ public void shareSelectedText() {
+ String selectedText = mActivity.getTerminalView().getStoredSelectedText();
+ if (DataUtils.isNullOrEmpty(selectedText)) return;
+ ShareUtils.shareText(mActivity, mActivity.getString(R.string.title_share_selected_text),
+ selectedText, mActivity.getString(R.string.title_share_selected_text_with));
+ }
+
public void showUrlSelection() {
TerminalSession session = mActivity.getCurrentSession();
if (session == null) return;
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 642fbb5e07..794d8df3e0 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -69,6 +69,10 @@
Terminal transcript
Send transcript to:
+ Share selected text
+ Terminal Text
+ Send selected text to:
+
Autofill password
Reset
diff --git a/terminal-view/src/main/java/com/termux/view/TerminalView.java b/terminal-view/src/main/java/com/termux/view/TerminalView.java
index 365e300001..3baecd2481 100644
--- a/terminal-view/src/main/java/com/termux/view/TerminalView.java
+++ b/terminal-view/src/main/java/com/termux/view/TerminalView.java
@@ -2,6 +2,7 @@
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
+import android.app.Activity;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
@@ -19,6 +20,7 @@
import android.view.InputDevice;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
+import android.view.Menu;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
@@ -30,6 +32,7 @@
import android.view.inputmethod.InputConnection;
import android.widget.Scroller;
+import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.termux.terminal.KeyHandler;
@@ -456,6 +459,14 @@ public void onScreenUpdated(boolean skipScrolling) {
if (mAccessibilityEnabled) setContentDescription(getText());
}
+ /** This must be called by the hosting activity in {@link Activity#onContextMenuClosed(Menu)}
+ * when context menu for the {@link TerminalView} is started by
+ * {@link TextSelectionCursorController#ACTION_MORE} is closed. */
+ public void onContextMenuClosed(Menu menu) {
+ // Unset the stored text since it shouldn't be used anymore and should be cleared from memory
+ unsetStoredSelectedText();
+ }
+
/**
* Sets the text size, which in turn sets the number of rows and columns.
*
@@ -1206,6 +1217,25 @@ public boolean isSelectingText() {
}
}
+ /** Get the currently selected text if selecting. */
+ public String getSelectedText() {
+ if (isSelectingText() && mTextSelectionCursorController != null)
+ return mTextSelectionCursorController.getSelectedText();
+ else
+ return null;
+ }
+
+ /** Get the selected text stored before "MORE" button was pressed on the context menu. */
+ @Nullable
+ public String getStoredSelectedText() {
+ return mTextSelectionCursorController != null ? mTextSelectionCursorController.getStoredSelectedText() : null;
+ }
+
+ /** Unset the selected text stored before "MORE" button was pressed on the context menu. */
+ public void unsetStoredSelectedText() {
+ if (mTextSelectionCursorController != null) mTextSelectionCursorController.unsetStoredSelectedText();
+ }
+
private ActionMode getTextSelectionActionMode() {
if (mTextSelectionCursorController != null) {
return mTextSelectionCursorController.getActionMode();
diff --git a/terminal-view/src/main/java/com/termux/view/textselection/TextSelectionCursorController.java b/terminal-view/src/main/java/com/termux/view/textselection/TextSelectionCursorController.java
index c08dca4470..c2cd7c6c0a 100644
--- a/terminal-view/src/main/java/com/termux/view/textselection/TextSelectionCursorController.java
+++ b/terminal-view/src/main/java/com/termux/view/textselection/TextSelectionCursorController.java
@@ -11,6 +11,8 @@
import android.view.MotionEvent;
import android.view.View;
+import androidx.annotation.Nullable;
+
import com.termux.terminal.TerminalBuffer;
import com.termux.terminal.WcWidth;
import com.termux.view.R;
@@ -20,6 +22,7 @@ public class TextSelectionCursorController implements CursorController {
private final TerminalView terminalView;
private final TextSelectionHandleView mStartHandle, mEndHandle;
+ private String mStoredSelectedText;
private boolean mIsSelectingText = false;
private long mShowStartTime = System.currentTimeMillis();
@@ -27,9 +30,9 @@ public class TextSelectionCursorController implements CursorController {
private int mSelX1 = -1, mSelX2 = -1, mSelY1 = -1, mSelY2 = -1;
private ActionMode mActionMode;
- private final int ACTION_COPY = 1;
- private final int ACTION_PASTE = 2;
- private final int ACTION_MORE = 3;
+ public final int ACTION_COPY = 1;
+ public final int ACTION_PASTE = 2;
+ public final int ACTION_MORE = 3;
public TextSelectionCursorController(TerminalView terminalView) {
this.terminalView = terminalView;
@@ -112,7 +115,7 @@ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
ClipboardManager clipboard = (ClipboardManager) terminalView.getContext().getSystemService(Context.CLIPBOARD_SERVICE);
menu.add(Menu.NONE, ACTION_COPY, Menu.NONE, R.string.copy_text).setShowAsAction(show);
- menu.add(Menu.NONE, ACTION_PASTE, Menu.NONE, R.string.paste_text).setEnabled(clipboard.hasPrimaryClip()).setShowAsAction(show);
+ menu.add(Menu.NONE, ACTION_PASTE, Menu.NONE, R.string.paste_text).setEnabled(clipboard != null && clipboard.hasPrimaryClip()).setShowAsAction(show);
menu.add(Menu.NONE, ACTION_MORE, Menu.NONE, R.string.text_selection_more);
return true;
}
@@ -131,7 +134,7 @@ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
case ACTION_COPY:
- String selectedText = terminalView.mEmulator.getSelectedText(mSelX1, mSelY1, mSelX2, mSelY2).trim();
+ String selectedText = getSelectedText();
terminalView.mTermSession.onCopyTextToClipboard(selectedText);
terminalView.stopTextSelectionMode();
break;
@@ -140,7 +143,13 @@ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
terminalView.mTermSession.onPasteTextFromClipboard();
break;
case ACTION_MORE:
- terminalView.stopTextSelectionMode(); //we stop text selection first, otherwise handles will show above popup
+ // We first store the selected text in case TerminalViewClient needs the
+ // selected text before MORE button was pressed since we are going to
+ // stop selection mode
+ mStoredSelectedText = getSelectedText();
+ // The text selection needs to be stopped before showing context menu,
+ // otherwise handles will show above popup
+ terminalView.stopTextSelectionMode();
terminalView.showContextMenu();
break;
}
@@ -361,6 +370,22 @@ public void getSelectors(int[] sel) {
sel[3] = mSelX2;
}
+ /** Get the currently selected text. */
+ public String getSelectedText() {
+ return terminalView.mEmulator.getSelectedText(mSelX1, mSelY1, mSelX2, mSelY2);
+ }
+
+ /** Get the selected text stored before "MORE" button was pressed on the context menu. */
+ @Nullable
+ public String getStoredSelectedText() {
+ return mStoredSelectedText;
+ }
+
+ /** Unset the selected text stored before "MORE" button was pressed on the context menu. */
+ public void unsetStoredSelectedText() {
+ mStoredSelectedText = null;
+ }
+
public ActionMode getActionMode() {
return mActionMode;
}
diff --git a/termux-shared/src/main/java/com/termux/shared/interact/ShareUtils.java b/termux-shared/src/main/java/com/termux/shared/interact/ShareUtils.java
index 8d42215bcc..969502447c 100644
--- a/termux-shared/src/main/java/com/termux/shared/interact/ShareUtils.java
+++ b/termux-shared/src/main/java/com/termux/shared/interact/ShareUtils.java
@@ -156,6 +156,7 @@ public static CharSequence getTextFromClipboard(Context context, boolean coerceT
+ /**
* Open a url.
*
* @param context The context for operations.