mirror of https://github.com/termux/termux-app
Merge pull request #2146 from trygveaa/click-on-url
Added: Allow users to directly open URL links in terminal transcript when clicked or tapped The user can add `terminal-onclick-url-open=true` entry to `termux.properties` file to enable opening of URL links in terminal transcript when clicked or tapped. The default value is `false`. Running `termux-reload-settings` command will also update the behaviour instantaneously if changed. Implemented in #2146
This commit is contained in:
commit
af16e79bf8
|
@ -25,6 +25,7 @@ import com.termux.app.TermuxActivity;
|
||||||
import com.termux.shared.data.UrlUtils;
|
import com.termux.shared.data.UrlUtils;
|
||||||
import com.termux.shared.file.FileUtils;
|
import com.termux.shared.file.FileUtils;
|
||||||
import com.termux.shared.interact.MessageDialogUtils;
|
import com.termux.shared.interact.MessageDialogUtils;
|
||||||
|
import com.termux.shared.interact.ShareUtils;
|
||||||
import com.termux.shared.shell.ShellUtils;
|
import com.termux.shared.shell.ShellUtils;
|
||||||
import com.termux.shared.terminal.TermuxTerminalViewClientBase;
|
import com.termux.shared.terminal.TermuxTerminalViewClientBase;
|
||||||
import com.termux.shared.terminal.io.extrakeys.SpecialButton;
|
import com.termux.shared.terminal.io.extrakeys.SpecialButton;
|
||||||
|
@ -42,6 +43,7 @@ import com.termux.shared.termux.TermuxUtils;
|
||||||
import com.termux.shared.view.KeyboardUtils;
|
import com.termux.shared.view.KeyboardUtils;
|
||||||
import com.termux.shared.view.ViewUtils;
|
import com.termux.shared.view.ViewUtils;
|
||||||
import com.termux.terminal.KeyHandler;
|
import com.termux.terminal.KeyHandler;
|
||||||
|
import com.termux.terminal.TerminalBuffer;
|
||||||
import com.termux.terminal.TerminalEmulator;
|
import com.termux.terminal.TerminalEmulator;
|
||||||
import com.termux.terminal.TerminalSession;
|
import com.termux.terminal.TerminalSession;
|
||||||
|
|
||||||
|
@ -172,10 +174,26 @@ public class TermuxTerminalViewClient extends TermuxTerminalViewClientBase {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSingleTapUp(MotionEvent e) {
|
public void onSingleTapUp(MotionEvent e) {
|
||||||
if (!KeyboardUtils.areDisableSoftKeyboardFlagsSet(mActivity))
|
TerminalEmulator term = mActivity.getCurrentSession().getEmulator();
|
||||||
KeyboardUtils.showSoftKeyboard(mActivity, mActivity.getTerminalView());
|
|
||||||
else
|
if (mActivity.getProperties().shouldOpenTerminalTranscriptURLOnClick()) {
|
||||||
Logger.logVerbose(LOG_TAG, "Not showing soft keyboard onSingleTapUp since its disabled");
|
int[] columnAndRow = mActivity.getTerminalView().getColumnAndRow(e, true);
|
||||||
|
String wordAtTap = term.getScreen().getWordAtLocation(columnAndRow[0], columnAndRow[1]);
|
||||||
|
LinkedHashSet<CharSequence> urlSet = UrlUtils.extractUrls(wordAtTap);
|
||||||
|
|
||||||
|
if (!urlSet.isEmpty()) {
|
||||||
|
String url = (String) urlSet.iterator().next();
|
||||||
|
ShareUtils.openURL(mActivity, url);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!term.isMouseTrackingActive() && !e.isFromSource(InputDevice.SOURCE_MOUSE)) {
|
||||||
|
if (!KeyboardUtils.areDisableSoftKeyboardFlagsSet(mActivity))
|
||||||
|
KeyboardUtils.showSoftKeyboard(mActivity, mActivity.getTerminalView());
|
||||||
|
else
|
||||||
|
Logger.logVerbose(LOG_TAG, "Not showing soft keyboard onSingleTapUp since its disabled");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -670,13 +688,7 @@ public class TermuxTerminalViewClient extends TermuxTerminalViewClientBase {
|
||||||
lv.setOnItemLongClickListener((parent, view, position, id) -> {
|
lv.setOnItemLongClickListener((parent, view, position, id) -> {
|
||||||
dialog.dismiss();
|
dialog.dismiss();
|
||||||
String url = (String) urls[position];
|
String url = (String) urls[position];
|
||||||
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
ShareUtils.openURL(mActivity, url);
|
||||||
try {
|
|
||||||
mActivity.startActivity(i, null);
|
|
||||||
} catch (ActivityNotFoundException e) {
|
|
||||||
// If no applications match, Android displays a system message.
|
|
||||||
mActivity.startActivity(Intent.createChooser(i, null));
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -102,6 +102,45 @@ public final class TerminalBuffer {
|
||||||
return builder.toString();
|
return builder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getWordAtLocation(int x, int y) {
|
||||||
|
// Set y1 and y2 to the lines where the wrapped line starts and ends.
|
||||||
|
// I.e. if a line that is wrapped to 3 lines starts at line 4, and this
|
||||||
|
// is called with y=5, then y1 would be set to 4 and y2 would be set to 6.
|
||||||
|
int y1 = y;
|
||||||
|
int y2 = y;
|
||||||
|
while (y1 > 0 && !getSelectedText(0, y1 - 1, mColumns, y, true, true).contains("\n")) {
|
||||||
|
y1--;
|
||||||
|
}
|
||||||
|
while (y2 < mScreenRows && !getSelectedText(0, y, mColumns, y2 + 1, true, true).contains("\n")) {
|
||||||
|
y2++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the text for the whole wrapped line
|
||||||
|
String text = getSelectedText(0, y1, mColumns, y2, true, true);
|
||||||
|
// The index of x in text
|
||||||
|
int textOffset = (y - y1) * mColumns + x;
|
||||||
|
|
||||||
|
if (textOffset >= text.length()) {
|
||||||
|
// The click was to the right of the last word on the line, so
|
||||||
|
// there's no word to return
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set x1 and x2 to the indices of the last space before x and the
|
||||||
|
// first space after x in text respectively
|
||||||
|
int x1 = text.lastIndexOf(' ', textOffset);
|
||||||
|
int x2 = text.indexOf(' ', textOffset);
|
||||||
|
if (x2 == -1) {
|
||||||
|
x2 = text.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x1 == x2) {
|
||||||
|
// The click was on a space, so there's no word to return
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return text.substring(x1 + 1, x2);
|
||||||
|
}
|
||||||
|
|
||||||
public int getActiveTranscriptRows() {
|
public int getActiveTranscriptRows() {
|
||||||
return mActiveTranscriptRows;
|
return mActiveTranscriptRows;
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,4 +45,21 @@ public class ScreenBufferTest extends TerminalTestCase {
|
||||||
withTerminalSized(5, 3).enterString("ABC\r\nFG");
|
withTerminalSized(5, 3).enterString("ABC\r\nFG");
|
||||||
assertEquals("ABC\nFG", mTerminal.getScreen().getSelectedText(0, 0, 1, 1, true, true));
|
assertEquals("ABC\nFG", mTerminal.getScreen().getSelectedText(0, 0, 1, 1, true, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testGetWordAtLocation() {
|
||||||
|
withTerminalSized(5, 3).enterString("ABCDEFGHIJ\r\nKLMNO");
|
||||||
|
assertEquals("ABCDEFGHIJKLMNO", mTerminal.getScreen().getWordAtLocation(0, 0));
|
||||||
|
assertEquals("ABCDEFGHIJKLMNO", mTerminal.getScreen().getWordAtLocation(4, 1));
|
||||||
|
assertEquals("ABCDEFGHIJKLMNO", mTerminal.getScreen().getWordAtLocation(4, 2));
|
||||||
|
|
||||||
|
withTerminalSized(5, 3).enterString("ABC DEF GHI ");
|
||||||
|
assertEquals("ABC", mTerminal.getScreen().getWordAtLocation(0, 0));
|
||||||
|
assertEquals("", mTerminal.getScreen().getWordAtLocation(3, 0));
|
||||||
|
assertEquals("DEF", mTerminal.getScreen().getWordAtLocation(4, 0));
|
||||||
|
assertEquals("DEF", mTerminal.getScreen().getWordAtLocation(0, 1));
|
||||||
|
assertEquals("DEF", mTerminal.getScreen().getWordAtLocation(1, 1));
|
||||||
|
assertEquals("GHI", mTerminal.getScreen().getWordAtLocation(0, 2));
|
||||||
|
assertEquals("", mTerminal.getScreen().getWordAtLocation(1, 2));
|
||||||
|
assertEquals("", mTerminal.getScreen().getWordAtLocation(2, 2));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,7 +94,7 @@ public final class TerminalView extends View {
|
||||||
@Override
|
@Override
|
||||||
public boolean onUp(MotionEvent event) {
|
public boolean onUp(MotionEvent event) {
|
||||||
mScrollRemainder = 0.0f;
|
mScrollRemainder = 0.0f;
|
||||||
if (mEmulator != null && mEmulator.isMouseTrackingActive() && !isSelectingText() && !scrolledWithFinger) {
|
if (mEmulator != null && mEmulator.isMouseTrackingActive() && !event.isFromSource(InputDevice.SOURCE_MOUSE) && !isSelectingText() && !scrolledWithFinger) {
|
||||||
// Quick event processing when mouse tracking is active - do not wait for check of double tapping
|
// Quick event processing when mouse tracking is active - do not wait for check of double tapping
|
||||||
// for zooming.
|
// for zooming.
|
||||||
sendMouseEventCode(event, TerminalEmulator.MOUSE_LEFT_BUTTON, true);
|
sendMouseEventCode(event, TerminalEmulator.MOUSE_LEFT_BUTTON, true);
|
||||||
|
@ -114,13 +114,8 @@ public final class TerminalView extends View {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
requestFocus();
|
requestFocus();
|
||||||
if (!mEmulator.isMouseTrackingActive()) {
|
mClient.onSingleTapUp(event);
|
||||||
if (!event.isFromSource(InputDevice.SOURCE_MOUSE)) {
|
return true;
|
||||||
mClient.onSingleTapUp(event);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -471,10 +466,31 @@ public final class TerminalView extends View {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the zero indexed column and row of the terminal view for the
|
||||||
|
* position of the event.
|
||||||
|
*
|
||||||
|
* @param event The event with the position to get the column and row for.
|
||||||
|
* @param relativeToScroll If true the column number will take the scroll
|
||||||
|
* position into account. E.g. if scrolled 3 lines up and the event
|
||||||
|
* position is in the top left, column will be -3 if relativeToScroll is
|
||||||
|
* true and 0 if relativeToScroll is false.
|
||||||
|
* @return Array with the column and row.
|
||||||
|
*/
|
||||||
|
public int[] getColumnAndRow(MotionEvent event, boolean relativeToScroll) {
|
||||||
|
int column = (int) (event.getX() / mRenderer.mFontWidth);
|
||||||
|
int row = (int) ((event.getY() - mRenderer.mFontLineSpacingAndAscent) / mRenderer.mFontLineSpacing);
|
||||||
|
if (relativeToScroll) {
|
||||||
|
row += mTopRow;
|
||||||
|
}
|
||||||
|
return new int[] { column, row };
|
||||||
|
}
|
||||||
|
|
||||||
/** Send a single mouse event code to the terminal. */
|
/** Send a single mouse event code to the terminal. */
|
||||||
void sendMouseEventCode(MotionEvent e, int button, boolean pressed) {
|
void sendMouseEventCode(MotionEvent e, int button, boolean pressed) {
|
||||||
int x = (int) (e.getX() / mRenderer.mFontWidth) + 1;
|
int[] columnAndRow = getColumnAndRow(e, false);
|
||||||
int y = (int) ((e.getY() - mRenderer.mFontLineSpacingAndAscent) / mRenderer.mFontLineSpacing) + 1;
|
int x = columnAndRow[0] + 1;
|
||||||
|
int y = columnAndRow[1] + 1;
|
||||||
if (pressed && (button == TerminalEmulator.MOUSE_WHEELDOWN_BUTTON || button == TerminalEmulator.MOUSE_WHEELUP_BUTTON)) {
|
if (pressed && (button == TerminalEmulator.MOUSE_WHEELDOWN_BUTTON || button == TerminalEmulator.MOUSE_WHEELUP_BUTTON)) {
|
||||||
if (mMouseStartDownTime == e.getDownTime()) {
|
if (mMouseStartDownTime == e.getDownTime()) {
|
||||||
x = mMouseScrollStartX;
|
x = mMouseScrollStartX;
|
||||||
|
@ -550,7 +566,6 @@ public final class TerminalView extends View {
|
||||||
sendMouseEventCode(event, TerminalEmulator.MOUSE_LEFT_BUTTON_MOVED, true);
|
sendMouseEventCode(event, TerminalEmulator.MOUSE_LEFT_BUTTON_MOVED, true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -89,14 +89,9 @@ public class TextSelectionCursorController implements CursorController {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setInitialTextSelectionPosition(MotionEvent event) {
|
public void setInitialTextSelectionPosition(MotionEvent event) {
|
||||||
int cx = (int) (event.getX() / terminalView.mRenderer.getFontWidth());
|
int[] columnAndRow = terminalView.getColumnAndRow(event, true);
|
||||||
final boolean eventFromMouse = event.isFromSource(InputDevice.SOURCE_MOUSE);
|
mSelX1 = mSelX2 = columnAndRow[0];
|
||||||
// Offset for finger:
|
mSelY1 = mSelY2 = columnAndRow[1];
|
||||||
final int SELECT_TEXT_OFFSET_Y = eventFromMouse ? 0 : -40;
|
|
||||||
int cy = (int) ((event.getY() + SELECT_TEXT_OFFSET_Y) / terminalView.mRenderer.getFontLineSpacing()) + terminalView.getTopRow();
|
|
||||||
|
|
||||||
mSelX1 = mSelX2 = cx;
|
|
||||||
mSelY1 = mSelY2 = cy;
|
|
||||||
|
|
||||||
TerminalBuffer screen = terminalView.mEmulator.getScreen();
|
TerminalBuffer screen = terminalView.mEmulator.getScreen();
|
||||||
if (!" ".equals(screen.getSelectedText(mSelX1, mSelY1, mSelX1, mSelY1))) {
|
if (!" ".equals(screen.getSelectedText(mSelX1, mSelY1, mSelX1, mSelY1))) {
|
||||||
|
|
Loading…
Reference in New Issue