diff --git a/ReactNativeClient/android/app/src/main/AndroidManifest.xml b/ReactNativeClient/android/app/src/main/AndroidManifest.xml index eb803a6d2e..fd3be6cbeb 100644 --- a/ReactNativeClient/android/app/src/main/AndroidManifest.xml +++ b/ReactNativeClient/android/app/src/main/AndroidManifest.xml @@ -1,67 +1,109 @@ - - - + + + + - - - - - + + + + + - + + + + - - - - - - - + - + + + + + + + - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - diff --git a/ReactNativeClient/android/app/src/main/java/net/cozic/joplin/MainApplication.java b/ReactNativeClient/android/app/src/main/java/net/cozic/joplin/MainApplication.java index 7f86c7b415..1924a703c0 100644 --- a/ReactNativeClient/android/app/src/main/java/net/cozic/joplin/MainApplication.java +++ b/ReactNativeClient/android/app/src/main/java/net/cozic/joplin/MainApplication.java @@ -2,15 +2,18 @@ package net.cozic.joplin; import android.app.Application; import android.content.Context; +import android.database.CursorWindow; +import androidx.multidex.MultiDex; import com.facebook.react.PackageList; import com.facebook.react.ReactApplication; import com.facebook.react.ReactInstanceManager; import com.facebook.react.ReactNativeHost; import com.facebook.react.ReactPackage; import com.facebook.soloader.SoLoader; +import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.util.List; -import androidx.multidex.MultiDex; +import net.cozic.joplin.share.SharePackage; public class MainApplication extends Application implements ReactApplication { @@ -33,7 +36,7 @@ public class MainApplication extends Application implements ReactApplication { @SuppressWarnings("UnnecessaryLocalVariable") List packages = new PackageList(this).getPackages(); // Packages that cannot be autolinked yet can be added manually here, for example: - // packages.add(new MyReactNativePackage()); + packages.add(new SharePackage()); return packages; } @@ -51,6 +54,18 @@ public class MainApplication extends Application implements ReactApplication { @Override public void onCreate() { super.onCreate(); + + // To try to fix the error "Row too big to fit into CursorWindow" + // https://github.com/andpor/react-native-sqlite-storage/issues/364#issuecomment-526423153 + // https://github.com/laurent22/joplin/issues/1767#issuecomment-515617991 + try { + Field field = CursorWindow.class.getDeclaredField("sCursorWindowSize"); + field.setAccessible(true); + field.set(null, 50 * 1024 * 1024); //the 102400 is the new size added + } catch (Exception e) { + e.printStackTrace(); + } + SoLoader.init(this, /* native exopackage */ false); initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); } diff --git a/ReactNativeClient/android/app/src/main/java/net/cozic/joplin/share/ShareActivity.java b/ReactNativeClient/android/app/src/main/java/net/cozic/joplin/share/ShareActivity.java new file mode 100644 index 0000000000..4baf7b9f45 --- /dev/null +++ b/ReactNativeClient/android/app/src/main/java/net/cozic/joplin/share/ShareActivity.java @@ -0,0 +1,12 @@ +package net.cozic.joplin.share; + +import com.facebook.react.ReactActivity; + +public class ShareActivity extends ReactActivity { + @Override + protected String getMainComponentName() { + // this is the name AppRegistry will use to launch the Share View + return "Joplin"; + } +} + diff --git a/ReactNativeClient/android/app/src/main/java/net/cozic/joplin/share/SharePackage.java b/ReactNativeClient/android/app/src/main/java/net/cozic/joplin/share/SharePackage.java new file mode 100644 index 0000000000..ebb61102ef --- /dev/null +++ b/ReactNativeClient/android/app/src/main/java/net/cozic/joplin/share/SharePackage.java @@ -0,0 +1,184 @@ +package net.cozic.joplin.share; + +import android.app.Activity; +import android.content.ContentResolver; +import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; +import android.provider.OpenableColumns; +import android.util.Log; +import android.webkit.MimeTypeMap; + +import androidx.annotation.NonNull; + +import com.facebook.react.ReactPackage; +import com.facebook.react.bridge.ActivityEventListener; +import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.NativeModule; +import com.facebook.react.bridge.Promise; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReactContextBaseJavaModule; +import com.facebook.react.bridge.ReactMethod; +import com.facebook.react.bridge.WritableArray; +import com.facebook.react.bridge.WritableMap; +import com.facebook.react.uimanager.ViewManager; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class SharePackage implements ReactPackage { + + @NonNull + @Override + public List createNativeModules(@NonNull ReactApplicationContext reactContext) { + return Collections.singletonList(new ShareModule(reactContext)); + } + + @NonNull + @Override + public List createViewManagers(@NonNull ReactApplicationContext reactContext) { + return Collections.emptyList(); + } + + public static class ShareModule extends ReactContextBaseJavaModule implements ActivityEventListener { + + ShareModule(@NonNull ReactApplicationContext reactContext) { + super(reactContext); + reactContext.addActivityEventListener(this); + } + + @Override + public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) { + } + + @Override + public void onNewIntent(Intent intent) { + } + + @NonNull + @Override + public String getName() { + return "ShareExtension"; + } + + @ReactMethod + public void close() { + Activity currentActivity = getCurrentActivity(); + if (currentActivity != null) { + currentActivity.finish(); + } + } + + @ReactMethod + public void data(Promise promise) { + promise.resolve(processIntent()); + } + + private WritableMap processIntent() { + Activity currentActivity = getCurrentActivity(); + WritableMap map = Arguments.createMap(); + + if (currentActivity == null) { + return null; + } + + Intent intent = currentActivity.getIntent(); + + if (intent == null || !(Intent.ACTION_SEND.equals(intent.getAction()) + || Intent.ACTION_SEND_MULTIPLE.equals(intent.getAction()))) { + return null; + } + + String type = intent.getType() == null ? "" : intent.getType(); + map.putString("type", type); + map.putString("title", getTitle(intent)); + map.putString("text", intent.getStringExtra(Intent.EXTRA_TEXT)); + + WritableArray resources = Arguments.createArray(); + + if (Intent.ACTION_SEND.equals(intent.getAction())) { + if (intent.hasExtra(Intent.EXTRA_STREAM)) { + resources.pushMap(getFileData(intent.getParcelableExtra(Intent.EXTRA_STREAM))); + } + } else if (Intent.ACTION_SEND_MULTIPLE.equals(intent.getAction())) { + ArrayList imageUris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM); + if (imageUris != null) { + for (Uri uri : imageUris) { + resources.pushMap(getFileData(uri)); + } + } + } + + map.putArray("resources", resources); + return map; + } + + private String getTitle(Intent intent) { + if (intent.hasExtra(Intent.EXTRA_SUBJECT)) { + return intent.getStringExtra(Intent.EXTRA_SUBJECT); + } else if (intent.hasExtra(Intent.EXTRA_TITLE)) { + return intent.getStringExtra(Intent.EXTRA_TITLE); + } else { + return null; + } + } + + private WritableMap getFileData(Uri uri) { + Log.d("joplin", "getFileData: " + uri); + + WritableMap imageData = Arguments.createMap(); + + ContentResolver contentResolver = getCurrentActivity().getContentResolver(); + String mimeType = contentResolver.getType(uri); + String name = getFileName(uri, contentResolver); + + if (mimeType == null || mimeType.equals("image/*")) { + String extension = getFileExtension(name); + mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); + } + + imageData.putString("uri", uri.toString()); + imageData.putString("name", name); + imageData.putString("mimeType", mimeType); + return imageData; + } + + private String getFileName(Uri uri, ContentResolver contentResolver) { + if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) { + File file = new File(uri.getPath()); + return file.getName(); + } else if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) { + String name = null; + Cursor cursor = contentResolver.query(uri, null, null, null, null); + if (cursor != null) { + try { + if (cursor.moveToFirst()) { + int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); + name = cursor.getString(nameIndex); + } + } finally { + cursor.close(); + } + } + return name; + } else { + Log.w("joplin", "Unknown URI scheme: " + uri.getScheme()); + return null; + } + } + + private String getFileExtension(String file) { + if (file == null) { + return null; + } + String ext = null; + int i = file.lastIndexOf('.'); + if (i > 0) { + ext = file.substring(i + 1); + } + return ext; + } + } +}