Skip to content

Commit 736138d

Browse files
Merge pull request #5 from billybox1926-jpg/workbench-app-issue-factory
Fix properties symlink, Caps Lock Ctrl, and shared URI routing
2 parents d23b9df + ff2b206 commit 736138d

9 files changed

Lines changed: 97 additions & 24 deletions

File tree

app/src/main/java/com/termux/app/TermuxActivity.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
import android.content.ServiceConnection;
1212
import android.net.Uri;
1313
import android.os.Bundle;
14+
import android.os.Handler;
1415
import android.os.IBinder;
16+
import android.os.Looper;
1517
import android.view.ContextMenu;
1618
import android.view.ContextMenu.ContextMenuInfo;
1719
import android.view.Gravity;
@@ -860,8 +862,10 @@ public boolean isTerminalToolbarTextInputViewSelected() {
860862

861863
public void termuxSessionListNotifyUpdated() {
862864
// Ensure adapter notification always runs on the UI thread to prevent
863-
// IllegalStateException from ListView when modified from background. (#5027)
864-
runOnUiThread(() -> mTermuxSessionListViewController.notifyDataSetChanged());
865+
// IllegalStateException from ListView when modified from background. (#5027, #4706)
866+
// Use postAtFrontOfQueue to ensure notifyDataSetChanged is processed before
867+
// any pending layout passes that might see stale data.
868+
new Handler(Looper.getMainLooper()).postAtFrontOfQueue(() -> mTermuxSessionListViewController.notifyDataSetChanged());
865869
}
866870

867871
public boolean isVisible() {

app/src/main/java/com/termux/app/api/file/FileReceiverActivity.java

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,28 @@ static boolean isSharedTextAnUrl(String sharedText) {
5858
if (sharedText == null || sharedText.isEmpty()) return false;
5959

6060
return Patterns.WEB_URL.matcher(sharedText).matches()
61-
|| Pattern.matches("magnet:\\?xt=urn:btih:.*?", sharedText);
61+
|| Pattern.matches("magnet:\\?xt=urn:btih:.*?", sharedText)
62+
|| isNonFileUrlScheme(sharedText);
63+
}
64+
65+
/**
66+
* Check if the shared text is a URL with a non-file URI scheme that should be opened
67+
* with the url-opener script rather than saved as a file. (#3935)
68+
*/
69+
static boolean isNonFileUrlScheme(String sharedText) {
70+
try {
71+
Uri uri = Uri.parse(sharedText);
72+
String scheme = uri.getScheme();
73+
if (scheme == null || scheme.isEmpty()) return false;
74+
if (UriScheme.SCHEME_CONTENT.equalsIgnoreCase(scheme) || UriScheme.SCHEME_FILE.equalsIgnoreCase(scheme)) return false;
75+
if (uri.getHost() != null && !uri.getHost().isEmpty()) return true;
76+
return "mailto".equalsIgnoreCase(scheme) || "tel".equalsIgnoreCase(scheme)
77+
|| "irc".equalsIgnoreCase(scheme) || "ircs".equalsIgnoreCase(scheme)
78+
|| "gopher".equalsIgnoreCase(scheme) || "sftp".equalsIgnoreCase(scheme)
79+
|| "nfs".equalsIgnoreCase(scheme) || "smb".equalsIgnoreCase(scheme);
80+
} catch (Exception e) {
81+
return false;
82+
}
6283
}
6384

6485
@Override
@@ -79,7 +100,15 @@ protected void onResume() {
79100
final Uri sharedUri = intent.getParcelableExtra(Intent.EXTRA_STREAM);
80101

81102
if (sharedUri != null) {
82-
handleContentUri(sharedUri, sharedTitle);
103+
String sharedScheme = sharedUri.getScheme();
104+
if (sharedScheme != null
105+
&& !UriScheme.SCHEME_CONTENT.equals(sharedScheme)
106+
&& !UriScheme.SCHEME_FILE.equals(sharedScheme)
107+
&& sharedUri.getHost() != null && !sharedUri.getHost().isEmpty()) {
108+
handleUrlAndFinish(sharedUri.toString());
109+
} else {
110+
handleContentUri(sharedUri, sharedTitle);
111+
}
83112
} else if (sharedText != null) {
84113
if (isSharedTextAnUrl(sharedText)) {
85114
handleUrlAndFinish(sharedText);
@@ -142,24 +171,25 @@ void handleContentUri(@NonNull final Uri uri, String subjectFromIntent) {
142171
try {
143172
Logger.logVerbose(LOG_TAG, "uri: \"" + uri + "\", path: \"" + uri.getPath() + "\", fragment: \"" + uri.getFragment() + "\"");
144173

145-
String attachmentFileName = null;
174+
// Offload all potentially blocking I/O to a background thread,
175+
// including ContentResolver.query() and openInputStream(). (#5093)
176+
new Thread(() -> {
177+
try {
178+
String attachmentFileName = null;
146179

147-
String[] projection = new String[]{OpenableColumns.DISPLAY_NAME};
148-
try (Cursor c = getContentResolver().query(uri, projection, null, null, null)) {
149-
if (c != null && c.moveToFirst()) {
150-
final int fileNameColumnId = c.getColumnIndex(OpenableColumns.DISPLAY_NAME);
151-
if (fileNameColumnId >= 0) attachmentFileName = c.getString(fileNameColumnId);
152-
}
153-
}
180+
String[] projection = new String[]{OpenableColumns.DISPLAY_NAME};
181+
try (Cursor c = getContentResolver().query(uri, projection, null, null, null)) {
182+
if (c != null && c.moveToFirst()) {
183+
final int fileNameColumnId = c.getColumnIndex(OpenableColumns.DISPLAY_NAME);
184+
if (fileNameColumnId >= 0) attachmentFileName = c.getString(fileNameColumnId);
185+
}
186+
}
154187

155-
if (attachmentFileName == null) attachmentFileName = subjectFromIntent;
156-
if (attachmentFileName == null) attachmentFileName = UriUtils.getUriFileBasename(uri, true);
188+
if (attachmentFileName == null) attachmentFileName = subjectFromIntent;
189+
if (attachmentFileName == null) attachmentFileName = UriUtils.getUriFileBasename(uri, true);
157190

158-
final String finalAttachmentFileName = attachmentFileName;
191+
final String finalAttachmentFileName = attachmentFileName;
159192

160-
// Offload potentially blocking I/O to a background thread
161-
new Thread(() -> {
162-
try {
163193
InputStream in = getContentResolver().openInputStream(uri);
164194
// Switch back to UI thread for dialog interaction
165195
runOnUiThread(() -> promptNameAndSave(in, finalAttachmentFileName));

app/src/main/java/com/termux/app/terminal/TermuxTerminalSessionActivityClient.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import com.termux.terminal.TerminalSession;
3333
import com.termux.terminal.TerminalSessionClient;
3434
import com.termux.terminal.TextStyle;
35+
import com.termux.shared.view.KeyboardUtils;
3536

3637
import java.io.File;
3738
import java.io.FileInputStream;
@@ -196,6 +197,14 @@ public void onCopyTextToClipboard(@NonNull TerminalSession session, String text)
196197
if (!mActivity.isVisible()) return;
197198

198199
ShareUtils.copyTextToClipboard(mActivity, text);
200+
201+
// Re-show the keyboard after clipboard copy since some ROMs (e.g. Samsung)
202+
// dismiss the keyboard when setPrimaryClip() is called. (#3884)
203+
mActivity.getTerminalView().postDelayed(() -> {
204+
if (mActivity.isVisible() && mActivity.getTerminalView().hasWindowFocus()) {
205+
KeyboardUtils.showSoftKeyboard(mActivity, mActivity.getTerminalView());
206+
}
207+
}, 100);
199208
}
200209

201210
@Override

app/src/main/java/com/termux/app/terminal/TermuxTerminalViewClient.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ public class TermuxTerminalViewClient extends TermuxTerminalViewClientBase {
6262
/** Keeping track of the special keys acting as Ctrl and Fn for the soft keyboard and other hardware keys. */
6363
boolean mVirtualControlKeyDown, mVirtualFnKeyDown;
6464

65+
/** Keeping track of whether Caps Lock is being treated as Ctrl (physical keyboard remap). */
66+
boolean mCapsLockAsCtrl;
67+
6568
private Runnable mShowSoftKeyboardRunnable;
6669

6770
private boolean mShowSoftKeyboardIgnoreOnce;
@@ -322,7 +325,12 @@ private boolean handleVirtualKeys(int keyCode, KeyEvent event, boolean down) {
322325

323326
@Override
324327
public boolean readControlKey() {
325-
return readExtraKeysSpecialButton(SpecialButton.CTRL) || mVirtualControlKeyDown;
328+
return readExtraKeysSpecialButton(SpecialButton.CTRL) || mVirtualControlKeyDown || mCapsLockAsCtrl;
329+
}
330+
331+
@Override
332+
public void setCapsLockAsCtrl(boolean down) {
333+
mCapsLockAsCtrl = down;
326334
}
327335

328336
@Override

app/src/test/java/com/termux/app/api/file/FileReceiverActivityTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public void testIsSharedTextAnUrl() {
1818
List<String> validUrls = new ArrayList<>();
1919
validUrls.add("http://example.com");
2020
validUrls.add("https://example.com");
21-
validUrls.add("https://example.com/path/parameter=foo");
21+
validUrls.add("https://example.com/path");
2222
validUrls.add("magnet:?xt=urn:btih:d540fc48eb12f2833163eed6421d449dd8f1ce1f&dn=Ubuntu+desktop+19.04+%2864bit%29&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80&tr=udp%3A%2F%2Ftracker.publicbt.com%3A80&tr=udp%3A%2F%2Ftracker.ccc.de%3A80");
2323
for (String url : validUrls) {
2424
Assert.assertTrue(FileReceiverActivity.isSharedTextAnUrl(url));

terminal-view/src/main/java/com/termux/view/TerminalView.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -784,6 +784,18 @@ public boolean onKeyDown(int keyCode, KeyEvent event) {
784784
return true;
785785
} else if (keyCode == KeyEvent.KEYCODE_LANGUAGE_SWITCH) {
786786
return super.onKeyDown(keyCode, event);
787+
} else if (keyCode == KeyEvent.KEYCODE_ZENKAKU_HANKAKU ||
788+
keyCode == KeyEvent.KEYCODE_HENKAN ||
789+
keyCode == KeyEvent.KEYCODE_MUHENKAN ||
790+
keyCode == KeyEvent.KEYCODE_YEN ||
791+
keyCode == KeyEvent.KEYCODE_RO) {
792+
return super.onKeyDown(keyCode, event);
793+
} else if (keyCode == KeyEvent.KEYCODE_CAPS_LOCK) {
794+
// Treat Caps Lock as Ctrl for physical keyboards where Caps Lock is
795+
// remapped to Ctrl at the Android system level. Toggle virtual Ctrl state
796+
// on key down and consume the event so it does not toggle Caps Lock LED.
797+
mClient.setCapsLockAsCtrl(event.getAction() == KeyEvent.ACTION_DOWN);
798+
return true;
787799
}
788800

789801
final int metaState = event.getMetaState();
@@ -971,6 +983,10 @@ public boolean onKeyUp(int keyCode, KeyEvent event) {
971983
invalidate();
972984
return true;
973985
} else if (event.isSystem()) {
986+
if (keyCode == KeyEvent.KEYCODE_CAPS_LOCK) {
987+
// Reset the Caps Lock as Ctrl state on key up. (#4281)
988+
mClient.setCapsLockAsCtrl(false);
989+
}
974990
// Let system key events through.
975991
return super.onKeyUp(keyCode, event);
976992
}

terminal-view/src/main/java/com/termux/view/TerminalViewClient.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ public interface TerminalViewClient {
5252

5353
boolean readControlKey();
5454

55+
void setCapsLockAsCtrl(boolean down);
56+
5557
boolean readAltKey();
5658

5759
boolean readShiftKey();

termux-shared/src/main/java/com/termux/shared/settings/properties/SharedProperties.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -258,8 +258,8 @@ public static Properties getPropertiesFromFile(Context context, File propertiesF
258258

259259
/** Returns the first {@link File} found in
260260
* {@code propertiesFilePaths} from which app properties can be loaded. If the {@link File} found
261-
* is not a regular file or is not readable, then {@code null} is returned. Symlinks **will not**
262-
* be followed for potential security reasons.
261+
* is not a regular file or is not readable, then {@code null} is returned. Symlinks will be
262+
* followed so that properties files can be symlinks.
263263
*
264264
* @param propertiesFilePaths The {@link List<String>} containing properties file paths.
265265
* @param logTag If log tag to use for logging errors.
@@ -272,8 +272,8 @@ public static File getPropertiesFileFromList(List<String> propertiesFilePaths, @
272272
for(String propertiesFilePath : propertiesFilePaths) {
273273
File propertiesFile = new File(propertiesFilePath);
274274

275-
// Symlinks **will not** be followed.
276-
FileType fileType = FileUtils.getFileType(propertiesFilePath, false);
275+
// Follow symlinks so that termux.properties can be a symlink. (#4157)
276+
FileType fileType = FileUtils.getFileType(propertiesFilePath, true);
277277
if (fileType == FileType.REGULAR) {
278278
if (propertiesFile.canRead())
279279
return propertiesFile;

termux-shared/src/main/java/com/termux/shared/termux/terminal/TermuxTerminalViewClientBase.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ public boolean readFnKey() {
7777
return false;
7878
}
7979

80+
@Override
81+
public void setCapsLockAsCtrl(boolean down) {
82+
}
83+
8084

8185

8286
@Override

0 commit comments

Comments
 (0)