1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-24 10:27:10 +02:00

Android: Fixes #7791: Sharing pictures to Joplin creates recurring duplications (#7807)

This commit is contained in:
javad mnjd 2023-02-19 21:53:00 +03:30 committed by GitHub
parent 21dbc800d5
commit 18a0ca0881
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 84 additions and 57 deletions

View File

@ -77,18 +77,8 @@
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- SHARE EXTENSION -->
<activity
android:noHistory="true"
android:name=".ShareActivity"
android:configChanges="orientation"
android:launchMode="singleTask"
android:label="@string/app_name"
android:excludeFromRecents="true"
android:theme="@style/AppTheme"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
@ -99,8 +89,8 @@
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="*/*" />
</intent-filter>
</activity>
<!-- /SHARE EXTENSION -->
</activity>
</application>

View File

@ -1,12 +0,0 @@
package net.cozic.joplin;
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

@ -14,6 +14,7 @@ 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.LifecycleEventListener;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
@ -21,6 +22,7 @@ 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.modules.core.DeviceEventManagerModule;
import com.facebook.react.uimanager.ViewManager;
import java.io.File;
@ -42,11 +44,14 @@ public class SharePackage implements ReactPackage {
return Collections.emptyList();
}
public static class ShareModule extends ReactContextBaseJavaModule implements ActivityEventListener {
public static class ShareModule extends ReactContextBaseJavaModule implements ActivityEventListener, LifecycleEventListener {
private boolean handledStartIntent = false;
private Intent receivedShareIntent = null;
ShareModule(@NonNull ReactApplicationContext reactContext) {
super(reactContext);
reactContext.addActivityEventListener(this);
reactContext.addLifecycleEventListener(this);
}
@Override
@ -55,6 +60,14 @@ public class SharePackage implements ReactPackage {
@Override
public void onNewIntent(Intent intent) {
if (intent == null || !(Intent.ACTION_SEND.equals(intent.getAction())
|| Intent.ACTION_SEND_MULTIPLE.equals(intent.getAction()))) {
return;
}
receivedShareIntent = intent;
this.getReactApplicationContext()
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit("new_share_intent", null);
}
@NonNull
@ -77,37 +90,29 @@ public class SharePackage implements ReactPackage {
}
private WritableMap processIntent() {
Activity currentActivity = getCurrentActivity();
WritableMap map = Arguments.createMap();
if (currentActivity == null) {
if (receivedShareIntent == null) {
return null;
}
Intent intent = currentActivity.getIntent();
if (intent == null || !(Intent.ACTION_SEND.equals(intent.getAction())
|| Intent.ACTION_SEND_MULTIPLE.equals(intent.getAction()))) {
if (receivedShareIntent.getBooleanExtra("is_processed", false)) {
return null;
}
if (intent.getBooleanExtra("is_processed", false)) {
return null;
}
String type = intent.getType() == null ? "" : intent.getType();
String type = receivedShareIntent.getType() == null ? "" : receivedShareIntent.getType();
map.putString("type", type);
map.putString("title", getTitle(intent));
map.putString("text", intent.getStringExtra(Intent.EXTRA_TEXT));
map.putString("title", getTitle(receivedShareIntent));
map.putString("text", receivedShareIntent.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)));
if (Intent.ACTION_SEND.equals(receivedShareIntent.getAction())) {
if (receivedShareIntent.hasExtra(Intent.EXTRA_STREAM)) {
resources.pushMap(getFileData(receivedShareIntent.getParcelableExtra(Intent.EXTRA_STREAM)));
}
} else if (Intent.ACTION_SEND_MULTIPLE.equals(intent.getAction())) {
ArrayList<Uri> imageUris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
} else if (Intent.ACTION_SEND_MULTIPLE.equals(receivedShareIntent.getAction())) {
ArrayList<Uri> imageUris = receivedShareIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
if (imageUris != null) {
for (Uri uri : imageUris) {
resources.pushMap(getFileData(uri));
@ -116,7 +121,7 @@ public class SharePackage implements ReactPackage {
}
map.putArray("resources", resources);
intent.putExtra("is_processed", true);
receivedShareIntent.putExtra("is_processed", true);
return map;
}
@ -185,5 +190,36 @@ public class SharePackage implements ReactPackage {
}
return ext;
}
@ReactMethod
public void addListener(String eventName) {
// Set up any upstream listeners or background tasks as necessary
}
@ReactMethod
public void removeListeners(Integer count) {
// Remove upstream listeners, stop unnecessary background tasks
}
@Override
public void onHostResume() {
if (this.getCurrentActivity() != null) {
Intent intent = this.getCurrentActivity().getIntent();
if (this.handledStartIntent) {
// sometimes onHostResume is fired after onNewIntent
// and we only care about the activity intent when the first time app opens
return;
}
this.handledStartIntent = true;
this.onNewIntent(intent);
}
}
@Override
public void onHostPause() {}
@Override
public void onHostDestroy() {
}
}
}

View File

@ -139,15 +139,7 @@ class NoteScreenComponent extends BaseScreenComponent {
}
if (this.state.fromShare) {
// effectively the same as NAV_BACK but NAV_BACK causes undesired behaviour in this case:
// - share to Joplin from some other app
// - open Joplin and open any note
// - go back -- with NAV_BACK this causes the app to exit rather than just showing notes
this.props.dispatch({
type: 'NAV_GO',
routeName: 'Notes',
folderId: this.state.note.parent_id,
});
ShareExtension.close();
return true;
}
@ -458,10 +450,6 @@ class NoteScreenComponent extends BaseScreenComponent {
shared.uninstallResourceHandling(this.refreshResource);
if (this.state.fromShare) {
ShareExtension.close();
}
this.saveActionQueue(this.state.note.id).processAllNow();
// It cannot theoretically be undefined, since componentDidMount should always be called before

View File

@ -735,6 +735,15 @@ class AppComponent extends React.Component {
}
};
this.handleNewShare_ = () => {
// look at this.handleOpenURL_ comment
if (this.props.biometricsDone) {
void this.handleShareData();
}
};
this.unsubscribeNewShareListener_ = ShareExtension.addShareListener(this.handleNewShare_);
this.handleScreenWidthChange_ = this.handleScreenWidthChange_.bind(this);
}
@ -840,6 +849,11 @@ class AppComponent extends React.Component {
}
if (this.unsubscribeNetInfoHandler_) this.unsubscribeNetInfoHandler_();
if (this.unsubscribeNewShareListener_) {
this.unsubscribeNewShareListener_();
this.unsubscribeNewShareListener_ = undefined;
}
}
public componentDidUpdate(prevProps: any) {

View File

@ -1,3 +1,5 @@
import { NativeEventEmitter } from 'react-native';
const { NativeModules, Platform } = require('react-native');
export interface SharedData {
@ -6,16 +8,25 @@ export interface SharedData {
resources?: string[];
}
let eventEmitter: NativeEventEmitter | undefined;
const ShareExtension = (NativeModules.ShareExtension) ?
{
data: () => NativeModules.ShareExtension.data(),
close: () => NativeModules.ShareExtension.close(),
shareURL: (Platform.OS === 'ios') ? NativeModules.ShareExtension.getConstants().SHARE_EXTENSION_SHARE_URL : '',
addShareListener: (Platform.OS === 'android') ? ((handler: (event: any)=> void) => {
if (!eventEmitter) {
eventEmitter = new NativeEventEmitter(NativeModules.ShareExtension);
}
return eventEmitter.addListener('new_share_intent', handler).remove;
}) : (() => {}),
} :
{
data: () => {},
close: () => {},
shareURL: '',
addShareListener: () => {},
};
export default ShareExtension;