diff --git a/app/lib/core/filesystem.dart b/app/lib/core/filesystem.dart index ff21e920..8346ca2d 100644 --- a/app/lib/core/filesystem.dart +++ b/app/lib/core/filesystem.dart @@ -87,14 +87,75 @@ class _Filesystem { Future _linkMozillaDir(Directory filesDir) async { final mozillaDir = Directory(p.join(selectedProfileDir.path, 'mozilla')); - await mozillaDir.create(); + await mozillaDir.create(recursive: true); - final mozillaLink = Link(p.join(filesDir.path, 'mozilla')); - if (await mozillaLink.exists()) { - await mozillaLink.delete(); + final mozillaPath = p.join(filesDir.path, 'mozilla'); + + Future moveAside(FileSystemEntityType type) async { + final backupPath = p.join( + filesDir.path, + 'mozilla.backup.${DateTime.now().millisecondsSinceEpoch}', + ); + + if (type == FileSystemEntityType.directory) { + await Directory(mozillaPath).rename(backupPath); + } else { + await File(mozillaPath).rename(backupPath); + } } - await mozillaLink.create(mozillaDir.path); + Future createLink() async { + await Link(mozillaPath).create(mozillaDir.path); + } + + final currentType = await FileSystemEntity.type( + mozillaPath, + followLinks: false, + ); + + switch (currentType) { + case FileSystemEntityType.notFound: + break; + case FileSystemEntityType.link: + final link = Link(mozillaPath); + try { + if (await link.target() == mozillaDir.path) { + return; + } + } on FileSystemException { + // Replace unreadable or broken links. + } + + await link.delete(); + case FileSystemEntityType.directory: + case FileSystemEntityType.file: + case FileSystemEntityType.unixDomainSock: + case FileSystemEntityType.pipe: + default: + await moveAside(currentType); + } + + try { + await createLink(); + } on PathExistsException { + final retryType = await FileSystemEntity.type( + mozillaPath, + followLinks: false, + ); + + if (retryType == FileSystemEntityType.link) { + final link = Link(mozillaPath); + if (await link.target() == mozillaDir.path) { + return; + } + + await link.delete(); + } else if (retryType != FileSystemEntityType.notFound) { + await moveAside(retryType); + } + + await createLink(); + } } Future _setupSqliteCache() async { diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/GlobalComponents.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/GlobalComponents.kt index 49519a6c..51aded03 100644 --- a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/GlobalComponents.kt +++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/GlobalComponents.kt @@ -150,7 +150,9 @@ object GlobalComponents { //RustHttpConfig.setClient(lazy { newComponents.core.client }) - newComponents.core.engine.warmUp() + if (mode == ComponentsMode.FULL) { + newComponents.core.engine.warmUp() + } fun restorePreviousCustomTabs() { if (previousCustomTabs.isEmpty()) return diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/ProfileContext.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/ProfileContext.kt index f064b3e5..f250970d 100644 --- a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/ProfileContext.kt +++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/ProfileContext.kt @@ -57,6 +57,10 @@ class ProfileContext(private val base: Context, val relativePath: String) : return customApplicationInfo } + override fun getApplicationContext(): Context { + return this + } + override fun getFilesDir(): File { return customFilesDir } diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/activities/IntentReceiverActivity.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/activities/IntentReceiverActivity.kt index fec20043..c4331f46 100644 --- a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/activities/IntentReceiverActivity.kt +++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/activities/IntentReceiverActivity.kt @@ -303,15 +303,14 @@ class IntentReceiverActivity : Activity() { contextId: String?, ) { val message = "This PWA was originally installed in a different profile. " + - "Opening it here will use your current profile's data and settings, " + - "which means you won't see the same content, preferences, or saved data " + - "that you had in the original profile.\n\n" + + "Opening it here uses only your current profile's data and settings. " + + "The original profile's app state and saved data will not be used.\n\n" + "Do you want to proceed anyway?" AlertDialog.Builder(this) .setTitle("PWA Profile Mismatch") .setMessage(message) - .setPositiveButton("Open Anyway") { _, _ -> + .setPositiveButton("Open in Current Profile") { _, _ -> Log.d(TAG, "User chose to open PWA despite profile mismatch") launchPwaWithContext(url, contextId) } diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoPwaApiImpl.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoPwaApiImpl.kt index 9b280ec0..e8819533 100644 --- a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoPwaApiImpl.kt +++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoPwaApiImpl.kt @@ -144,7 +144,7 @@ class GeckoPwaApiImpl( val (iconBitmap, isMaskable) = loadPwaIcon(manifest) - val shortcutId = generateShortcutId(manifest.startUrl) + val shortcutId = generateShortcutId(manifest.startUrl, profileUuid) val launchToken = resolveLaunchToken( shortcutManager = shortcutManager, shortcutId = shortcutId, @@ -189,11 +189,11 @@ class GeckoPwaApiImpl( } /** - * Generates a collision-resistant shortcut ID from a URL using SHA-256. + * Generates a collision-resistant shortcut ID from URL + profile using SHA-256. */ - private fun generateShortcutId(url: String): String { + private fun generateShortcutId(url: String, profileUuid: String): String { val digest = MessageDigest.getInstance("SHA-256") - val hash = digest.digest(url.toByteArray()) + val hash = digest.digest("$url::$profileUuid".toByteArray()) val hex = hash.take(16).joinToString("") { "%02x".format(it) } return "pwa_$hex" } diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/components/Core.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/components/Core.kt index 291efc56..6d17e3a5 100644 --- a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/components/Core.kt +++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/components/Core.kt @@ -232,9 +232,7 @@ class Core( */ val customTabsStore by lazy { CustomTabsServiceStore() } - // Must use the base application context (not ProfileContext) so the database - // matches what WebAppLauncherActivity (from the library) uses when loading manifests. - val webAppManifestStorage by lazy { ManifestStorage(context.applicationContext) } + val webAppManifestStorage by lazy { ManifestStorage(context) } val webAppShortcutManager by lazy { WebAppShortcutManager(context, client, webAppManifestStorage) @@ -288,4 +286,4 @@ class Core( } } -} \ No newline at end of file +}