From a90e02927f980c59f0e123d01bbe886dbad1c418 Mon Sep 17 00:00:00 2001 From: Filip Stanis Date: Thu, 14 Oct 2021 01:02:39 +0100 Subject: [PATCH 1/2] (rebase) adds "recent notes" widget for Android --- .eslintignore | 4 + .gitignore | 4 + .../android/app/src/main/AndroidManifest.xml | 17 ++++ .../net/cozic/joplin/MainApplication.java | 2 + .../net/cozic/joplin/widgets/WidgetData.java | 80 +++++++++++++++++ .../joplin/widgets/WidgetDataPackage.java | 29 +++++++ .../widgets/recents/RecentsWidgetData.java | 78 +++++++++++++++++ .../recents/RecentsWidgetProvider.java | 79 +++++++++++++++++ .../widgets/recents/RecentsWidgetService.java | 85 +++++++++++++++++++ .../src/main/res/layout/recents_widget.xml | 22 +++++ .../main/res/layout/recents_widget_item.xml | 9 ++ .../app/src/main/res/values/dimens.xml | 4 + .../app/src/main/res/values/strings.xml | 1 + .../res/xml/recents_widget_provider_info.xml | 7 ++ packages/app-mobile/root.tsx | 38 +++++++++ packages/app-mobile/utils/RecentsWidget.ts | 20 +++++ .../app-mobile/utils/updateRecentsWidget.ts | 15 ++++ 17 files changed, 494 insertions(+) create mode 100644 packages/app-mobile/android/app/src/main/java/net/cozic/joplin/widgets/WidgetData.java create mode 100644 packages/app-mobile/android/app/src/main/java/net/cozic/joplin/widgets/WidgetDataPackage.java create mode 100644 packages/app-mobile/android/app/src/main/java/net/cozic/joplin/widgets/recents/RecentsWidgetData.java create mode 100644 packages/app-mobile/android/app/src/main/java/net/cozic/joplin/widgets/recents/RecentsWidgetProvider.java create mode 100644 packages/app-mobile/android/app/src/main/java/net/cozic/joplin/widgets/recents/RecentsWidgetService.java create mode 100644 packages/app-mobile/android/app/src/main/res/layout/recents_widget.xml create mode 100644 packages/app-mobile/android/app/src/main/res/layout/recents_widget_item.xml create mode 100644 packages/app-mobile/android/app/src/main/res/values/dimens.xml create mode 100644 packages/app-mobile/android/app/src/main/res/xml/recents_widget_provider_info.xml create mode 100644 packages/app-mobile/utils/RecentsWidget.ts create mode 100644 packages/app-mobile/utils/updateRecentsWidget.ts diff --git a/.eslintignore b/.eslintignore index af0a6c8bcbf..b8f2a85dd6c 100644 --- a/.eslintignore +++ b/.eslintignore @@ -858,6 +858,10 @@ packages/app-mobile/utils/setupNotifications.js.map packages/app-mobile/utils/shareHandler.d.ts packages/app-mobile/utils/shareHandler.js packages/app-mobile/utils/shareHandler.js.map +packages/app-mobile/utils/RecentsWidget.js +packages/app-mobile/utils/RecentsWidget.js.map +packages/app-mobile/utils/updateRecentsWidget.js +packages/app-mobile/utils/updateRecentsWidget.js.map packages/fork-htmlparser2/src/CollectingHandler.d.ts packages/fork-htmlparser2/src/CollectingHandler.js packages/fork-htmlparser2/src/CollectingHandler.js.map diff --git a/.gitignore b/.gitignore index 5e4f054e984..31fa1d24a29 100644 --- a/.gitignore +++ b/.gitignore @@ -841,6 +841,10 @@ packages/app-mobile/utils/setupNotifications.js.map packages/app-mobile/utils/shareHandler.d.ts packages/app-mobile/utils/shareHandler.js packages/app-mobile/utils/shareHandler.js.map +packages/app-mobile/utils/RecentsWidget.js +packages/app-mobile/utils/RecentsWidget.js.map +packages/app-mobile/utils/updateRecentsWidget.js +packages/app-mobile/utils/updateRecentsWidget.js.map packages/fork-htmlparser2/src/CollectingHandler.d.ts packages/fork-htmlparser2/src/CollectingHandler.js packages/fork-htmlparser2/src/CollectingHandler.js.map diff --git a/packages/app-mobile/android/app/src/main/AndroidManifest.xml b/packages/app-mobile/android/app/src/main/AndroidManifest.xml index e76de4e9ba8..819a9a5c22c 100644 --- a/packages/app-mobile/android/app/src/main/AndroidManifest.xml +++ b/packages/app-mobile/android/app/src/main/AndroidManifest.xml @@ -61,8 +61,25 @@ + + + + + + + + + + + + + + getPackages() { packages.add(new SharePackage()); packages.add(new SslPackage()); packages.add(new TextInputPackage()); + packages.add(new WidgetDataPackage()); return packages; } diff --git a/packages/app-mobile/android/app/src/main/java/net/cozic/joplin/widgets/WidgetData.java b/packages/app-mobile/android/app/src/main/java/net/cozic/joplin/widgets/WidgetData.java new file mode 100644 index 00000000000..4a8f26f452b --- /dev/null +++ b/packages/app-mobile/android/app/src/main/java/net/cozic/joplin/widgets/WidgetData.java @@ -0,0 +1,80 @@ +package net.cozic.joplin.widgets; + +import android.content.Context; +import android.content.SharedPreferences; + +import androidx.annotation.NonNull; + +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 org.json.JSONException; +import org.json.JSONObject; + +public abstract class WidgetData { + private static final String NAME = "widget_data"; + private SharedPreferences sharedPreferences; + + protected Context context; + + protected abstract String getKey(); + + protected WidgetData(Context context) { + this.context = context; + sharedPreferences = context.getSharedPreferences(NAME, Context.MODE_PRIVATE); + } + + protected JSONObject readJSON() { + try { + return new JSONObject(readString()); + } catch (JSONException e) { + return new JSONObject(); + } + } + + protected void writeJSON(JSONObject value) { + writeString(value.toString()); + } + + protected String readString() { + return sharedPreferences.getString(getKey(), "{}"); + } + + protected void writeString(String value) { + sharedPreferences.edit().putString(getKey(), value).apply(); + } + + public ReactModule createReactModule(String name) { + return new ReactModule((ReactApplicationContext) context, name, this); + } + + private final static class ReactModule extends ReactContextBaseJavaModule { + private String name; + private WidgetData widgetData; + + private ReactModule(@NonNull ReactApplicationContext reactContext, String name, WidgetData widgetData) { + super(reactContext); + this.name = name; + this.widgetData = widgetData; + } + + @NonNull + @Override + public String getName() { + return name; + } + + @ReactMethod + public void write(String value) { + widgetData.writeString(value); + } + + @ReactMethod + public void read(Promise promise) { + promise.resolve(widgetData.readString()); + } + + } +} diff --git a/packages/app-mobile/android/app/src/main/java/net/cozic/joplin/widgets/WidgetDataPackage.java b/packages/app-mobile/android/app/src/main/java/net/cozic/joplin/widgets/WidgetDataPackage.java new file mode 100644 index 00000000000..af7d3ac4efb --- /dev/null +++ b/packages/app-mobile/android/app/src/main/java/net/cozic/joplin/widgets/WidgetDataPackage.java @@ -0,0 +1,29 @@ +package net.cozic.joplin.widgets; + +import androidx.annotation.NonNull; + +import com.facebook.react.ReactPackage; +import com.facebook.react.bridge.NativeModule; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.uimanager.ViewManager; + +import net.cozic.joplin.widgets.recents.RecentsWidgetData; + +import java.util.Collections; +import java.util.List; + +public class WidgetDataPackage implements ReactPackage { + @NonNull + @Override + public List createNativeModules(@NonNull ReactApplicationContext reactContext) { + return Collections.singletonList( + new RecentsWidgetData(reactContext).createReactModule("RecentsWidget") + ); + } + + @NonNull + @Override + public List createViewManagers(@NonNull ReactApplicationContext reactContext) { + return Collections.emptyList(); + } +} diff --git a/packages/app-mobile/android/app/src/main/java/net/cozic/joplin/widgets/recents/RecentsWidgetData.java b/packages/app-mobile/android/app/src/main/java/net/cozic/joplin/widgets/recents/RecentsWidgetData.java new file mode 100644 index 00000000000..99260474476 --- /dev/null +++ b/packages/app-mobile/android/app/src/main/java/net/cozic/joplin/widgets/recents/RecentsWidgetData.java @@ -0,0 +1,78 @@ +package net.cozic.joplin.widgets.recents; + +import android.content.Context; +import android.content.Intent; + +import net.cozic.joplin.widgets.WidgetData; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class RecentsWidgetData extends WidgetData { + public RecentsWidgetData(Context context) { + super(context); + } + + private void broadcastUpdate() { + Intent intent = new Intent(context, RecentsWidgetProvider.class); + intent.setAction(RecentsWidgetProvider.UPDATE_ACTION); + context.sendBroadcast(intent); + } + + public List readRecents() { + JSONObject data = readJSON(); + try { + JSONArray notes = data.getJSONArray("notes"); + List result = new ArrayList<>(notes.length()); + for (int i = 0; i < notes.length(); i++) { + result.add(NoteItem.fromJSONObject(notes.getJSONObject(i))); + } + return result; + } catch (JSONException e) { + return Collections.emptyList(); + } + } + + @Override + protected void writeString(String value) { + super.writeString(value); + broadcastUpdate(); + } + + @Override + protected String getKey() { + return "RecentsWidget"; + } + + public static final class NoteItem { + private String id; + private String title; + + public static NoteItem fromJSONObject(JSONObject obj) throws JSONException { + return new NoteItem(obj.getString("id"), obj.getString("title")); + } + + public NoteItem(String id, String title) { + this.id = id; + this.title = title; + } + + public String getId() { + return id; + } + + public String getTitle() { + return title; + } + + @Override + public int hashCode() { + return getId().hashCode(); + } + } +} diff --git a/packages/app-mobile/android/app/src/main/java/net/cozic/joplin/widgets/recents/RecentsWidgetProvider.java b/packages/app-mobile/android/app/src/main/java/net/cozic/joplin/widgets/recents/RecentsWidgetProvider.java new file mode 100644 index 00000000000..3d0b94e0292 --- /dev/null +++ b/packages/app-mobile/android/app/src/main/java/net/cozic/joplin/widgets/recents/RecentsWidgetProvider.java @@ -0,0 +1,79 @@ +package net.cozic.joplin.widgets.recents; + +import android.app.PendingIntent; +import android.appwidget.AppWidgetManager; +import android.appwidget.AppWidgetProvider; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.widget.RemoteViews; + +import net.cozic.joplin.R; + +public class RecentsWidgetProvider extends AppWidgetProvider { + public static final String CLICK_ACTION = "RECENTS_WIDGET_CLICK_ACTION"; + public static final String UPDATE_ACTION = "RECENTS_WIDGET_UPDATE_ACTION"; + + public static final String NOTE_ID = "RECENTS_WIDGET_NOTE_ID"; + + private static final int listViewId = R.id.list_view; + + private void handleClick(Context context, String noteId) { + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("joplin://notes/" + noteId)); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + } + + private void handleUpdate(Context context) { + AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); + int[] ids = appWidgetManager.getAppWidgetIds(new ComponentName(context, getClass())); + appWidgetManager.notifyAppWidgetViewDataChanged(ids, listViewId); + } + + @Override + public void onReceive(Context context, Intent intent) { + super.onReceive(context, intent); + String action = intent.getAction(); + if (action == null) { + return; + } + switch (action) { + case CLICK_ACTION: + String noteId = intent.getStringExtra(NOTE_ID); + if (noteId != null) { + handleClick(context, noteId); + } + break; + case UPDATE_ACTION: + handleUpdate(context); + break; + } + } + + @Override + public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { + for (int appWidgetId : appWidgetIds) { + RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.recents_widget); + rv.setRemoteAdapter(listViewId, widgetIntent(context, appWidgetId)); + rv.setEmptyView(listViewId, R.id.empty_view); + rv.setPendingIntentTemplate(listViewId, pendingIntentTemplate(context, appWidgetId)); + appWidgetManager.updateAppWidget(appWidgetId, rv); + } + super.onUpdate(context, appWidgetManager, appWidgetIds); + } + + private Intent widgetIntent(Context context, int appWidgetId) { + Intent intent = new Intent(context, RecentsWidgetService.class); + intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); + intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME))); + return intent; + } + + private PendingIntent pendingIntentTemplate(Context context, int appWidgetId) { + Intent intent = new Intent(context, RecentsWidgetProvider.class); + intent.setAction(RecentsWidgetProvider.CLICK_ACTION); + intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); + return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); + } +} diff --git a/packages/app-mobile/android/app/src/main/java/net/cozic/joplin/widgets/recents/RecentsWidgetService.java b/packages/app-mobile/android/app/src/main/java/net/cozic/joplin/widgets/recents/RecentsWidgetService.java new file mode 100644 index 00000000000..a2e7b3247f7 --- /dev/null +++ b/packages/app-mobile/android/app/src/main/java/net/cozic/joplin/widgets/recents/RecentsWidgetService.java @@ -0,0 +1,85 @@ +package net.cozic.joplin.widgets.recents; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.widget.RemoteViews; +import android.widget.RemoteViewsService; + +import net.cozic.joplin.R; + +import java.util.List; + +public class RecentsWidgetService extends RemoteViewsService { + @Override + public RemoteViewsFactory onGetViewFactory(Intent intent) { + return new RecentsWidgetDataViewsFactory(getApplicationContext(), intent); + } + + private static class RecentsWidgetDataViewsFactory implements RemoteViewsService.RemoteViewsFactory { + private List notes; + private Context context; + private RecentsWidgetData recentsWidgetData; + + public RecentsWidgetDataViewsFactory(Context context, Intent intent) { + this.context = context; + recentsWidgetData = new RecentsWidgetData(context); + } + + @Override + public void onCreate() { + notes = recentsWidgetData.readRecents(); + } + + @Override + public void onDataSetChanged() { + notes = recentsWidgetData.readRecents(); + } + + @Override + public RemoteViews getViewAt(int position) { + RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.recents_widget_item); + rv.setTextViewText(R.id.recents_widget_item, notes.get(position).getTitle()); + rv.setOnClickFillInIntent(R.id.recents_widget_item, fillInIntent(notes.get(position).getId())); + return rv; + } + + private Intent fillInIntent(String noteId) { + Bundle extras = new Bundle(); + extras.putString(RecentsWidgetProvider.NOTE_ID, noteId); + Intent intent = new Intent(); + intent.putExtras(extras); + return intent; + } + + @Override + public void onDestroy() { + notes.clear(); + } + + @Override + public int getCount() { + return notes.size(); + } + + @Override + public RemoteViews getLoadingView() { + return null; + } + + @Override + public int getViewTypeCount() { + return 1; + } + + @Override + public boolean hasStableIds() { + return true; + } + + @Override + public long getItemId(int position) { + return notes.get(position).hashCode(); + } + } +} diff --git a/packages/app-mobile/android/app/src/main/res/layout/recents_widget.xml b/packages/app-mobile/android/app/src/main/res/layout/recents_widget.xml new file mode 100644 index 00000000000..332dbc24dc8 --- /dev/null +++ b/packages/app-mobile/android/app/src/main/res/layout/recents_widget.xml @@ -0,0 +1,22 @@ + + + + + + + diff --git a/packages/app-mobile/android/app/src/main/res/layout/recents_widget_item.xml b/packages/app-mobile/android/app/src/main/res/layout/recents_widget_item.xml new file mode 100644 index 00000000000..4b26de13649 --- /dev/null +++ b/packages/app-mobile/android/app/src/main/res/layout/recents_widget_item.xml @@ -0,0 +1,9 @@ + + diff --git a/packages/app-mobile/android/app/src/main/res/values/dimens.xml b/packages/app-mobile/android/app/src/main/res/values/dimens.xml new file mode 100644 index 00000000000..1c00c58ca6a --- /dev/null +++ b/packages/app-mobile/android/app/src/main/res/values/dimens.xml @@ -0,0 +1,4 @@ + + 16sp + 8dp + \ No newline at end of file diff --git a/packages/app-mobile/android/app/src/main/res/values/strings.xml b/packages/app-mobile/android/app/src/main/res/values/strings.xml index f89aea2b78e..d71c88e95d4 100644 --- a/packages/app-mobile/android/app/src/main/res/values/strings.xml +++ b/packages/app-mobile/android/app/src/main/res/values/strings.xml @@ -1,4 +1,5 @@ Joplin net.cozic.joplin.notification + No recent notes found diff --git a/packages/app-mobile/android/app/src/main/res/xml/recents_widget_provider_info.xml b/packages/app-mobile/android/app/src/main/res/xml/recents_widget_provider_info.xml new file mode 100644 index 00000000000..047cb4ddd95 --- /dev/null +++ b/packages/app-mobile/android/app/src/main/res/xml/recents_widget_provider_info.xml @@ -0,0 +1,7 @@ + + diff --git a/packages/app-mobile/root.tsx b/packages/app-mobile/root.tsx index 376bf10e0ae..1574e2a2f4c 100644 --- a/packages/app-mobile/root.tsx +++ b/packages/app-mobile/root.tsx @@ -20,6 +20,7 @@ import PoorManIntervals from '@joplin/lib/PoorManIntervals'; import reducer from '@joplin/lib/reducer'; import ShareExtension from './utils/ShareExtension'; import handleShared from './utils/shareHandler'; +import updateRecentsWidget from './utils/updateRecentsWidget'; import uuid from '@joplin/lib/uuid'; import { loadKeychainServiceAndSettings } from '@joplin/lib/services/SettingUtils'; import KeychainServiceDriverMobile from '@joplin/lib/services/keychain/KeychainServiceDriver.mobile'; @@ -698,6 +699,8 @@ class AppComponent extends React.Component { this.handleOpenURL_ = (event: any) => { if (event.url == ShareExtension.shareURL) { void this.handleShareData(); + } else { + void this.navigate(event.url); } }; } @@ -752,6 +755,8 @@ class AppComponent extends React.Component { }); } + Linking.getInitialURL().then((url: string) => this.navigate(url)); + Linking.addEventListener('url', this.handleOpenURL_); BackButtonService.initialize(this.backButtonHandler_); @@ -770,6 +775,8 @@ class AppComponent extends React.Component { await setupNotifications(this.props.dispatch); + await updateRecentsWidget(); + // Setting.setValue('encryption.masterPassword', 'WRONG'); // setTimeout(() => NavService.go('EncryptionConfig'), 2000); } @@ -896,6 +903,37 @@ class AppComponent extends React.Component { ); } + + private async navigate(url: string) { + if (!url) return; + let parsedUrl; + try { + parsedUrl = new URL(url); + } catch (_) { + // invalid URL + return; + } + reg.logger().info(`navigate to ${url}`); + + await this.props.dispatch({ type: 'NAV_BACK' }); + await this.props.dispatch({ type: 'SIDE_MENU_CLOSE' }); + const { pathname } = parsedUrl; + if (pathname.startsWith('//notes/')) { + const noteId = pathname.replace('//notes/', ''); + await this.props.dispatch({ + type: 'NAV_GO', + noteId, + routeName: 'Note', + }); + } else if (pathname.startsWith('//folder/')) { + const folderId = pathname.replace('//folder/', ''); + await this.props.dispatch({ + type: 'NAV_GO', + folderId, + routeName: 'Folder', + }); + } + } } const mapStateToProps = (state: any) => { diff --git a/packages/app-mobile/utils/RecentsWidget.ts b/packages/app-mobile/utils/RecentsWidget.ts new file mode 100644 index 00000000000..b6c7f5365ae --- /dev/null +++ b/packages/app-mobile/utils/RecentsWidget.ts @@ -0,0 +1,20 @@ +const { NativeModules, Platform } = require('react-native'); + +export interface NoteItem { + id: string; + title: string; +} + +interface WidgetData { + notes?: NoteItem[]; +} + +export const RecentsWidget = (Platform.OS === 'android' && NativeModules.RecentsWidget) ? + { + read: async (): Promise => JSON.parse(await NativeModules.RecentsWidget.read()), + write: async (data: WidgetData) => NativeModules.RecentsWidget.write(JSON.stringify(data)), + } : + { + read: async (): Promise => ({}), + write: async (_: WidgetData) => {}, + }; diff --git a/packages/app-mobile/utils/updateRecentsWidget.ts b/packages/app-mobile/utils/updateRecentsWidget.ts new file mode 100644 index 00000000000..1cc86b60f55 --- /dev/null +++ b/packages/app-mobile/utils/updateRecentsWidget.ts @@ -0,0 +1,15 @@ +import { RecentsWidget } from './RecentsWidget'; +import Note from '@joplin/lib/models/Note'; + +const MAX_COUNT = 10; + +export default async () => { + const recents = await Note.all({ + fields: ['id', 'title'], + order: [{ by: 'updated_time', dir: 'DESC' }], + limit: MAX_COUNT, + }); + return RecentsWidget.write({ + notes: recents, + }); +}; From d1ebb36fc7504f1a466cb6641c640dde8a977121 Mon Sep 17 00:00:00 2001 From: Filip Stanis Date: Sat, 20 Nov 2021 18:02:56 +0000 Subject: [PATCH 2/2] Removes deep link logic, moves widget updating to 'itemChange' events --- .eslintignore | 4 +- .gitignore | 4 +- .../android/app/src/main/AndroidManifest.xml | 6 --- .../recents/RecentsWidgetProvider.java | 2 +- packages/app-mobile/root.tsx | 51 +++++-------------- packages/app-mobile/utils/WidgetUtils.ts | 32 ++++++++++++ .../app-mobile/utils/updateRecentsWidget.ts | 15 ------ 7 files changed, 49 insertions(+), 65 deletions(-) create mode 100644 packages/app-mobile/utils/WidgetUtils.ts delete mode 100644 packages/app-mobile/utils/updateRecentsWidget.ts diff --git a/.eslintignore b/.eslintignore index b8f2a85dd6c..69b64a1f097 100644 --- a/.eslintignore +++ b/.eslintignore @@ -860,8 +860,8 @@ packages/app-mobile/utils/shareHandler.js packages/app-mobile/utils/shareHandler.js.map packages/app-mobile/utils/RecentsWidget.js packages/app-mobile/utils/RecentsWidget.js.map -packages/app-mobile/utils/updateRecentsWidget.js -packages/app-mobile/utils/updateRecentsWidget.js.map +packages/app-mobile/utils/WidgetUtils.js +packages/app-mobile/utils/WidgetUtils.js.map packages/fork-htmlparser2/src/CollectingHandler.d.ts packages/fork-htmlparser2/src/CollectingHandler.js packages/fork-htmlparser2/src/CollectingHandler.js.map diff --git a/.gitignore b/.gitignore index 31fa1d24a29..7d4720810d4 100644 --- a/.gitignore +++ b/.gitignore @@ -843,8 +843,8 @@ packages/app-mobile/utils/shareHandler.js packages/app-mobile/utils/shareHandler.js.map packages/app-mobile/utils/RecentsWidget.js packages/app-mobile/utils/RecentsWidget.js.map -packages/app-mobile/utils/updateRecentsWidget.js -packages/app-mobile/utils/updateRecentsWidget.js.map +packages/app-mobile/utils/WidgetUtils.js +packages/app-mobile/utils/WidgetUtils.js.map packages/fork-htmlparser2/src/CollectingHandler.d.ts packages/fork-htmlparser2/src/CollectingHandler.js packages/fork-htmlparser2/src/CollectingHandler.js.map diff --git a/packages/app-mobile/android/app/src/main/AndroidManifest.xml b/packages/app-mobile/android/app/src/main/AndroidManifest.xml index 819a9a5c22c..de692b95156 100644 --- a/packages/app-mobile/android/app/src/main/AndroidManifest.xml +++ b/packages/app-mobile/android/app/src/main/AndroidManifest.xml @@ -61,12 +61,6 @@ - - - - - - diff --git a/packages/app-mobile/android/app/src/main/java/net/cozic/joplin/widgets/recents/RecentsWidgetProvider.java b/packages/app-mobile/android/app/src/main/java/net/cozic/joplin/widgets/recents/RecentsWidgetProvider.java index 3d0b94e0292..848d0920f98 100644 --- a/packages/app-mobile/android/app/src/main/java/net/cozic/joplin/widgets/recents/RecentsWidgetProvider.java +++ b/packages/app-mobile/android/app/src/main/java/net/cozic/joplin/widgets/recents/RecentsWidgetProvider.java @@ -20,7 +20,7 @@ public class RecentsWidgetProvider extends AppWidgetProvider { private static final int listViewId = R.id.list_view; private void handleClick(Context context, String noteId) { - Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("joplin://notes/" + noteId)); + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("joplin://x-callback-url/openNote?id=" + noteId)); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); } diff --git a/packages/app-mobile/root.tsx b/packages/app-mobile/root.tsx index 1574e2a2f4c..a21bb37cbf2 100644 --- a/packages/app-mobile/root.tsx +++ b/packages/app-mobile/root.tsx @@ -6,9 +6,10 @@ import setupQuickActions from './setupQuickActions'; import PluginAssetsLoader from './PluginAssetsLoader'; import AlarmService from '@joplin/lib/services/AlarmService'; import Alarm from '@joplin/lib/models/Alarm'; +import eventManager from '@joplin/lib/eventManager'; import time from '@joplin/lib/time'; import Logger, { TargetType } from '@joplin/lib/Logger'; -import BaseModel from '@joplin/lib/BaseModel'; +import BaseModel, { ModelType } from '@joplin/lib/BaseModel'; import BaseService from '@joplin/lib/services/BaseService'; import ResourceService from '@joplin/lib/services/ResourceService'; import KvStore from '@joplin/lib/services/KvStore'; @@ -20,7 +21,7 @@ import PoorManIntervals from '@joplin/lib/PoorManIntervals'; import reducer from '@joplin/lib/reducer'; import ShareExtension from './utils/ShareExtension'; import handleShared from './utils/shareHandler'; -import updateRecentsWidget from './utils/updateRecentsWidget'; +import { updateRecentsWidgetWithDebounce } from './utils/WidgetUtils'; import uuid from '@joplin/lib/uuid'; import { loadKeychainServiceAndSettings } from '@joplin/lib/services/SettingUtils'; import KeychainServiceDriverMobile from '@joplin/lib/services/keychain/KeychainServiceDriver.mobile'; @@ -699,8 +700,6 @@ class AppComponent extends React.Component { this.handleOpenURL_ = (event: any) => { if (event.url == ShareExtension.shareURL) { void this.handleShareData(); - } else { - void this.navigate(event.url); } }; } @@ -755,10 +754,10 @@ class AppComponent extends React.Component { }); } - Linking.getInitialURL().then((url: string) => this.navigate(url)); - Linking.addEventListener('url', this.handleOpenURL_); + eventManager.on('itemChange', this.onItemChange_); + BackButtonService.initialize(this.backButtonHandler_); AlarmService.setInAppNotificationHandler(async (alarmId: string) => { @@ -775,8 +774,6 @@ class AppComponent extends React.Component { await setupNotifications(this.props.dispatch); - await updateRecentsWidget(); - // Setting.setValue('encryption.masterPassword', 'WRONG'); // setTimeout(() => NavService.go('EncryptionConfig'), 2000); } @@ -817,6 +814,13 @@ class AppComponent extends React.Component { return false; } + private onItemChange_({ itemType }: {itemType: ModelType}) { + if (itemType !== ModelType.Note) { + return; + } + updateRecentsWidgetWithDebounce(); + } + private async handleShareData() { const sharedData = await ShareExtension.data(); if (sharedData) { @@ -903,37 +907,6 @@ class AppComponent extends React.Component { ); } - - private async navigate(url: string) { - if (!url) return; - let parsedUrl; - try { - parsedUrl = new URL(url); - } catch (_) { - // invalid URL - return; - } - reg.logger().info(`navigate to ${url}`); - - await this.props.dispatch({ type: 'NAV_BACK' }); - await this.props.dispatch({ type: 'SIDE_MENU_CLOSE' }); - const { pathname } = parsedUrl; - if (pathname.startsWith('//notes/')) { - const noteId = pathname.replace('//notes/', ''); - await this.props.dispatch({ - type: 'NAV_GO', - noteId, - routeName: 'Note', - }); - } else if (pathname.startsWith('//folder/')) { - const folderId = pathname.replace('//folder/', ''); - await this.props.dispatch({ - type: 'NAV_GO', - folderId, - routeName: 'Folder', - }); - } - } } const mapStateToProps = (state: any) => { diff --git a/packages/app-mobile/utils/WidgetUtils.ts b/packages/app-mobile/utils/WidgetUtils.ts new file mode 100644 index 00000000000..439cbe3b56b --- /dev/null +++ b/packages/app-mobile/utils/WidgetUtils.ts @@ -0,0 +1,32 @@ +import { RecentsWidget } from './RecentsWidget'; +import Note from '@joplin/lib/models/Note'; +import { reg } from '@joplin/lib/registry'; +import shim from '@joplin/lib/shim'; + +const MAX_COUNT = 10; +const DEBOUNCE_TIMEOUT = 5000; + +export async function updateRecentsWidget() { + reg.logger().info('updating recents widget'); + const recents = await Note.all({ + fields: ['id', 'title'], + order: [{ by: 'updated_time', dir: 'DESC' }], + limit: MAX_COUNT, + }); + return RecentsWidget.write({ + notes: recents, + }); +} + +let hasUpdateScheduled = false; + +export function updateRecentsWidgetWithDebounce() { + if (hasUpdateScheduled) { + return; + } + hasUpdateScheduled = true; + shim.setTimeout(async () => { + await updateRecentsWidget(); + hasUpdateScheduled = false; + }, DEBOUNCE_TIMEOUT); +} diff --git a/packages/app-mobile/utils/updateRecentsWidget.ts b/packages/app-mobile/utils/updateRecentsWidget.ts deleted file mode 100644 index 1cc86b60f55..00000000000 --- a/packages/app-mobile/utils/updateRecentsWidget.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { RecentsWidget } from './RecentsWidget'; -import Note from '@joplin/lib/models/Note'; - -const MAX_COUNT = 10; - -export default async () => { - const recents = await Note.all({ - fields: ['id', 'title'], - order: [{ by: 'updated_time', dir: 'DESC' }], - limit: MAX_COUNT, - }); - return RecentsWidget.write({ - notes: recents, - }); -};