diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 3be2fcbb..32c3957e 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -90,6 +90,14 @@
android:name="com.termux.app.TermuxService"
android:exported="false" />
+
+
+
+
diff --git a/app/src/main/java/com/termux/app/TermuxOpenReceiver.java b/app/src/main/java/com/termux/app/TermuxOpenReceiver.java
new file mode 100644
index 00000000..c5a9a2e0
--- /dev/null
+++ b/app/src/main/java/com/termux/app/TermuxOpenReceiver.java
@@ -0,0 +1,179 @@
+package com.termux.app;
+
+import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+import android.provider.MediaStore;
+import android.util.Log;
+import android.webkit.MimeTypeMap;
+
+import com.termux.terminal.EmulatorDebug;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+
+public class TermuxOpenReceiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final Uri data = intent.getData();
+ if (data == null) {
+ Log.e(EmulatorDebug.LOG_TAG, "termux-open: Called without intent data");
+ return;
+ }
+
+ final boolean isExternalUrl = data.getScheme() != null && !data.getScheme().equals("file");
+ if (isExternalUrl) {
+ Intent viewIntent = new Intent(Intent.ACTION_VIEW, data);
+ try {
+ context.startActivity(viewIntent);
+ } catch (ActivityNotFoundException e) {
+ Log.e(EmulatorDebug.LOG_TAG, "termux-open: No app handles the url " + data);
+ }
+ return;
+ }
+
+ final String filePath = data.getPath();
+ final String contentTypeExtra = intent.getStringExtra("content-type");
+ final boolean useChooser = intent.getBooleanExtra("chooser", false);
+ final String actionExtra = intent.getAction();
+
+ String intentAction = null;
+ if (actionExtra == null) {
+ intentAction = Intent.ACTION_VIEW;
+ } else {
+ switch (actionExtra) {
+ case "edit":
+ intentAction = Intent.ACTION_EDIT;
+ break;
+ case "send":
+ intentAction = Intent.ACTION_SEND;
+ break;
+ case "view":
+ intentAction = Intent.ACTION_VIEW;
+ break;
+ default:
+ Log.e(EmulatorDebug.LOG_TAG, "Invalid action '" + actionExtra + "', using 'view'");
+ break;
+ }
+ }
+
+ final File fileToShare = new File(filePath);
+ if (!(fileToShare.isFile() && fileToShare.canRead())) {
+ Log.e(EmulatorDebug.LOG_TAG, "termux-open: Not a readable file: '" + fileToShare.getAbsolutePath() + "'");
+ return;
+ }
+
+ Intent sendIntent = new Intent();
+ sendIntent.setAction(intentAction);
+ Uri uriToShare = Uri.withAppendedPath(Uri.parse("content://com.termux.files/"), filePath);
+ sendIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+ String contentTypeToUse;
+ if (contentTypeExtra == null) {
+ String fileName = fileToShare.getName();
+ int lastDotIndex = fileName.lastIndexOf('.');
+ String fileExtension = fileName.substring(lastDotIndex + 1, fileName.length());
+ MimeTypeMap mimeTypes = MimeTypeMap.getSingleton();
+ // Lower casing makes it work with e.g. "JPG":
+ contentTypeToUse = mimeTypes.getMimeTypeFromExtension(fileExtension.toLowerCase());
+ if (contentTypeToUse == null) contentTypeToUse = "application/octet-stream";
+ } else {
+ contentTypeToUse = contentTypeExtra;
+ }
+
+ sendIntent.putExtra(Intent.EXTRA_SUBJECT, fileToShare.getName());
+
+ if (Intent.ACTION_SEND.equals(intentAction)) {
+ sendIntent.putExtra(Intent.EXTRA_STREAM, uriToShare);
+ sendIntent.setType(contentTypeToUse);
+ } else {
+ sendIntent.setDataAndType(uriToShare, contentTypeToUse);
+ }
+
+ if (useChooser) {
+ sendIntent = Intent.createChooser(sendIntent, null).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ }
+
+ context.startActivity(sendIntent);
+ }
+
+ public static class ContentProvider extends android.content.ContentProvider {
+
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
+ File file = new File(uri.getPath());
+ String fileName = file.getName();
+
+ if (projection == null) {
+ projection = new String[]{
+ MediaStore.MediaColumns.DISPLAY_NAME,
+ MediaStore.MediaColumns.SIZE,
+ MediaStore.MediaColumns._ID
+ };
+ }
+
+ Object[] row = new Object[projection.length];
+ for (int i = 0; i < projection.length; i++) {
+ String column = projection[i];
+ Object value;
+ switch (column) {
+ case MediaStore.MediaColumns.DISPLAY_NAME:
+ value = file.getName();
+ break;
+ case MediaStore.MediaColumns.SIZE:
+ value = (int) file.length();
+ break;
+ case MediaStore.MediaColumns._ID:
+ value = 1;
+ break;
+ default:
+ value = null;
+ }
+ row[i] = value;
+ }
+
+ MatrixCursor cursor = new MatrixCursor(projection);
+ cursor.addRow(row);
+ return cursor;
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ return null;
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ return null;
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ return 0;
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ return 0;
+ }
+
+ @Override
+ public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
+ File file = new File(uri.getPath());
+ return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
+ }
+ }
+
+}