feat: On Android, you can now share text & files from other apps directly into Standard Notes (#2352)
This commit is contained in:
@@ -43,6 +43,16 @@
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="*/*" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND_MULTIPLE" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="*/*" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.standardnotes;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.content.res.Configuration;
|
||||
@@ -54,4 +55,10 @@ public class MainActivity extends ReactActivity {
|
||||
public void invokeDefaultOnBackPressed() {
|
||||
moveTaskToBack(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
setIntent(intent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ public class MainApplication extends Application implements ReactApplication {
|
||||
|
||||
packages.add(new Fido2ApiPackage());
|
||||
packages.add(new CustomWebViewPackage());
|
||||
packages.add(new ReceiveSharingIntentPackage());
|
||||
|
||||
return packages;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
// Adapted from
|
||||
// https://github.com/ajith-ab/react-native-receive-sharing-intent
|
||||
|
||||
package com.standardnotes;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Application;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.provider.OpenableColumns;
|
||||
|
||||
import com.facebook.react.bridge.Promise;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.bridge.WritableNativeMap;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Objects;
|
||||
|
||||
public class ReceiveSharingIntentHelper {
|
||||
|
||||
private Context context;
|
||||
|
||||
public ReceiveSharingIntentHelper(Application context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public void sendFileNames(Context context, Intent intent, Promise promise) {
|
||||
try {
|
||||
String action = intent.getAction();
|
||||
String type = intent.getType();
|
||||
if (type == null) {
|
||||
return;
|
||||
}
|
||||
if (!type.startsWith("text") && (Objects.equals(action, Intent.ACTION_SEND) || Objects.equals(action, Intent.ACTION_SEND_MULTIPLE))) {
|
||||
WritableMap files = getMediaUris(intent, context);
|
||||
if (files == null) return;
|
||||
promise.resolve(files);
|
||||
}
|
||||
else if (type.startsWith("text") && Objects.equals(action, Intent.ACTION_SEND)) {
|
||||
String text = null;
|
||||
String subject = null;
|
||||
try {
|
||||
text = intent.getStringExtra(Intent.EXTRA_TEXT);
|
||||
subject = intent.getStringExtra(Intent.EXTRA_SUBJECT);
|
||||
} catch (Exception ignored) {}
|
||||
WritableMap files;
|
||||
if (text == null) {
|
||||
files = getMediaUris(intent, context);
|
||||
if (files == null) return;
|
||||
}
|
||||
else {
|
||||
files = new WritableNativeMap();
|
||||
WritableMap file = new WritableNativeMap();
|
||||
file.putString("contentUri", null);
|
||||
file.putString("fileName", null);
|
||||
file.putString("extension", null);
|
||||
if (text.startsWith("http")) {
|
||||
file.putString("weblink", text);
|
||||
file.putString("text", null);
|
||||
} else {
|
||||
file.putString("weblink", null);
|
||||
file.putString("text", text);
|
||||
}
|
||||
file.putString("subject", subject);
|
||||
files.putMap("0", file);
|
||||
}
|
||||
promise.resolve(files);
|
||||
}
|
||||
else if (Objects.equals(action, Intent.ACTION_VIEW)) {
|
||||
String link = intent.getDataString();
|
||||
WritableMap files = new WritableNativeMap();
|
||||
WritableMap file = new WritableNativeMap();
|
||||
file.putString("contentUri", null);
|
||||
file.putString("mimeType", null);
|
||||
file.putString("text", null);
|
||||
file.putString("weblink", link);
|
||||
file.putString("fileName", null);
|
||||
file.putString("extension", null);
|
||||
files.putMap("0", file);
|
||||
promise.resolve(files);
|
||||
}
|
||||
else if (Objects.equals(action, Intent.ACTION_PROCESS_TEXT)) {
|
||||
String text = null;
|
||||
try {
|
||||
text = intent.getStringExtra(Intent.EXTRA_PROCESS_TEXT);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
WritableMap files = new WritableNativeMap();
|
||||
WritableMap file = new WritableNativeMap();
|
||||
file.putString("contentUri", null);
|
||||
file.putString("fileName", null);
|
||||
file.putString("extension", null);
|
||||
file.putString("weblink", null);
|
||||
file.putString("text", text);
|
||||
files.putMap("0", file);
|
||||
promise.resolve(files);
|
||||
}
|
||||
else {
|
||||
promise.reject("error", "Invalid file type.");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
promise.reject("error", e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressLint("Range")
|
||||
public WritableMap getMediaUris(Intent intent, Context context) {
|
||||
if (intent == null) return null;
|
||||
|
||||
String subject = null;
|
||||
try {
|
||||
subject = intent.getStringExtra(Intent.EXTRA_SUBJECT);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
WritableMap files = new WritableNativeMap();
|
||||
if (Objects.equals(intent.getAction(), Intent.ACTION_SEND)) {
|
||||
WritableMap file = new WritableNativeMap();
|
||||
Uri contentUri = intent.getParcelableExtra(Intent.EXTRA_STREAM);
|
||||
if (contentUri == null) return null;
|
||||
ContentResolver contentResolver = context.getContentResolver();
|
||||
file.putString("mimeType", contentResolver.getType(contentUri));
|
||||
Cursor queryResult = contentResolver.query(contentUri, null, null, null, null);
|
||||
queryResult.moveToFirst();
|
||||
file.putString("fileName", queryResult.getString(queryResult.getColumnIndex(OpenableColumns.DISPLAY_NAME)));
|
||||
file.putString("contentUri", contentUri.toString());
|
||||
file.putString("text", null);
|
||||
file.putString("weblink", null);
|
||||
file.putString("subject", subject);
|
||||
files.putMap("0", file);
|
||||
queryResult.close();
|
||||
} else if (Objects.equals(intent.getAction(), Intent.ACTION_SEND_MULTIPLE)) {
|
||||
ArrayList<Uri> contentUris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
|
||||
if (contentUris != null) {
|
||||
int index = 0;
|
||||
for (Uri uri : contentUris) {
|
||||
WritableMap file = new WritableNativeMap();
|
||||
ContentResolver contentResolver = context.getContentResolver();
|
||||
// Based on https://developer.android.com/training/secure-file-sharing/retrieve-info
|
||||
file.putString("mimeType", contentResolver.getType(uri));
|
||||
Cursor queryResult = contentResolver.query(uri, null, null, null, null);
|
||||
queryResult.moveToFirst();
|
||||
file.putString("fileName", queryResult.getString(queryResult.getColumnIndex(OpenableColumns.DISPLAY_NAME)));
|
||||
file.putString("contentUri", uri.toString());
|
||||
file.putString("text", null);
|
||||
file.putString("weblink", null);
|
||||
file.putString("subject", subject);
|
||||
files.putMap(Integer.toString(index), file);
|
||||
queryResult.close();
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
public void clearFileNames(Intent intent) {
|
||||
String type = intent.getType();
|
||||
if (type == null) return;
|
||||
if (type.startsWith("text")) {
|
||||
intent.removeExtra(Intent.EXTRA_TEXT);
|
||||
} else if (type.startsWith("image") || type.startsWith("video") || type.startsWith("application")) {
|
||||
intent.removeExtra(Intent.EXTRA_STREAM);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
// Adapted from
|
||||
// https://github.com/ajith-ab/react-native-receive-sharing-intent
|
||||
|
||||
package com.standardnotes;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.content.Intent;
|
||||
|
||||
|
||||
import android.os.Build;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import com.facebook.react.bridge.Promise;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
|
||||
|
||||
public class ReceiveSharingIntentModule extends ReactContextBaseJavaModule {
|
||||
public final String Log_Tag = "ReceiveSharingIntent";
|
||||
|
||||
private final ReactApplicationContext reactContext;
|
||||
private ReceiveSharingIntentHelper receiveSharingIntentHelper;
|
||||
|
||||
public ReceiveSharingIntentModule(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
this.reactContext = reactContext;
|
||||
Application applicationContext = (Application) reactContext.getApplicationContext();
|
||||
receiveSharingIntentHelper = new ReceiveSharingIntentHelper(applicationContext);
|
||||
}
|
||||
|
||||
|
||||
protected void onNewIntent(Intent intent) {
|
||||
Activity mActivity = getCurrentActivity();
|
||||
if(mActivity == null) { return; }
|
||||
mActivity.setIntent(intent);
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
|
||||
@ReactMethod
|
||||
public void getFileNames(Promise promise){
|
||||
Activity mActivity = getCurrentActivity();
|
||||
if(mActivity == null) { return; }
|
||||
Intent intent = mActivity.getIntent();
|
||||
if(intent == null) { return; }
|
||||
receiveSharingIntentHelper.sendFileNames(reactContext, intent, promise);
|
||||
mActivity.setIntent(null);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void clearFileNames(){
|
||||
Activity mActivity = getCurrentActivity();
|
||||
if(mActivity == null) { return; }
|
||||
Intent intent = mActivity.getIntent();
|
||||
if(intent == null) { return; }
|
||||
receiveSharingIntentHelper.clearFileNames(intent);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "ReceiveSharingIntent";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.standardnotes;
|
||||
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.bridge.NativeModule;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.uimanager.ViewManager;
|
||||
import com.standardnotes.ReceiveSharingIntentModule;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class ReceiveSharingIntentPackage implements ReactPackage {
|
||||
@Override
|
||||
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
|
||||
List<NativeModule> modules = new ArrayList<>();
|
||||
|
||||
modules.add(new ReceiveSharingIntentModule(reactContext));
|
||||
|
||||
return modules;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user