From aacf5988ccbd823703706d03e81335ae1068a325 Mon Sep 17 00:00:00 2001 From: Jared Date: Tue, 31 Mar 2026 10:50:24 -0700 Subject: [PATCH 1/2] Add home screen long-press shortcuts for iOS and Android Add two static shortcuts accessible via long-press on the app icon: - "Save 2FA First!" warning linking to the support article about getting a new phone and preserving credentials - "Contact Support" linking to live chat support iOS uses SF Symbols (nosign, message.fill) with shortcut types following reverse-DNS convention. Android uses system drawable icons (ic_dialog_alert, ic_menu_help) and native browser handling for https URLs to avoid misrouting through the deep link handler. Made-with: Cursor --- CHANGELOG.md | 3 ++ android/app/src/main/AndroidManifest.xml | 3 ++ .../java/co/edgesecure/app/MainActivity.kt | 36 ++++++++++++++++++ android/app/src/main/res/values/strings.xml | 4 ++ android/app/src/main/res/xml/shortcuts.xml | 23 ++++++++++++ ios/edge/AppDelegate.swift | 37 +++++++++++++++++++ ios/edge/Info.plist | 33 +++++++++++++++++ 7 files changed, 139 insertions(+) create mode 100644 android/app/src/main/res/xml/shortcuts.xml diff --git a/CHANGELOG.md b/CHANGELOG.md index 3494d9e8417..c58b0ae9b1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased (develop) +- added: Home screen long-press shortcut to contact support +- added: Home screen long-press shortcut warning about 2FA and credentials needed after uninstall + ## 4.47.0 (staging) - added: Include Zano sweep private key support for ZANO and tokens. diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 33e6b4cb839..4b5e0c6e9a9 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -92,6 +92,9 @@ + diff --git a/android/app/src/main/java/co/edgesecure/app/MainActivity.kt b/android/app/src/main/java/co/edgesecure/app/MainActivity.kt index 178abd63ef8..470b7e8941d 100644 --- a/android/app/src/main/java/co/edgesecure/app/MainActivity.kt +++ b/android/app/src/main/java/co/edgesecure/app/MainActivity.kt @@ -1,5 +1,6 @@ package co.edgesecure.app +import android.content.Intent import android.content.pm.ActivityInfo import android.os.Build import android.os.Bundle @@ -38,6 +39,41 @@ class MainActivity : ReactActivity() { if (resources.getBoolean(R.bool.portrait_only)) { requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT } + + maybeOpenInBrowser(intent) + } + + override fun onNewIntent(intent: Intent) { + if (!maybeOpenInBrowser(intent)) { + super.onNewIntent(intent) + } + } + + /** + * Opens https/http URLs in the default browser when they aren't + * registered deep link hosts handled by React Native. This prevents + * shortcut intents from being misrouted through the deep link handler. + * Returns true if the URL was opened in the browser. + */ + private fun maybeOpenInBrowser(intent: Intent?): Boolean { + val data = intent?.data ?: return false + val scheme = data.scheme + if (scheme != "https" && scheme != "http") return false + + val host = data.host + if (DEEP_LINK_HOSTS.contains(host)) return false + + val browserIntent = Intent(Intent.ACTION_VIEW, data) + startActivity(browserIntent) + return true + } + + companion object { + private val DEEP_LINK_HOSTS = setOf( + "deep.edge.app", + "dl.edge.app", + "return.edge.app" + ) } // Edge addition diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 806da384537..7ccc4c6d6bc 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -1,3 +1,7 @@ Edge + Contact Support + Contact support for help from a live agent + ⚠️ Save 2FA First! + ⚠️ Login requires 2FA & credentials! diff --git a/android/app/src/main/res/xml/shortcuts.xml b/android/app/src/main/res/xml/shortcuts.xml new file mode 100644 index 00000000000..d29e573bee2 --- /dev/null +++ b/android/app/src/main/res/xml/shortcuts.xml @@ -0,0 +1,23 @@ + + + + + + + + + diff --git a/ios/edge/AppDelegate.swift b/ios/edge/AppDelegate.swift index 9483d70d2ba..0395451041f 100644 --- a/ios/edge/AppDelegate.swift +++ b/ios/edge/AppDelegate.swift @@ -12,6 +12,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? var securityView: UIView? + // Deferred until React Native is fully initialized in didFinishLaunchingWithOptions + private var pendingShortcutItem: UIApplicationShortcutItem? + var reactNativeDelegate: ReactNativeDelegate? var reactNativeFactory: RCTReactNativeFactory? @@ -49,6 +52,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate { _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil ) -> Bool { + if let shortcutItem = launchOptions?[.shortcutItem] as? UIApplicationShortcutItem { + pendingShortcutItem = shortcutItem + } + // Initialize SDK's: initializeSentry() FirebaseApp.configure() @@ -72,9 +79,39 @@ class AppDelegate: UIResponder, UIApplicationDelegate { launchOptions: launchOptions ) + if let shortcutItem = pendingShortcutItem { + _ = handleShortcutItem(shortcutItem) + pendingShortcutItem = nil + } + return true } + func application( + _ application: UIApplication, + performActionFor shortcutItem: UIApplicationShortcutItem, + completionHandler: @escaping (Bool) -> Void + ) { + let handled = handleShortcutItem(shortcutItem) + completionHandler(handled) + } + + private func handleShortcutItem(_ shortcutItem: UIApplicationShortcutItem) -> Bool { + guard let urlString = shortcutItem.userInfo?["url"] as? String, + let url = URL(string: urlString) else { return false } + + if url.scheme == "https" || url.scheme == "http" { + UIApplication.shared.open(url, options: [:]) { success in + if !success { + print("Failed to open shortcut URL: \(urlString)") + } + } + return true + } + + return RCTLinkingManager.application(UIApplication.shared, open: url, options: [:]) + } + /** * Periodic background fetch logic. * Edge addition. diff --git a/ios/edge/Info.plist b/ios/edge/Info.plist index 2a9afea9725..28a8e401a31 100644 --- a/ios/edge/Info.plist +++ b/ios/edge/Info.plist @@ -57,6 +57,39 @@ CFBundleVersion 99999999 + UIApplicationShortcutItems + + + UIApplicationShortcutItemTitle + ⚠️ Save 2FA First! + UIApplicationShortcutItemSubtitle + Login requires 2FA & credentials! + UIApplicationShortcutItemIconSymbolName + nosign + UIApplicationShortcutItemType + co.edgesecure.app.do_not_uninstall + UIApplicationShortcutItemUserInfo + + url + https://support.edge.app/en/articles/13892372-getting-a-new-phone + + + + UIApplicationShortcutItemTitle + Contact Support + UIApplicationShortcutItemSubtitle + Get help from our live support agents + UIApplicationShortcutItemIconSymbolName + message.fill + UIApplicationShortcutItemType + co.edgesecure.app.contact_support + UIApplicationShortcutItemUserInfo + + url + https://support.edge.app/en/articles/14054649-need-help-reach-out-via-our-chat-bubble?chat=open + + + LSApplicationQueriesSchemes https From a1f8cef8e85fa5575b2f1838f039a2cbe5acfea0 Mon Sep 17 00:00:00 2001 From: Jared Date: Tue, 31 Mar 2026 10:50:29 -0700 Subject: [PATCH 2/2] Add Crowdin localization for native shortcut strings Register Android strings.xml and iOS InfoPlist.strings as Crowdin source files so shortcut labels can be translated alongside existing in-app strings. Crowdin will generate locale-specific resource directories (values-*/strings.xml, *.lproj/InfoPlist.strings) when translations are submitted. Made-with: Cursor --- crowdin.yml | 4 ++++ ios/edge/en.lproj/InfoPlist.strings | 5 +++++ 2 files changed, 9 insertions(+) create mode 100644 ios/edge/en.lproj/InfoPlist.strings diff --git a/crowdin.yml b/crowdin.yml index 45423568ad9..4796e3bef94 100644 --- a/crowdin.yml +++ b/crowdin.yml @@ -1,6 +1,10 @@ files: - source: /src/locales/strings/enUS.json translation: /src/locales/strings/%two_letters_code%.json + - source: /android/app/src/main/res/values/strings.xml + translation: /android/app/src/main/res/values-%android_code%/strings.xml + - source: /ios/edge/en.lproj/InfoPlist.strings + translation: /ios/edge/%osx_code%/InfoPlist.strings - source: /localization/AppStoreDescription.txt translation: /localization/AppStoreDescription.%two_letters_code%.txt - source: /localization/PlayStoreDescription.txt diff --git a/ios/edge/en.lproj/InfoPlist.strings b/ios/edge/en.lproj/InfoPlist.strings new file mode 100644 index 00000000000..f63ca39dd2f --- /dev/null +++ b/ios/edge/en.lproj/InfoPlist.strings @@ -0,0 +1,5 @@ +/* Home screen shortcut titles and subtitles */ +"⚠️ Save 2FA First!" = "⚠️ Save 2FA First!"; +"Login requires 2FA & credentials!" = "Login requires 2FA & credentials!"; +"Contact Support" = "Contact Support"; +"Get help from our live support agents" = "Get help from our live support agents";