1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-11-27 08:21:03 +02:00

Restored sharing

This commit is contained in:
Laurent Cozic 2020-10-14 18:20:00 +01:00
parent 0bdb449e72
commit dff59f5603
4 changed files with 310 additions and 57 deletions

View File

@ -1,67 +1,109 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.cozic.joplin">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!-- RN-NOTIFICATION -->
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<!-- /RN-NOTIFICATION -->
<!-- RN-NOTIFICATION -->
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<!-- /RN-NOTIFICATION -->
<application
android:name=".MainApplication"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:allowBackup="false"
android:networkSecurityConfig="@xml/network_security_config"
android:theme="@style/AppTheme">
<!-- Make these features optional to enable Chromebooks -->
<!-- https://github.com/laurent22/joplin/issues/37 -->
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
<!-- RN-NOTIFICATION -->
<receiver
android:name="com.emekalites.react.alarm.notification.AlarmReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="ACTION_DISMISS" />
<action android:name="ACTION_SNOOZE" />
</intent-filter>
</receiver>
<application
android:name=".MainApplication"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:allowBackup="false"
android:networkSecurityConfig="@xml/network_security_config"
android:theme="@style/AppTheme">
<receiver
android:name="com.emekalites.react.alarm.notification.AlarmDismissReceiver"
android:enabled="true"
android:exported="true" />
<!-- RN-NOTIFICATION -->
<receiver
android:name="com.emekalites.react.alarm.notification.AlarmReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="ACTION_DISMISS" />
<action android:name="ACTION_SNOOZE" />
</intent-filter>
</receiver>
<receiver
android:name="com.emekalites.react.alarm.notification.AlarmBootReceiver"
android:directBootAware="true"
android:enabled="false"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
<action android:name="com.htc.intent.action.QUICKBOOT_POWERON" />
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
</intent-filter>
</receiver>
<!-- /RN-NOTIFICATION -->
<receiver
android:name="com.emekalites.react.alarm.notification.AlarmDismissReceiver"
android:enabled="true"
android:exported="true" />
<receiver
android:name="com.emekalites.react.alarm.notification.AlarmBootReceiver"
android:directBootAware="true"
android:enabled="false"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
<action android:name="com.htc.intent.action.QUICKBOOT_POWERON" />
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
</intent-filter>
</receiver>
<!-- /RN-NOTIFICATION -->
<!--
2018-12-16: Changed android:launchMode from "singleInstance" to "singleTop" for Firebase notification
Previously singleInstance was necessary to prevent multiple instance of the RN app from running at the same time, but maybe no longer needed.
2020-10-06: Changed back again to "singleInstance" and notifications still seem to work. Changing to singleInstance
to try to fix this bug: https://discourse.joplinapp.org/t/joplin-android-app-looses-nextcloud-sync-settings/10997/6
Users would lose their settings, and it's possibly due to multiple instances of the app running at the same time, perhaps
due to sharing with the app. When checking the log, it would show "saving settings", then the app startup message, and after the app
has started, it would show "setting saved". So basically the app, or one instance of it, has started while settings were being saved
2020-10-08: Changed back again to "singleTop" as it has worked so far. The multiple instance bug was "fixed" in a different way
See ReactNativeClient/root.js for more info about the bug.
-->
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
android:launchMode="singleTop"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
<!-- SHARE EXTENSION -->
<activity
android:noHistory="true"
android:name=".share.ShareActivity"
android:configChanges="orientation"
android:label="@string/app_name"
android:excludeFromRecents="true"
android:theme="@style/AppTheme">
<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>
<!-- /SHARE EXTENSION -->
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
</application>
</manifest>

View File

@ -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<ReactPackage> 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());
}

View File

@ -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";
}
}

View File

@ -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<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactContext) {
return Collections.singletonList(new ShareModule(reactContext));
}
@NonNull
@Override
public List<ViewManager> 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<Uri> 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;
}
}
}