From ce49ad0ea3ceb437b643d343fbfab4bad1b14ac9 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Mon, 19 Aug 2024 11:30:30 +0200 Subject: [PATCH 01/10] add conversation shortcuts and styling --- .../CustomNotificationProvider.java | 60 ++++++++++++------- 1 file changed, 39 insertions(+), 21 deletions(-) diff --git a/android/app/src/main/java/com/expensify/chat/customairshipextender/CustomNotificationProvider.java b/android/app/src/main/java/com/expensify/chat/customairshipextender/CustomNotificationProvider.java index 8eff32dedf76..a19abcce7d92 100644 --- a/android/app/src/main/java/com/expensify/chat/customairshipextender/CustomNotificationProvider.java +++ b/android/app/src/main/java/com/expensify/chat/customairshipextender/CustomNotificationProvider.java @@ -8,6 +8,7 @@ import android.app.NotificationChannelGroup; import android.app.NotificationManager; import android.content.Context; +import android.content.Intent; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.Canvas; @@ -30,6 +31,8 @@ import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationManagerCompat; import androidx.core.app.Person; +import androidx.core.content.pm.ShortcutInfoCompat; +import androidx.core.content.pm.ShortcutManagerCompat; import androidx.core.graphics.drawable.IconCompat; import androidx.versionedparcelable.ParcelUtils; @@ -47,6 +50,7 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; +import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Objects; @@ -210,39 +214,53 @@ private void applyMessageStyle(@NonNull Context context, NotificationCompat.Buil // create the Person object who sent the latest report comment Bitmap personIcon = fetchIcon(context, avatar); builder.setLargeIcon(personIcon); + Intent intent = new Intent(Intent.ACTION_VIEW, + Uri.parse("new-expensify://r/" + reportID)); Person person = createMessagePersonObject(IconCompat.createWithBitmap(personIcon), accountID, name); + ShortcutInfoCompat shortcutInfo = new ShortcutInfoCompat.Builder(context, accountID) + .setShortLabel(name) + .setLongLabel(name) + .setCategories(Collections.singleton(CATEGORY_MESSAGE)) + .setIntent(intent) + .setLongLived(true) + .setPerson(person) + .setIcon(IconCompat.createWithBitmap(personIcon)) + .build(); + + ShortcutManagerCompat.pushDynamicShortcut(context, shortcutInfo); // Create latest received message object long createdTimeInMillis = getMessageTimeInMillis(messageData.get("created").getString("")); NotificationCompat.MessagingStyle.Message newMessage = new NotificationCompat.MessagingStyle.Message(message, createdTimeInMillis, person); - // Conversational styling should be applied to groups chats, rooms, and any 1:1 chats with more than one notification (ensuring the large profile image is always shown) - if (!conversationName.isEmpty() || hasExistingNotification) { - // Create the messaging style notification builder for this notification, associating it with the person who sent the report comment - NotificationCompat.MessagingStyle messagingStyle = new NotificationCompat.MessagingStyle(person) - .setGroupConversation(true) - .setConversationTitle(conversationName); + NotificationCompat.MessagingStyle messagingStyle = new NotificationCompat.MessagingStyle(person); + // Add all conversation messages to the notification, including the last one we just received. + List messages; + if (hasExistingNotification) { + NotificationCompat.MessagingStyle previousStyle = NotificationCompat.MessagingStyle.extractMessagingStyleFromNotification(existingReportNotification.getNotification()); + messages = previousStyle != null ? previousStyle.getMessages() : new ArrayList<>(List.of(recreatePreviousMessage(existingReportNotification))); + } else { + messages = new ArrayList<>(); + } - // Add all conversation messages to the notification, including the last one we just received. - List messages; - if (hasExistingNotification) { - NotificationCompat.MessagingStyle previousStyle = NotificationCompat.MessagingStyle.extractMessagingStyleFromNotification(existingReportNotification.getNotification()); - messages = previousStyle != null ? previousStyle.getMessages() : new ArrayList<>(List.of(recreatePreviousMessage(existingReportNotification))); - } else { - messages = new ArrayList<>(); - } - - // add the last one message we just received. - messages.add(newMessage); + // add the last one message we just received. + messages.add(newMessage); - for (NotificationCompat.MessagingStyle.Message activeMessage : messages) { - messagingStyle.addMessage(activeMessage); - } + for (NotificationCompat.MessagingStyle.Message activeMessage : messages) { + messagingStyle.addMessage(activeMessage); + } - builder.setStyle(messagingStyle); + // Conversational styling should be applied to groups chats, rooms, and any 1:1 chats with more than one notification (ensuring the large profile image is always shown) + if (!conversationName.isEmpty()) { + // Create the messaging style notification builder for this notification, associating it with the person who sent the report comment + messagingStyle + .setGroupConversation(true) + .setConversationTitle(conversationName); } + builder.setStyle(messagingStyle); + builder.setShortcutId(accountID); // save reportID and person info for future merging builder.addExtras(createMessageExtrasBundle(reportID, person)); From 676e2f71a9d6ed5e7f8abeab61c2823706154dd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Ska=C5=82ka?= Date: Tue, 3 Sep 2024 19:38:03 +0200 Subject: [PATCH 02/10] Add Android native module --- .../com/expensify/chat/MainApplication.kt | 2 + .../CustomNotificationProvider.java | 18 ++------ .../ShortcutManagerModule.java | 42 +++++++++++++++++++ .../ShortcutManagerPackage.java | 29 +++++++++++++ .../ShortcutManagerUtils.java | 38 +++++++++++++++++ ...e-device-info+10.3.1+002+turbomodule.patch | 4 +- 6 files changed, 117 insertions(+), 16 deletions(-) create mode 100644 android/app/src/main/java/com/expensify/chat/shortcutManagerModule/ShortcutManagerModule.java create mode 100644 android/app/src/main/java/com/expensify/chat/shortcutManagerModule/ShortcutManagerPackage.java create mode 100644 android/app/src/main/java/com/expensify/chat/shortcutManagerModule/ShortcutManagerUtils.java diff --git a/android/app/src/main/java/com/expensify/chat/MainApplication.kt b/android/app/src/main/java/com/expensify/chat/MainApplication.kt index 26a28d9955a0..2cc8b7780253 100644 --- a/android/app/src/main/java/com/expensify/chat/MainApplication.kt +++ b/android/app/src/main/java/com/expensify/chat/MainApplication.kt @@ -8,6 +8,7 @@ import android.database.CursorWindow import android.os.Process import androidx.multidex.MultiDexApplication import com.expensify.chat.bootsplash.BootSplashPackage +import com.expensify.chat.shortcutManagerModule.ShortcutManagerPackage import com.facebook.react.PackageList import com.facebook.react.ReactApplication import com.facebook.react.ReactNativeHost @@ -29,6 +30,7 @@ class MainApplication : MultiDexApplication(), ReactApplication { PackageList(this).packages.apply { // Packages that cannot be autolinked yet can be added manually here, for example: // add(MyReactNativePackage()); + add(ShortcutManagerPackage()) add(BootSplashPackage()) add(ExpensifyAppPackage()) add(RNTextInputResetPackage()) diff --git a/android/app/src/main/java/com/expensify/chat/customairshipextender/CustomNotificationProvider.java b/android/app/src/main/java/com/expensify/chat/customairshipextender/CustomNotificationProvider.java index a19abcce7d92..eb5b78aea744 100644 --- a/android/app/src/main/java/com/expensify/chat/customairshipextender/CustomNotificationProvider.java +++ b/android/app/src/main/java/com/expensify/chat/customairshipextender/CustomNotificationProvider.java @@ -37,6 +37,7 @@ import androidx.versionedparcelable.ParcelUtils; import com.expensify.chat.R; +import com.expensify.chat.shortcutManagerModule.ShortcutManagerUtils; import com.urbanairship.AirshipConfigOptions; import com.urbanairship.json.JsonMap; import com.urbanairship.json.JsonValue; @@ -211,24 +212,13 @@ private void applyMessageStyle(@NonNull Context context, NotificationCompat.Buil String message = alert != null ? alert : messageData.get("message").getList().get(0).getMap().get("text").getString(); String conversationName = payload.get("roomName") == null ? "" : payload.get("roomName").getString(""); - // create the Person object who sent the latest report comment + // Create the Person object who sent the latest report comment Bitmap personIcon = fetchIcon(context, avatar); builder.setLargeIcon(personIcon); - Intent intent = new Intent(Intent.ACTION_VIEW, - Uri.parse("new-expensify://r/" + reportID)); Person person = createMessagePersonObject(IconCompat.createWithBitmap(personIcon), accountID, name); - ShortcutInfoCompat shortcutInfo = new ShortcutInfoCompat.Builder(context, accountID) - .setShortLabel(name) - .setLongLabel(name) - .setCategories(Collections.singleton(CATEGORY_MESSAGE)) - .setIntent(intent) - .setLongLived(true) - .setPerson(person) - .setIcon(IconCompat.createWithBitmap(personIcon)) - .build(); - - ShortcutManagerCompat.pushDynamicShortcut(context, shortcutInfo); + + ShortcutManagerUtils.addDynamicShortcut(context, reportId, name, accountID, personIcon, person); // Create latest received message object long createdTimeInMillis = getMessageTimeInMillis(messageData.get("created").getString("")); diff --git a/android/app/src/main/java/com/expensify/chat/shortcutManagerModule/ShortcutManagerModule.java b/android/app/src/main/java/com/expensify/chat/shortcutManagerModule/ShortcutManagerModule.java new file mode 100644 index 000000000000..07103f81d4b7 --- /dev/null +++ b/android/app/src/main/java/com/expensify/chat/shortcutManagerModule/ShortcutManagerModule.java @@ -0,0 +1,42 @@ +package com.expensify.chat.shortcutManagerModule; + +import static androidx.core.app.NotificationCompat.CATEGORY_MESSAGE; + +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.net.Uri; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.core.app.Person; +import androidx.core.content.pm.ShortcutInfoCompat; +import androidx.core.content.pm.ShortcutManagerCompat; +import androidx.core.graphics.drawable.IconCompat; + +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReactContextBaseJavaModule; +import com.facebook.react.bridge.ReactMethod; + +import java.util.Collections; + +import com.expensify.chat.customairshipextender.CustomNotificationProvider; + +public class ShortcutManagerModule extends ReactContextBaseJavaModule { + private ReactApplicationContext context; + + public ShortcutManagerModule(ReactApplicationContext context) { + super(context); + } + + @NonNull + @Override + public String getName() { + return "ShortcutManager"; + } + + @ReactMethod + public void removeAllDynamicShortcuts() { + ShortcutManagerUtils.removeAllDynamicShortcuts(context); + } +} diff --git a/android/app/src/main/java/com/expensify/chat/shortcutManagerModule/ShortcutManagerPackage.java b/android/app/src/main/java/com/expensify/chat/shortcutManagerModule/ShortcutManagerPackage.java new file mode 100644 index 000000000000..d28f75592d93 --- /dev/null +++ b/android/app/src/main/java/com/expensify/chat/shortcutManagerModule/ShortcutManagerPackage.java @@ -0,0 +1,29 @@ +package com.expensify.chat.shortcutManagerModule; + +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 java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class ShortcutManagerPackage implements ReactPackage { + + @NonNull + @Override + public List createViewManagers(@NonNull ReactApplicationContext reactContext) { + return Collections.emptyList(); + } + + @NonNull + @Override + public List createNativeModules(@NonNull ReactApplicationContext reactContext) { + List modules = new ArrayList<>(); + modules.add(new ShortcutManagerModule(reactContext)); + return modules; + } +} diff --git a/android/app/src/main/java/com/expensify/chat/shortcutManagerModule/ShortcutManagerUtils.java b/android/app/src/main/java/com/expensify/chat/shortcutManagerModule/ShortcutManagerUtils.java new file mode 100644 index 000000000000..a8d58d672db0 --- /dev/null +++ b/android/app/src/main/java/com/expensify/chat/shortcutManagerModule/ShortcutManagerUtils.java @@ -0,0 +1,38 @@ +package com.expensify.chat.shortcutManagerModule; + +import static androidx.core.app.NotificationCompat.CATEGORY_MESSAGE; + +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.net.Uri; + +import androidx.core.app.Person; +import androidx.core.content.pm.ShortcutInfoCompat; +import androidx.core.content.pm.ShortcutManagerCompat; +import androidx.core.graphics.drawable.IconCompat; + +import java.util.Collections; + +public class ShortcutManagerUtils { + public static void removeAllDynamicShortcuts(Context context) { + ShortcutManagerCompat.removeAllDynamicShortcuts(context); + } + + public static void addDynamicShortcut(Context context, String reportID, String name, String accountID, Bitmap personIcon, Person person) { + Intent intent = new Intent(Intent.ACTION_VIEW, + Uri.parse("new-expensify://r/" + reportID)); + + ShortcutInfoCompat shortcutInfo = new ShortcutInfoCompat.Builder(context, accountID) + .setShortLabel(name) + .setLongLabel(name) + .setCategories(Collections.singleton(CATEGORY_MESSAGE)) + .setIntent(intent) + .setLongLived(true) + .setPerson(person) + .setIcon(IconCompat.createWithBitmap(personIcon)) + .build(); + ShortcutManagerCompat.pushDynamicShortcut(context, shortcutInfo); + } + +} diff --git a/patches/react-native-device-info+10.3.1+002+turbomodule.patch b/patches/react-native-device-info+10.3.1+002+turbomodule.patch index 4b62c30947c2..b7698fd06c85 100644 --- a/patches/react-native-device-info+10.3.1+002+turbomodule.patch +++ b/patches/react-native-device-info+10.3.1+002+turbomodule.patch @@ -477,7 +477,7 @@ index b01e9d2..d954717 100644 @ReactMethod(isBlockingSynchronousMethod = true) - public int getApiLevelSync() { return Build.VERSION.SDK_INT; } + public double getApiLevelSync() { return Build.VERSION.SDK_INT; } - @ReactMethod + hod public void getApiLevel(Promise p) { p.resolve(getApiLevelSync()); } @@ -858,11 +897,11 @@ public class RNDeviceModule extends ReactContextBaseJavaModule { @@ -566,7 +566,7 @@ index 0000000..5aad02a + @DoNotStrip + public abstract void getSupported32BitAbis(Promise promise); + -+ @ReactMethod(isBlockingSynchronousMethod = true) ++ hod(isBlockingSynchronousMethod = true) + @DoNotStrip + public abstract WritableArray getSupported32BitAbisSync(); + From 746b59aab9c5e05d4dfe7e6c1129255498ff2f6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Ska=C5=82ka?= Date: Wed, 4 Sep 2024 10:07:42 +0200 Subject: [PATCH 03/10] Add dynamic shortcut clearing on signing out --- .../chat/shortcutManagerModule/ShortcutManagerModule.java | 1 + src/libs/ShortcutManager/index.ts | 6 ++++++ src/libs/actions/Session/index.ts | 3 +++ src/types/modules/react-native.d.ts | 2 ++ 4 files changed, 12 insertions(+) create mode 100644 src/libs/ShortcutManager/index.ts diff --git a/android/app/src/main/java/com/expensify/chat/shortcutManagerModule/ShortcutManagerModule.java b/android/app/src/main/java/com/expensify/chat/shortcutManagerModule/ShortcutManagerModule.java index 07103f81d4b7..fdb6d0ba3b97 100644 --- a/android/app/src/main/java/com/expensify/chat/shortcutManagerModule/ShortcutManagerModule.java +++ b/android/app/src/main/java/com/expensify/chat/shortcutManagerModule/ShortcutManagerModule.java @@ -27,6 +27,7 @@ public class ShortcutManagerModule extends ReactContextBaseJavaModule { public ShortcutManagerModule(ReactApplicationContext context) { super(context); + this.context = context; } @NonNull diff --git a/src/libs/ShortcutManager/index.ts b/src/libs/ShortcutManager/index.ts new file mode 100644 index 000000000000..f1aa501085f6 --- /dev/null +++ b/src/libs/ShortcutManager/index.ts @@ -0,0 +1,6 @@ +type ShortcutManagerModule = { + removeAllDynamicShortcuts: () => void; +}; + +// eslint-disable-next-line import/prefer-default-export +export type {ShortcutManagerModule}; diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index e905464e551f..b093c171e9cf 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -56,6 +56,8 @@ import type Session from '@src/types/onyx/Session'; import clearCache from './clearCache'; import updateSessionAuthTokens from './updateSessionAuthTokens'; +const {ShortcutManager} = NativeModules; + let session: Session = {}; let authPromiseResolver: ((value: boolean) => void) | null = null; Onyx.connect({ @@ -204,6 +206,7 @@ function hasAuthToken(): boolean { function signOutAndRedirectToSignIn(shouldResetToHome?: boolean, shouldStashSession?: boolean, killHybridApp = true) { Log.info('Redirecting to Sign In because signOut() was called'); hideContextMenu(false); + ShortcutManager.removeAllDynamicShortcuts(); if (!isAnonymousUser()) { // In the HybridApp, we want the Old Dot to handle the sign out process if (NativeModules.HybridAppModule && killHybridApp) { diff --git a/src/types/modules/react-native.d.ts b/src/types/modules/react-native.d.ts index 7e3f299ad938..40c5b72ce1e4 100644 --- a/src/types/modules/react-native.d.ts +++ b/src/types/modules/react-native.d.ts @@ -2,6 +2,7 @@ import type {TargetedEvent} from 'react-native'; import type {BootSplashModule} from '@libs/BootSplash/types'; import type {EnvironmentCheckerModule} from '@libs/Environment/betaChecker/types'; +import type {ShortcutManagerModule} from '@libs/ShortcutManager'; import type StartupTimer from '@libs/StartupTimer/types'; type HybridAppModule = { @@ -42,6 +43,7 @@ declare module 'react-native' { StartupTimer: StartupTimer; RNTextInputReset: RNTextInputResetModule; EnvironmentChecker: EnvironmentCheckerModule; + ShortcutManager: ShortcutManagerModule; } namespace Animated { From f7c30b069c8eab1945877ee549f308fe74170693 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Ska=C5=82ka?= Date: Wed, 4 Sep 2024 10:32:18 +0200 Subject: [PATCH 04/10] Add iOS native module --- ios/NewExpensify.xcodeproj/project.pbxproj | 10 +++++++++- ios/RCTShortcutManagerModule.h | 4 ++++ ios/RCTShortcutManagerModule.m | 10 ++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 ios/RCTShortcutManagerModule.h create mode 100644 ios/RCTShortcutManagerModule.m diff --git a/ios/NewExpensify.xcodeproj/project.pbxproj b/ios/NewExpensify.xcodeproj/project.pbxproj index 0f9edeab601d..48b92ef30cba 100644 --- a/ios/NewExpensify.xcodeproj/project.pbxproj +++ b/ios/NewExpensify.xcodeproj/project.pbxproj @@ -16,6 +16,8 @@ 0CDA8E35287DD650004ECBEC /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0CDA8E33287DD650004ECBEC /* AppDelegate.mm */; }; 0CDA8E37287DD6A0004ECBEC /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0CDA8E36287DD6A0004ECBEC /* Images.xcassets */; }; 0CDA8E38287DD6A0004ECBEC /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0CDA8E36287DD6A0004ECBEC /* Images.xcassets */; }; + 0DFC45942C884E0A00B56C91 /* RCTShortcutManagerModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DFC45932C884E0A00B56C91 /* RCTShortcutManagerModule.m */; }; + 0DFC45952C884E0A00B56C91 /* RCTShortcutManagerModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DFC45932C884E0A00B56C91 /* RCTShortcutManagerModule.m */; }; 0F5BE0CE252686330097D869 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 0F5BE0CD252686320097D869 /* GoogleService-Info.plist */; }; 0F5E5350263B73FD004CA14F /* EnvironmentChecker.m in Sources */ = {isa = PBXBuildFile; fileRef = 0F5E534F263B73FD004CA14F /* EnvironmentChecker.m */; }; 0F5E5351263B73FD004CA14F /* EnvironmentChecker.m in Sources */ = {isa = PBXBuildFile; fileRef = 0F5E534F263B73FD004CA14F /* EnvironmentChecker.m */; }; @@ -89,7 +91,9 @@ 083353EA2B5AB22900C603C0 /* success.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; name = success.mp3; path = ../assets/sounds/success.mp3; sourceTree = ""; }; 0CDA8E33287DD650004ECBEC /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = NewExpensify/AppDelegate.mm; sourceTree = ""; }; 0CDA8E36287DD6A0004ECBEC /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = NewExpensify/Images.xcassets; sourceTree = ""; }; - 0D3F9E814828D91464DF9D35 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; name = PrivacyInfo.xcprivacy; path = NewExpensify/PrivacyInfo.xcprivacy; sourceTree = ""; }; + 0D3F9E814828D91464DF9D35 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = NewExpensify/PrivacyInfo.xcprivacy; sourceTree = ""; }; + 0DFC45922C884D7900B56C91 /* RCTShortcutManagerModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTShortcutManagerModule.h; sourceTree = ""; }; + 0DFC45932C884E0A00B56C91 /* RCTShortcutManagerModule.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTShortcutManagerModule.m; sourceTree = ""; }; 0F5BE0CD252686320097D869 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 0F5E534E263B73D5004CA14F /* EnvironmentChecker.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EnvironmentChecker.h; sourceTree = ""; }; 0F5E534F263B73FD004CA14F /* EnvironmentChecker.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EnvironmentChecker.m; sourceTree = ""; }; @@ -267,6 +271,8 @@ 83CBB9F61A601CBA00E9B192 = { isa = PBXGroup; children = ( + 0DFC45922C884D7900B56C91 /* RCTShortcutManagerModule.h */, + 0DFC45932C884E0A00B56C91 /* RCTShortcutManagerModule.m */, 499B0DA92BE2A1C000CABFB0 /* PrivacyInfo.xcprivacy */, 374FB8D528A133A7000D84EF /* OriginImageRequestHandler.h */, 374FB8D628A133FE000D84EF /* OriginImageRequestHandler.mm */, @@ -876,6 +882,7 @@ buildActionMask = 2147483647; files = ( 0F5E5351263B73FD004CA14F /* EnvironmentChecker.m in Sources */, + 0DFC45952C884E0A00B56C91 /* RCTShortcutManagerModule.m in Sources */, 0CDA8E35287DD650004ECBEC /* AppDelegate.mm in Sources */, 7041848626A8E47D00E09F4D /* RCTStartupTimer.m in Sources */, 7F5E81F06BCCF61AD02CEA06 /* ExpoModulesProvider.swift in Sources */, @@ -887,6 +894,7 @@ buildActionMask = 2147483647; files = ( 18D050E0262400AF000D658B /* BridgingFile.swift in Sources */, + 0DFC45942C884E0A00B56C91 /* RCTShortcutManagerModule.m in Sources */, 0F5E5350263B73FD004CA14F /* EnvironmentChecker.m in Sources */, 374FB8D728A133FE000D84EF /* OriginImageRequestHandler.mm in Sources */, 7041848526A8E47D00E09F4D /* RCTStartupTimer.m in Sources */, diff --git a/ios/RCTShortcutManagerModule.h b/ios/RCTShortcutManagerModule.h new file mode 100644 index 000000000000..5d596d5e7a5e --- /dev/null +++ b/ios/RCTShortcutManagerModule.h @@ -0,0 +1,4 @@ +// RCTShortcutManagerModule.h +#import +@interface RCTShortcutManagerModule : NSObject +@end diff --git a/ios/RCTShortcutManagerModule.m b/ios/RCTShortcutManagerModule.m new file mode 100644 index 000000000000..024b25a625a3 --- /dev/null +++ b/ios/RCTShortcutManagerModule.m @@ -0,0 +1,10 @@ +// RCTCalendarModule.m +#import "RCTShortcutManagerModule.h" + +@implementation RCTShortcutManagerModule + +RCT_EXPORT_METHOD(removeAllDynamicShortcuts){} + +RCT_EXPORT_MODULE(ShortcutManager); + +@end From ce11ee577e4b558cd5d962bdfe6483fba65dbd11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Ska=C5=82ka?= Date: Wed, 4 Sep 2024 11:59:17 +0200 Subject: [PATCH 05/10] Undo patch changes --- ios/NewExpensify.xcodeproj/project.pbxproj | 6 +++--- .../react-native-device-info+10.3.1+002+turbomodule.patch | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ios/NewExpensify.xcodeproj/project.pbxproj b/ios/NewExpensify.xcodeproj/project.pbxproj index 8297ff717145..768062717d4b 100644 --- a/ios/NewExpensify.xcodeproj/project.pbxproj +++ b/ios/NewExpensify.xcodeproj/project.pbxproj @@ -43,7 +43,7 @@ D27CE6B77196EF3EF450EEAC /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 0D3F9E814828D91464DF9D35 /* PrivacyInfo.xcprivacy */; }; DD79042B2792E76D004484B4 /* RCTBootSplash.mm in Sources */ = {isa = PBXBuildFile; fileRef = DD79042A2792E76D004484B4 /* RCTBootSplash.mm */; }; DDCB2E57F334C143AC462B43 /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D20D83B0E39BA6D21761E72 /* ExpoModulesProvider.swift */; }; - E51DC681C7DEE40AEBDDFBFE /* (null) in Frameworks */ = {isa = PBXBuildFile; }; + E51DC681C7DEE40AEBDDFBFE /* BuildFile in Frameworks */ = {isa = PBXBuildFile; }; E9DF872D2525201700607FDC /* AirshipConfig.plist in Resources */ = {isa = PBXBuildFile; fileRef = E9DF872C2525201700607FDC /* AirshipConfig.plist */; }; ED222ED90E074A5481A854FA /* ExpensifyNeue-BoldItalic.otf in Resources */ = {isa = PBXBuildFile; fileRef = 8B28D84EF339436DBD42A203 /* ExpensifyNeue-BoldItalic.otf */; }; F0C450EA2705020500FD2970 /* colors.json in Resources */ = {isa = PBXBuildFile; fileRef = F0C450E92705020500FD2970 /* colors.json */; }; @@ -175,8 +175,8 @@ buildActionMask = 2147483647; files = ( 383643682B6D4AE2005BB9AE /* DeviceCheck.framework in Frameworks */, - E51DC681C7DEE40AEBDDFBFE /* (null) in Frameworks */, - E51DC681C7DEE40AEBDDFBFE /* (null) in Frameworks */, + E51DC681C7DEE40AEBDDFBFE /* BuildFile in Frameworks */, + E51DC681C7DEE40AEBDDFBFE /* BuildFile in Frameworks */, 8744C5400E24E379441C04A4 /* libPods-NewExpensify.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/patches/react-native-device-info+10.3.1+002+turbomodule.patch b/patches/react-native-device-info+10.3.1+002+turbomodule.patch index b7698fd06c85..4b62c30947c2 100644 --- a/patches/react-native-device-info+10.3.1+002+turbomodule.patch +++ b/patches/react-native-device-info+10.3.1+002+turbomodule.patch @@ -477,7 +477,7 @@ index b01e9d2..d954717 100644 @ReactMethod(isBlockingSynchronousMethod = true) - public int getApiLevelSync() { return Build.VERSION.SDK_INT; } + public double getApiLevelSync() { return Build.VERSION.SDK_INT; } - hod + @ReactMethod public void getApiLevel(Promise p) { p.resolve(getApiLevelSync()); } @@ -858,11 +897,11 @@ public class RNDeviceModule extends ReactContextBaseJavaModule { @@ -566,7 +566,7 @@ index 0000000..5aad02a + @DoNotStrip + public abstract void getSupported32BitAbis(Promise promise); + -+ hod(isBlockingSynchronousMethod = true) ++ @ReactMethod(isBlockingSynchronousMethod = true) + @DoNotStrip + public abstract WritableArray getSupported32BitAbisSync(); + From c93af3ca441b1de5c65de10fba8892770f0ba89c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Ska=C5=82ka?= Date: Wed, 4 Sep 2024 11:59:31 +0200 Subject: [PATCH 06/10] Update JS module impoert --- src/libs/ShortcutManager/index.ts | 10 +++++++++- src/libs/actions/Session/index.ts | 3 +-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/libs/ShortcutManager/index.ts b/src/libs/ShortcutManager/index.ts index f1aa501085f6..cc748caaa0de 100644 --- a/src/libs/ShortcutManager/index.ts +++ b/src/libs/ShortcutManager/index.ts @@ -1,6 +1,14 @@ +import {NativeModules} from 'react-native'; + type ShortcutManagerModule = { removeAllDynamicShortcuts: () => void; }; -// eslint-disable-next-line import/prefer-default-export +const {ShortcutManager} = NativeModules; + export type {ShortcutManagerModule}; + +export default ShortcutManager || + ({ + removeAllDynamicShortcuts: () => {}, + } as ShortcutManagerModule); diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index b093c171e9cf..1c92723aa8e8 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -36,6 +36,7 @@ import NetworkConnection from '@libs/NetworkConnection'; import * as Pusher from '@libs/Pusher/pusher'; import * as ReportUtils from '@libs/ReportUtils'; import * as SessionUtils from '@libs/SessionUtils'; +import ShortcutManager from '@libs/ShortcutManager'; import Timers from '@libs/Timers'; import {hideContextMenu} from '@pages/home/report/ContextMenu/ReportActionContextMenu'; import {KEYS_TO_PRESERVE, openApp} from '@userActions/App'; @@ -56,8 +57,6 @@ import type Session from '@src/types/onyx/Session'; import clearCache from './clearCache'; import updateSessionAuthTokens from './updateSessionAuthTokens'; -const {ShortcutManager} = NativeModules; - let session: Session = {}; let authPromiseResolver: ((value: boolean) => void) | null = null; Onyx.connect({ From c00ed8f691e21bc6571f3fa96f3831f850ff2401 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Ska=C5=82ka?= Date: Wed, 4 Sep 2024 12:36:32 +0200 Subject: [PATCH 07/10] Add review changes --- .../customairshipextender/CustomNotificationProvider.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/android/app/src/main/java/com/expensify/chat/customairshipextender/CustomNotificationProvider.java b/android/app/src/main/java/com/expensify/chat/customairshipextender/CustomNotificationProvider.java index eb5b78aea744..791371630821 100644 --- a/android/app/src/main/java/com/expensify/chat/customairshipextender/CustomNotificationProvider.java +++ b/android/app/src/main/java/com/expensify/chat/customairshipextender/CustomNotificationProvider.java @@ -210,7 +210,7 @@ private void applyMessageStyle(@NonNull Context context, NotificationCompat.Buil // Use the formatted alert message from the backend. Otherwise fallback on the message in the Onyx data. String message = alert != null ? alert : messageData.get("message").getList().get(0).getMap().get("text").getString(); - String conversationName = payload.get("roomName") == null ? "" : payload.get("roomName").getString(""); + String roomName = payload.get("roomName") == null ? "" : payload.get("roomName").getString(""); // Create the Person object who sent the latest report comment Bitmap personIcon = fetchIcon(context, avatar); @@ -243,11 +243,11 @@ private void applyMessageStyle(@NonNull Context context, NotificationCompat.Buil } // Conversational styling should be applied to groups chats, rooms, and any 1:1 chats with more than one notification (ensuring the large profile image is always shown) - if (!conversationName.isEmpty()) { + if (!roomName.isEmpty()) { // Create the messaging style notification builder for this notification, associating it with the person who sent the report comment messagingStyle .setGroupConversation(true) - .setConversationTitle(conversationName); + .setConversationTitle(roomName); } builder.setStyle(messagingStyle); builder.setShortcutId(accountID); From 973934e7cc6330164e59e394826060a1111fe65e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Ska=C5=82ka?= Date: Wed, 4 Sep 2024 12:40:44 +0200 Subject: [PATCH 08/10] Add comment to iOS module --- ios/RCTShortcutManagerModule.m | 1 + 1 file changed, 1 insertion(+) diff --git a/ios/RCTShortcutManagerModule.m b/ios/RCTShortcutManagerModule.m index 024b25a625a3..bab19019a967 100644 --- a/ios/RCTShortcutManagerModule.m +++ b/ios/RCTShortcutManagerModule.m @@ -1,4 +1,5 @@ // RCTCalendarModule.m +// iOS doesn't have dynamic shortcuts like Android, so this module contains noop functions to prevent iOS from crashing #import "RCTShortcutManagerModule.h" @implementation RCTShortcutManagerModule From bcfc0f59a6746548f4c3317d063612dbe32785ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Ska=C5=82ka?= Date: Thu, 12 Sep 2024 13:24:17 +0200 Subject: [PATCH 09/10] Change shortcut reportID from string to long --- .../chat/customairshipextender/CustomNotificationProvider.java | 2 +- .../chat/shortcutManagerModule/ShortcutManagerUtils.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/android/app/src/main/java/com/expensify/chat/customairshipextender/CustomNotificationProvider.java b/android/app/src/main/java/com/expensify/chat/customairshipextender/CustomNotificationProvider.java index 791371630821..b950921a0cd5 100644 --- a/android/app/src/main/java/com/expensify/chat/customairshipextender/CustomNotificationProvider.java +++ b/android/app/src/main/java/com/expensify/chat/customairshipextender/CustomNotificationProvider.java @@ -218,7 +218,7 @@ private void applyMessageStyle(@NonNull Context context, NotificationCompat.Buil Person person = createMessagePersonObject(IconCompat.createWithBitmap(personIcon), accountID, name); - ShortcutManagerUtils.addDynamicShortcut(context, reportId, name, accountID, personIcon, person); + ShortcutManagerUtils.addDynamicShortcut(context, reportID, name, accountID, personIcon, person); // Create latest received message object long createdTimeInMillis = getMessageTimeInMillis(messageData.get("created").getString("")); diff --git a/android/app/src/main/java/com/expensify/chat/shortcutManagerModule/ShortcutManagerUtils.java b/android/app/src/main/java/com/expensify/chat/shortcutManagerModule/ShortcutManagerUtils.java index a8d58d672db0..5947faaa67c4 100644 --- a/android/app/src/main/java/com/expensify/chat/shortcutManagerModule/ShortcutManagerUtils.java +++ b/android/app/src/main/java/com/expensify/chat/shortcutManagerModule/ShortcutManagerUtils.java @@ -19,7 +19,7 @@ public static void removeAllDynamicShortcuts(Context context) { ShortcutManagerCompat.removeAllDynamicShortcuts(context); } - public static void addDynamicShortcut(Context context, String reportID, String name, String accountID, Bitmap personIcon, Person person) { + public static void addDynamicShortcut(Context context, long reportID, String name, String accountID, Bitmap personIcon, Person person) { Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("new-expensify://r/" + reportID)); From 729bc4ad8bff880e9c2709945fc10632a01dde3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Ska=C5=82ka?= Date: Mon, 16 Sep 2024 13:19:19 +0200 Subject: [PATCH 10/10] Add review changes --- src/libs/Notification/PushNotification/index.native.ts | 2 ++ src/libs/actions/Session/index.ts | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/Notification/PushNotification/index.native.ts b/src/libs/Notification/PushNotification/index.native.ts index 334317ec5d0a..448365c1cd1d 100644 --- a/src/libs/Notification/PushNotification/index.native.ts +++ b/src/libs/Notification/PushNotification/index.native.ts @@ -2,6 +2,7 @@ import type {PushPayload} from '@ua/react-native-airship'; import Airship, {EventType} from '@ua/react-native-airship'; import Onyx from 'react-native-onyx'; import Log from '@libs/Log'; +import ShortcutManager from '@libs/ShortcutManager'; import * as PushNotificationActions from '@userActions/PushNotification'; import ONYXKEYS from '@src/ONYXKEYS'; import ForegroundNotifications from './ForegroundNotifications'; @@ -139,6 +140,7 @@ const deregister: Deregister = () => { Airship.removeAllListeners(EventType.PushReceived); Airship.removeAllListeners(EventType.NotificationResponse); ForegroundNotifications.disableForegroundNotifications(); + ShortcutManager.removeAllDynamicShortcuts(); }; /** diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index de2cd60ece39..ab209e9bf928 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -36,7 +36,6 @@ import NetworkConnection from '@libs/NetworkConnection'; import * as Pusher from '@libs/Pusher/pusher'; import * as ReportUtils from '@libs/ReportUtils'; import * as SessionUtils from '@libs/SessionUtils'; -import ShortcutManager from '@libs/ShortcutManager'; import Timers from '@libs/Timers'; import {hideContextMenu} from '@pages/home/report/ContextMenu/ReportActionContextMenu'; import {KEYS_TO_PRESERVE, openApp} from '@userActions/App'; @@ -206,7 +205,6 @@ function hasAuthToken(): boolean { function signOutAndRedirectToSignIn(shouldResetToHome?: boolean, shouldStashSession?: boolean, killHybridApp = true) { Log.info('Redirecting to Sign In because signOut() was called'); hideContextMenu(false); - ShortcutManager.removeAllDynamicShortcuts(); if (!isAnonymousUser()) { // In the HybridApp, we want the Old Dot to handle the sign out process if (NativeModules.HybridAppModule && killHybridApp) {