diff --git a/app/src/main/java/com/termux/app/TermuxActivity.java b/app/src/main/java/com/termux/app/TermuxActivity.java
index 7a554892..3e7f83c3 100644
--- a/app/src/main/java/com/termux/app/TermuxActivity.java
+++ b/app/src/main/java/com/termux/app/TermuxActivity.java
@@ -36,6 +36,7 @@ import com.termux.R;
import com.termux.app.terminal.TermuxActivityRootView;
import com.termux.shared.activities.ReportActivity;
import com.termux.shared.packages.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;
@@ -162,6 +163,7 @@ public final class TermuxActivity extends Activity implements ServiceConnection
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;
@@ -597,7 +599,10 @@ public final class TermuxActivity extends Activity implements ServiceConnection
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);
@@ -625,6 +630,9 @@ public final class TermuxActivity extends Activity implements ServiceConnection
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;
@@ -654,6 +662,13 @@ public final class TermuxActivity extends Activity implements ServiceConnection
}
}
+ @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/TermuxTerminalSessionClient.java b/app/src/main/java/com/termux/app/terminal/TermuxTerminalSessionClient.java
index 852cb085..a9e60251 100644
--- a/app/src/main/java/com/termux/app/terminal/TermuxTerminalSessionClient.java
+++ b/app/src/main/java/com/termux/app/terminal/TermuxTerminalSessionClient.java
@@ -15,6 +15,7 @@ import android.widget.ListView;
import com.termux.R;
import com.termux.shared.shell.TermuxSession;
import com.termux.shared.interact.TextInputDialogUtils;
+import com.termux.shared.interact.ShareUtils;
import com.termux.app.TermuxActivity;
import com.termux.shared.terminal.TermuxTerminalSessionClientBase;
import com.termux.shared.termux.TermuxConstants;
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 55a104d9..279e3b04 100644
--- a/app/src/main/java/com/termux/app/terminal/TermuxTerminalViewClient.java
+++ b/app/src/main/java/com/termux/app/terminal/TermuxTerminalViewClient.java
@@ -652,6 +652,13 @@ public class TermuxTerminalViewClient extends TermuxTerminalViewClientBase {
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 d5335bf0..63199a97 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -65,6 +65,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 2f326c80..49925f81 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 @@ package com.termux.view;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
+import android.app.Activity;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
@@ -20,6 +21,7 @@ import android.view.HapticFeedbackConstants;
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;
@@ -31,6 +33,7 @@ import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.widget.Scroller;
+import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.termux.terminal.KeyHandler;
@@ -441,6 +444,14 @@ public final class TerminalView extends View {
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.
*
@@ -1211,6 +1222,25 @@ public final class TerminalView extends View {
}
}
+ /** 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 09e9ba22..714d56e4 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
@@ -12,6 +12,8 @@ import android.view.MenuItem;
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;
@@ -21,6 +23,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();
@@ -28,9 +31,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;
@@ -113,7 +116,7 @@ public class TextSelectionCursorController implements CursorController {
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;
}
@@ -132,7 +135,7 @@ public class TextSelectionCursorController implements CursorController {
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;
@@ -141,7 +144,13 @@ public class TextSelectionCursorController implements CursorController {
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;
}
@@ -356,6 +365,22 @@ public class TextSelectionCursorController implements CursorController {
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 c54f2615..22adab39 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
@@ -155,6 +155,7 @@ public class ShareUtils {
+ /**
* Open a url.
*
* @param context The context for operations.