diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/GeckoPlatformView.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/GeckoPlatformView.kt index 84d1ee74..a72994d5 100644 --- a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/GeckoPlatformView.kt +++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/GeckoPlatformView.kt @@ -11,6 +11,7 @@ import android.app.Activity import android.content.Context import android.view.ViewGroup import android.widget.FrameLayout +import eu.weblibre.flutter_mozilla_components.ext.EventSequence import eu.weblibre.flutter_mozilla_components.pigeons.GeckoStateEvents import io.flutter.plugin.common.StandardMessageCodec import io.flutter.plugin.platform.PlatformView @@ -58,7 +59,7 @@ private class NativeFragmentView( super.onFlutterViewAttached(flutterView) components.engineReportedInitialized = false - flutterEvents.onViewReadyStateChange(System.currentTimeMillis(), true) { _ -> } + flutterEvents.onViewReadyStateChange(EventSequence.next(), true) { _ -> } } override fun getView(): View { diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoMlApiImpl.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoMlApiImpl.kt index b1fa23a0..0b8568b0 100644 --- a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoMlApiImpl.kt +++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoMlApiImpl.kt @@ -1,5 +1,6 @@ package eu.weblibre.flutter_mozilla_components.api +import eu.weblibre.flutter_mozilla_components.ext.EventSequence import eu.weblibre.flutter_mozilla_components.feature.MLEngineFeature import eu.weblibre.flutter_mozilla_components.feature.ResultConsumer import eu.weblibre.flutter_mozilla_components.pigeons.GeckoMlApi @@ -68,7 +69,7 @@ class GeckoMlApiImpl( ) runOnUiThread { - stateEvents.onMlProgress(System.currentTimeMillis(), mlProgress) { result -> + stateEvents.onMlProgress(EventSequence.next(), mlProgress) { result -> result.onFailure { error -> android.util.Log.e("GeckoMlApi", "Failed to emit progress event: ${error.message}") } diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoPrefApiImpl.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoPrefApiImpl.kt index f5f18f3f..36a414a3 100644 --- a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoPrefApiImpl.kt +++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoPrefApiImpl.kt @@ -7,6 +7,7 @@ package eu.weblibre.flutter_mozilla_components.api import eu.weblibre.flutter_mozilla_components.GlobalComponents +import eu.weblibre.flutter_mozilla_components.ext.EventSequence import eu.weblibre.flutter_mozilla_components.feature.PrefManagerFeature import eu.weblibre.flutter_mozilla_components.feature.ResultConsumer import eu.weblibre.flutter_mozilla_components.pigeons.GeckoPref @@ -201,7 +202,7 @@ class GeckoPrefApiImpl : GeckoPrefApi, BrowserPrefObserverDelegate { override fun onPreferenceChange(observedPreference: BrowserPreference<*>) { components.flutterEvents.onPreferenceChange( - System.currentTimeMillis(), GeckoPref( + EventSequence.next(), GeckoPref( name = observedPreference.pref, value = observedPreference.value, defaultValue = observedPreference.defaultValue, diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoSuggestionApiImpl.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoSuggestionApiImpl.kt index 1ff2352c..b433a26d 100644 --- a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoSuggestionApiImpl.kt +++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoSuggestionApiImpl.kt @@ -7,6 +7,7 @@ package eu.weblibre.flutter_mozilla_components.api import eu.weblibre.flutter_mozilla_components.GlobalComponents +import eu.weblibre.flutter_mozilla_components.ext.EventSequence import eu.weblibre.flutter_mozilla_components.api.GeckoDeleteBrowsingDataControllerImpl.Companion import eu.weblibre.flutter_mozilla_components.ext.toWebPBytes import eu.weblibre.flutter_mozilla_components.pigeons.AutocompleteResult @@ -77,7 +78,7 @@ class GeckoSuggestionApiImpl( runOnUiThread { suggestionEvents.onSuggestionResult( - System.currentTimeMillis(), + EventSequence.next(), provider, mappedResults ) { } diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoTabsApiImpl.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoTabsApiImpl.kt index a7222477..ef6fb5a7 100644 --- a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoTabsApiImpl.kt +++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoTabsApiImpl.kt @@ -17,6 +17,7 @@ import eu.weblibre.flutter_mozilla_components.pigeons.SourceValue import eu.weblibre.flutter_mozilla_components.pigeons.WebExtensionActionType import eu.weblibre.flutter_mozilla_components.pigeons.WebExtensionData import eu.weblibre.flutter_mozilla_components.GlobalComponents +import eu.weblibre.flutter_mozilla_components.ext.EventSequence import eu.weblibre.flutter_mozilla_components.ext.toWebPBytes import eu.weblibre.flutter_mozilla_components.pigeons.FindResultState import eu.weblibre.flutter_mozilla_components.pigeons.GeckoStateEvents @@ -135,7 +136,7 @@ class GeckoTabsApiImpl : GeckoTabsApi { withContext(Dispatchers.Main) { components.flutterEvents.onIconChange( - System.currentTimeMillis(), + EventSequence.next(), tab.id, iconBytes ) { } @@ -159,7 +160,7 @@ class GeckoTabsApiImpl : GeckoTabsApi { val bytes = it.toWebPBytes() withContext(Dispatchers.Main) { components.flutterEvents.onThumbnailChange( - System.currentTimeMillis(), + EventSequence.next(), tab.id, bytes ) { } @@ -191,21 +192,21 @@ class GeckoTabsApiImpl : GeckoTabsApi { if (onSelectedTabChange) { components.flutterEvents.onSelectedTabChange( - System.currentTimeMillis(), + EventSequence.next(), selectedTab ) { } } if (onTabListChange) { components.flutterEvents.onTabListChange( - System.currentTimeMillis(), + EventSequence.next(), tabs.map { it.id }) { } } tabs.forEach { tab -> if (onTabContentStateChange) { components.flutterEvents.onTabContentStateChange( - System.currentTimeMillis(), + EventSequence.next(), TabContentState( id = tab.id, parentId = tab.parentId, @@ -226,7 +227,7 @@ class GeckoTabsApiImpl : GeckoTabsApi { if (onSecurityInfoStateChange) { components.flutterEvents.onSecurityInfoStateChange( - System.currentTimeMillis(), + EventSequence.next(), tab.id, SecurityInfoState( tab.content.securityInfo.isSecure, @@ -238,7 +239,7 @@ class GeckoTabsApiImpl : GeckoTabsApi { if (onReaderableStateChange) { components.flutterEvents.onReaderableStateChange( - System.currentTimeMillis(), + EventSequence.next(), tab.id, ReaderableState( tab.readerState.readerable, @@ -249,7 +250,7 @@ class GeckoTabsApiImpl : GeckoTabsApi { if (onHistoryStateChange) { components.flutterEvents.onHistoryStateChange( - System.currentTimeMillis(), + EventSequence.next(), tab.id, HistoryState( items = tab.content.history.items.map { item -> @@ -264,7 +265,7 @@ class GeckoTabsApiImpl : GeckoTabsApi { if (onFindResults) { components.flutterEvents.onFindResults( - System.currentTimeMillis(), + EventSequence.next(), tab.id, tab.content.findResults.map { result -> FindResultState( @@ -321,7 +322,7 @@ class GeckoTabsApiImpl : GeckoTabsApi { badgeBackgroundColor = browserAction.badgeBackgroundColor?.toLong(), ) components.addonEvents.onUpsertWebExtensionAction( - System.currentTimeMillis(), + EventSequence.next(), extension.id, WebExtensionActionType.BROWSER, data @@ -336,7 +337,7 @@ class GeckoTabsApiImpl : GeckoTabsApi { val imageBytes = icon.toWebPBytes() withContext(Dispatchers.Main) { components.addonEvents.onUpdateWebExtensionIcon( - System.currentTimeMillis(), + EventSequence.next(), extension.id, WebExtensionActionType.BROWSER, imageBytes @@ -362,7 +363,7 @@ class GeckoTabsApiImpl : GeckoTabsApi { badgeBackgroundColor = pageAction.badgeBackgroundColor?.toLong(), ) components.addonEvents.onUpsertWebExtensionAction( - System.currentTimeMillis(), + EventSequence.next(), extension.id, WebExtensionActionType.PAGE, data @@ -377,7 +378,7 @@ class GeckoTabsApiImpl : GeckoTabsApi { val imageBytes = icon.toWebPBytes() withContext(Dispatchers.Main) { components.addonEvents.onUpdateWebExtensionIcon( - System.currentTimeMillis(), + EventSequence.next(), extension.id, WebExtensionActionType.PAGE, imageBytes diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/components/Events.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/components/Events.kt index 0ce58b40..af324799 100644 --- a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/components/Events.kt +++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/components/Events.kt @@ -6,7 +6,9 @@ package eu.weblibre.flutter_mozilla_components.components +import eu.weblibre.flutter_mozilla_components.GlobalComponents import eu.weblibre.flutter_mozilla_components.api.ReaderViewEventsImpl +import eu.weblibre.flutter_mozilla_components.ext.EventSequence import eu.weblibre.flutter_mozilla_components.ext.toWebPBytes import eu.weblibre.flutter_mozilla_components.pigeons.FindResultState import eu.weblibre.flutter_mozilla_components.pigeons.GeckoAddonEvents @@ -44,7 +46,7 @@ class Events( .debounce { 50 } .collect { tabId -> flutterEvents.onSelectedTabChange( - System.currentTimeMillis(), + EventSequence.next(), tabId ) { _ -> } } @@ -70,7 +72,7 @@ class Events( .collect { tab -> val iconBytes = tab.content.icon?.toWebPBytes() flutterEvents.onIconChange( - System.currentTimeMillis(), + EventSequence.next(), tab.id, iconBytes ) { _ -> } @@ -85,7 +87,7 @@ class Events( .debounce(15) .collect { tab -> flutterEvents.onSecurityInfoStateChange( - System.currentTimeMillis(), + EventSequence.next(), tab.id, SecurityInfoState( tab.content.securityInfo.isSecure, @@ -110,7 +112,7 @@ class Events( .debounce(25) .collect { tab -> flutterEvents.onReaderableStateChange( - System.currentTimeMillis(), + EventSequence.next(), tab.id, ReaderableState( tab.readerState.readerable, @@ -135,7 +137,7 @@ class Events( .debounce(15) .collect { tab -> flutterEvents.onHistoryStateChange( - System.currentTimeMillis(), + EventSequence.next(), tab.id, HistoryState( items = tab.content.history.items.map { item -> @@ -170,7 +172,7 @@ class Events( .debounce(15) .collect { tab -> flutterEvents.onTabContentStateChange( - System.currentTimeMillis(), + EventSequence.next(), TabContentState( id = tab.id, parentId = tab.parentId, diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/ext/EventSequence.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/ext/EventSequence.kt new file mode 100644 index 00000000..cf0256fd --- /dev/null +++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/ext/EventSequence.kt @@ -0,0 +1,22 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package eu.weblibre.flutter_mozilla_components.ext + +import java.util.concurrent.atomic.AtomicLong + +/** + * Thread-safe monotonic sequence counter for event ordering. + * + * Replaces System.currentTimeMillis() for event timestamps sent to Flutter, + * guaranteeing strictly increasing values regardless of wall-clock changes + * and eliminating same-millisecond collisions. + */ +object EventSequence { + private val counter = AtomicLong(0) + + fun next(): Long = counter.incrementAndGet() +} diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/feature/BrowserExtensionFeature.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/feature/BrowserExtensionFeature.kt index 713c4bb7..966bb203 100644 --- a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/feature/BrowserExtensionFeature.kt +++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/feature/BrowserExtensionFeature.kt @@ -7,6 +7,7 @@ package eu.weblibre.flutter_mozilla_components.feature import androidx.annotation.VisibleForTesting +import eu.weblibre.flutter_mozilla_components.ext.EventSequence import eu.weblibre.flutter_mozilla_components.pigeons.BrowserExtensionEvents import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking @@ -86,7 +87,7 @@ object BrowserExtensionFeature { runOnUiThread { extensionEvents.onFeedRequested( - System.currentTimeMillis(), + EventSequence.next(), url ) { _ -> } } diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/feature/ContainerProxyFeature.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/feature/ContainerProxyFeature.kt index 3268df75..9aec7c5e 100644 --- a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/feature/ContainerProxyFeature.kt +++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/feature/ContainerProxyFeature.kt @@ -8,6 +8,7 @@ package eu.weblibre.flutter_mozilla_components.feature import androidx.annotation.VisibleForTesting import eu.weblibre.flutter_mozilla_components.GlobalComponents +import eu.weblibre.flutter_mozilla_components.ext.EventSequence import eu.weblibre.flutter_mozilla_components.pigeons.ContainerSiteAssignment import eu.weblibre.flutter_mozilla_components.pigeons.GeckoStateEvents import kotlinx.coroutines.Dispatchers @@ -115,7 +116,7 @@ object ContainerProxyFeature { if (status == "success") { runOnUiThread { events.onContainerSiteAssignment( - System.currentTimeMillis(), + EventSequence.next(), ContainerSiteAssignment( requestId = requestId, tabId = components.core.store.state.selectedTabId, diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/feature/KeyboardVisibilityFeature.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/feature/KeyboardVisibilityFeature.kt index 63634ad5..33005c78 100644 --- a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/feature/KeyboardVisibilityFeature.kt +++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/feature/KeyboardVisibilityFeature.kt @@ -14,6 +14,7 @@ import androidx.core.graphics.Insets import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsAnimationCompat import androidx.core.view.WindowInsetsCompat +import eu.weblibre.flutter_mozilla_components.ext.EventSequence import eu.weblibre.flutter_mozilla_components.pigeons.GeckoViewportEvents import mozilla.components.support.base.log.logger.Logger import org.mozilla.gecko.util.ThreadUtils.runOnUiThread @@ -185,7 +186,7 @@ class KeyboardVisibilityFeature( lastKeyboardHeight = heightPx lastKeyboardVisible = isVisible - val timestamp = System.currentTimeMillis() + val timestamp = EventSequence.next() logger.debug("$TAG: Keyboard change - height=$heightPx, visible=$isVisible, animating=$isAnimating") diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/feature/ReadabilityExtractFeature.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/feature/ReadabilityExtractFeature.kt index 31ec0bd0..3eca7fcb 100644 --- a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/feature/ReadabilityExtractFeature.kt +++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/feature/ReadabilityExtractFeature.kt @@ -6,6 +6,7 @@ package eu.weblibre.flutter_mozilla_components.feature +import eu.weblibre.flutter_mozilla_components.ext.EventSequence import eu.weblibre.flutter_mozilla_components.pigeons.GeckoTabContentEvents import eu.weblibre.flutter_mozilla_components.pigeons.TabContent import kotlinx.coroutines.CoroutineScope @@ -50,7 +51,7 @@ class ReadabilityExtractFeature( override fun onPortMessage(message: Any, port: Port) { if (message is JSONObject) { tabContentEvents.onContentUpdate( - System.currentTimeMillis(), + EventSequence.next(), contentArg = TabContent( tabId = tabId, fullContentMarkdown = message.tryGetString("fullContentMarkdown"), diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/feature/WebExtensionToolbarFeature.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/feature/WebExtensionToolbarFeature.kt index b195260c..346ee5f4 100644 --- a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/feature/WebExtensionToolbarFeature.kt +++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/feature/WebExtensionToolbarFeature.kt @@ -7,6 +7,7 @@ package eu.weblibre.flutter_mozilla_components.feature import android.os.Handler import android.os.HandlerThread import androidx.annotation.VisibleForTesting +import eu.weblibre.flutter_mozilla_components.ext.EventSequence import eu.weblibre.flutter_mozilla_components.ext.toWebPBytes import eu.weblibre.flutter_mozilla_components.pigeons.GeckoAddonEvents import eu.weblibre.flutter_mozilla_components.pigeons.WebExtensionActionType @@ -66,7 +67,7 @@ class WebExtensionToolbarFeature( .filter { !store.state.extensions.containsKey(it) || store.state.extensions[it]?.enabled == false } .forEach { extensionId -> addonEvents.onRemoveWebExtensionAction( - timestampArg = System.currentTimeMillis(), + timestampArg = EventSequence.next(), extensionIdArg = extensionId, actionTypeArg = WebExtensionActionType.BROWSER, ) {} @@ -77,7 +78,7 @@ class WebExtensionToolbarFeature( .filter { !store.state.extensions.containsKey(it) || store.state.extensions[it]?.enabled == false } .forEach { extensionId -> addonEvents.onRemoveWebExtensionAction( - timestampArg = System.currentTimeMillis(), + timestampArg = EventSequence.next(), extensionIdArg = extensionId, actionTypeArg = WebExtensionActionType.PAGE, ) {} @@ -106,7 +107,7 @@ class WebExtensionToolbarFeature( webExtensionBrowserActions.keys.toList().forEach { extensionId -> if (extensionId !in enabledExtensionIds) { addonEvents.onRemoveWebExtensionAction( - timestampArg = System.currentTimeMillis(), + timestampArg = EventSequence.next(), extensionIdArg = extensionId, actionTypeArg = WebExtensionActionType.BROWSER, ) {} @@ -117,7 +118,7 @@ class WebExtensionToolbarFeature( webExtensionPageActions.keys.toList().forEach { extensionId -> if (extensionId !in enabledExtensionIds) { addonEvents.onRemoveWebExtensionAction( - timestampArg = System.currentTimeMillis(), + timestampArg = EventSequence.next(), extensionIdArg = extensionId, actionTypeArg = WebExtensionActionType.PAGE, ) {} @@ -130,7 +131,7 @@ class WebExtensionToolbarFeature( if (extensionNotAllowedInTab(extension, tab)) { webExtensionPageActions[extension.id]?.let { addonEvents.onRemoveWebExtensionAction( - timestampArg = System.currentTimeMillis(), + timestampArg = EventSequence.next(), extensionIdArg = extension.id, actionTypeArg = WebExtensionActionType.PAGE, ) {} @@ -138,7 +139,7 @@ class WebExtensionToolbarFeature( } webExtensionBrowserActions[extension.id]?.let { addonEvents.onRemoveWebExtensionAction( - timestampArg = System.currentTimeMillis(), + timestampArg = EventSequence.next(), extensionIdArg = extension.id, actionTypeArg = WebExtensionActionType.BROWSER, ) {} @@ -220,7 +221,7 @@ class WebExtensionToolbarFeature( ) addonEvents.onUpsertWebExtensionAction( - timestampArg = System.currentTimeMillis(), + timestampArg = EventSequence.next(), extensionIdArg = extension.id, actionTypeArg = if (isPageAction) WebExtensionActionType.PAGE else WebExtensionActionType.BROWSER, extensionDataArg = data @@ -235,7 +236,7 @@ class WebExtensionToolbarFeature( val imageBytes = icon.toWebPBytes() runOnUiThread { addonEvents.onUpdateWebExtensionIcon( - timestampArg = System.currentTimeMillis(), + timestampArg = EventSequence.next(), extensionIdArg = extensionId, actionTypeArg = if (isPageAction) WebExtensionActionType.PAGE else WebExtensionActionType.BROWSER, iconArg = imageBytes diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/integration/ReaderViewIntegration.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/integration/ReaderViewIntegration.kt index 357474fc..03434f73 100644 --- a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/integration/ReaderViewIntegration.kt +++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/integration/ReaderViewIntegration.kt @@ -10,6 +10,7 @@ import android.content.Context import android.graphics.drawable.Drawable import androidx.core.content.ContextCompat import eu.weblibre.flutter_mozilla_components.GlobalComponents +import eu.weblibre.flutter_mozilla_components.ext.EventSequence import eu.weblibre.flutter_mozilla_components.api.ReaderViewEventsImpl import eu.weblibre.flutter_mozilla_components.api.ReaderViewControllerListener import eu.weblibre.flutter_mozilla_components.pigeons.ReaderViewController @@ -36,12 +37,12 @@ class ReaderViewIntegration( if (enabled) { feature.showReaderView() - readerViewController.appearanceButtonVisibility(System.currentTimeMillis(),true) { _ -> }; + readerViewController.appearanceButtonVisibility(EventSequence.next(),true) { _ -> }; } else { feature.hideReaderView() feature.hideControls() - readerViewController.appearanceButtonVisibility(System.currentTimeMillis(),false) { _ -> }; + readerViewController.appearanceButtonVisibility(EventSequence.next(),false) { _ -> }; } } diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/middleware/FlutterEventMiddleware.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/middleware/FlutterEventMiddleware.kt index 6ddf30a5..7095a5c8 100644 --- a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/middleware/FlutterEventMiddleware.kt +++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/middleware/FlutterEventMiddleware.kt @@ -6,6 +6,7 @@ package eu.weblibre.flutter_mozilla_components.middleware import android.graphics.Bitmap import android.util.Log import eu.weblibre.flutter_mozilla_components.GlobalComponents +import eu.weblibre.flutter_mozilla_components.ext.EventSequence import eu.weblibre.flutter_mozilla_components.ext.resize import eu.weblibre.flutter_mozilla_components.ext.toWebPBytes import eu.weblibre.flutter_mozilla_components.pigeons.AudioHitResult @@ -55,7 +56,7 @@ class FlutterEventMiddleware(private val flutterEvents: GeckoStateEvents) : Midd val bytes = resized.toWebPBytes() runOnUiThread { - flutterEvents.onThumbnailChange(System.currentTimeMillis(), action.sessionId, bytes) { _ -> } + flutterEvents.onThumbnailChange(EventSequence.next(), action.sessionId, bytes) { _ -> } } } //UpdateReaderConnectRequiredAction seems to be the only event that is called predictable @@ -64,7 +65,7 @@ class FlutterEventMiddleware(private val flutterEvents: GeckoStateEvents) : Midd if(!components.engineReportedInitialized) { runOnUiThread { flutterEvents.onEngineReadyStateChange( - System.currentTimeMillis(), + EventSequence.next(), true ) { _ -> } } @@ -75,7 +76,7 @@ class FlutterEventMiddleware(private val flutterEvents: GeckoStateEvents) : Midd is TabListAction.AddTabAction -> { runOnUiThread { flutterEvents.onTabAdded( - System.currentTimeMillis(), + EventSequence.next(), action.tab.id ) { _ -> } } @@ -85,7 +86,7 @@ class FlutterEventMiddleware(private val flutterEvents: GeckoStateEvents) : Midd runOnUiThread { flutterEvents.onIconUpdate( - System.currentTimeMillis(), + EventSequence.next(), action.pageUrl, bytes ) { _ -> } @@ -94,7 +95,7 @@ class FlutterEventMiddleware(private val flutterEvents: GeckoStateEvents) : Midd is ContentAction.UpdateHitResultAction -> { runOnUiThread { flutterEvents.onLongPress( - System.currentTimeMillis(), + EventSequence.next(), action.sessionId, when(val result = action.hitResult) { is HitResult.AUDIO -> AudioHitResult(result.src, result.title) @@ -112,7 +113,7 @@ class FlutterEventMiddleware(private val flutterEvents: GeckoStateEvents) : Midd is ContentAction.AddFindResultAction -> { runOnUiThread { flutterEvents.onFindResults( - System.currentTimeMillis(), + EventSequence.next(), action.sessionId, listOf( FindResultState( @@ -127,7 +128,7 @@ class FlutterEventMiddleware(private val flutterEvents: GeckoStateEvents) : Midd is ContentAction.ClearFindResultsAction -> { runOnUiThread { flutterEvents.onFindResults( - System.currentTimeMillis(), + EventSequence.next(), action.sessionId, listOf() ) { _ -> } @@ -136,7 +137,7 @@ class FlutterEventMiddleware(private val flutterEvents: GeckoStateEvents) : Midd // is ReaderAction.UpdateReaderScrollYAction -> { // runOnUiThread { // flutterEvents.onScrollChange( -// System.currentTimeMillis(), +// EventSequence.next(), // action.tabId, // action.scrollY.toLong() // ) { _ -> } diff --git a/packages/flutter_mozilla_components/lib/src/domain/services/gecko_addon.dart b/packages/flutter_mozilla_components/lib/src/domain/services/gecko_addon.dart index 5645ebf4..4112cb67 100644 --- a/packages/flutter_mozilla_components/lib/src/domain/services/gecko_addon.dart +++ b/packages/flutter_mozilla_components/lib/src/domain/services/gecko_addon.dart @@ -51,18 +51,18 @@ class GeckoAddonService extends GeckoAddonEvents { @override void onRemoveWebExtensionAction( - int timestamp, + int sequence, String extensionId, WebExtensionActionType actionType, ) { switch (actionType) { case WebExtensionActionType.browser: - _browserExtensionSubject.addWhenMoreRecent(timestamp, extensionId, ( + _browserExtensionSubject.addWhenMoreRecent(sequence, extensionId, ( extensionId: extensionId, data: null, )); case WebExtensionActionType.page: - _pageExtensionSubject.addWhenMoreRecent(timestamp, extensionId, ( + _pageExtensionSubject.addWhenMoreRecent(sequence, extensionId, ( extensionId: extensionId, data: null, )); @@ -71,19 +71,19 @@ class GeckoAddonService extends GeckoAddonEvents { @override void onUpdateWebExtensionIcon( - int timestamp, + int sequence, String extensionId, WebExtensionActionType actionType, Uint8List icon, ) { switch (actionType) { case WebExtensionActionType.browser: - _browserIconSubject.addWhenMoreRecent(timestamp, extensionId, ( + _browserIconSubject.addWhenMoreRecent(sequence, extensionId, ( extensionId: extensionId, bytes: icon, )); case WebExtensionActionType.page: - _pageIconSubject.addWhenMoreRecent(timestamp, extensionId, ( + _pageIconSubject.addWhenMoreRecent(sequence, extensionId, ( extensionId: extensionId, bytes: icon, )); @@ -92,19 +92,19 @@ class GeckoAddonService extends GeckoAddonEvents { @override void onUpsertWebExtensionAction( - int timestamp, + int sequence, String extensionId, WebExtensionActionType actionType, WebExtensionData extensionData, ) { switch (actionType) { case WebExtensionActionType.browser: - _browserExtensionSubject.addWhenMoreRecent(timestamp, extensionId, ( + _browserExtensionSubject.addWhenMoreRecent(sequence, extensionId, ( extensionId: extensionId, data: extensionData, )); case WebExtensionActionType.page: - _pageExtensionSubject.addWhenMoreRecent(timestamp, extensionId, ( + _pageExtensionSubject.addWhenMoreRecent(sequence, extensionId, ( extensionId: extensionId, data: extensionData, )); diff --git a/packages/flutter_mozilla_components/lib/src/domain/services/gecko_browser_extension.dart b/packages/flutter_mozilla_components/lib/src/domain/services/gecko_browser_extension.dart index 53ff5f74..6e6b82d6 100644 --- a/packages/flutter_mozilla_components/lib/src/domain/services/gecko_browser_extension.dart +++ b/packages/flutter_mozilla_components/lib/src/domain/services/gecko_browser_extension.dart @@ -58,8 +58,8 @@ class GeckoBrowserExtensionService extends BrowserExtensionEvents { } @override - void onFeedRequested(int timestamp, String url) { - _feedRequest.addWhenMoreRecent(timestamp, null, url); + void onFeedRequested(int sequence, String url) { + _feedRequest.addWhenMoreRecent(sequence, null, url); } void dispose() { diff --git a/packages/flutter_mozilla_components/lib/src/domain/services/gecko_event.dart b/packages/flutter_mozilla_components/lib/src/domain/services/gecko_event.dart index 1919ed25..7cdcd5ff 100644 --- a/packages/flutter_mozilla_components/lib/src/domain/services/gecko_event.dart +++ b/packages/flutter_mozilla_components/lib/src/domain/services/gecko_event.dart @@ -20,6 +20,7 @@ typedef ThumbnailEvent = ({String tabId, Uint8List? bytes}); typedef FindResultsEvent = ({String tabId, List results}); typedef LongPressEvent = ({String tabId, HitResult hitResult}); typedef ScrollEvent = ({String tabId, int scrollY}); +typedef ManifestUpdateEvent = ({String tabId, PwaManifest? manifest}); class GeckoEventService extends GeckoStateEvents { // Stream controllers @@ -44,6 +45,7 @@ class GeckoEventService extends GeckoStateEvents { final _tabAddedSubject = PublishSubject(); final _mlProgressSubject = PublishSubject(); + final _manifestUpdateSubject = PublishSubject(); // Event streams ValueStream get viewReadyStateEvents => _viewStateSubject.stream; @@ -68,52 +70,46 @@ class GeckoEventService extends GeckoStateEvents { Stream get tabAddedStream => _tabAddedSubject.stream; Stream get mlProgressEvents => _mlProgressSubject.stream; + Stream get manifestUpdateEvents => + _manifestUpdateSubject.stream; @override - void onViewReadyStateChange(int timestamp, bool state) { - _viewStateSubject.addWhenMoreRecent(timestamp, null, state); + void onViewReadyStateChange(int sequence, bool state) { + _viewStateSubject.addWhenMoreRecent(sequence, null, state); } @override - void onEngineReadyStateChange(int timestamp, bool state) { - _engineStateSubject.addWhenMoreRecent(timestamp, null, state); + void onEngineReadyStateChange(int sequence, bool state) { + _engineStateSubject.addWhenMoreRecent(sequence, null, state); } // Overridden methods @override - void onTabListChange(int timestamp, List tabIds) { - _tabListSubject.addWhenMoreRecent( - timestamp, - null, - tabIds.nonNulls.toList(), - ); + void onTabListChange(int sequence, List tabIds) { + _tabListSubject.addWhenMoreRecent(sequence, null, tabIds.nonNulls.toList()); } @override - void onSelectedTabChange(int timestamp, String? id) { - _selectedTabSubject.addWhenMoreRecent(timestamp, id, id); + void onSelectedTabChange(int sequence, String? id) { + _selectedTabSubject.addWhenMoreRecent(sequence, id, id); } @override - void onTabContentStateChange(int timestamp, TabContentState state) { - _tabContentSubject.addWhenMoreRecent(timestamp, state.id, state); + void onTabContentStateChange(int sequence, TabContentState state) { + _tabContentSubject.addWhenMoreRecent(sequence, state.id, state); } @override - void onHistoryStateChange(int timestamp, String id, HistoryState state) { - _historySubject.addWhenMoreRecent(timestamp, id, ( + void onHistoryStateChange(int sequence, String id, HistoryState state) { + _historySubject.addWhenMoreRecent(sequence, id, ( tabId: id, history: state, )); } @override - void onReaderableStateChange( - int timestamp, - String id, - ReaderableState state, - ) { - _readerableSubject.addWhenMoreRecent(timestamp, id, ( + void onReaderableStateChange(int sequence, String id, ReaderableState state) { + _readerableSubject.addWhenMoreRecent(sequence, id, ( tabId: id, readerable: state, )); @@ -121,87 +117,91 @@ class GeckoEventService extends GeckoStateEvents { @override void onSecurityInfoStateChange( - int timestamp, + int sequence, String id, SecurityInfoState state, ) { - _securityInfoSubject.addWhenMoreRecent(timestamp, id, ( + _securityInfoSubject.addWhenMoreRecent(sequence, id, ( tabId: id, securityInfo: state, )); } @override - void onIconChange(int timestamp, String id, Uint8List? bytes) { - _iconChangeSubject.addWhenMoreRecent(timestamp, id, ( + void onIconChange(int sequence, String id, Uint8List? bytes) { + _iconChangeSubject.addWhenMoreRecent(sequence, id, ( tabId: id, bytes: bytes, )); } @override - void onIconUpdate(int timestamp, String url, Uint8List bytes) { - _iconUpdateSubject.addWhenMoreRecent(timestamp, url, ( + void onIconUpdate(int sequence, String url, Uint8List bytes) { + _iconUpdateSubject.addWhenMoreRecent(sequence, url, ( url: url, bytes: bytes, )); } @override - void onThumbnailChange(int timestamp, String id, Uint8List? bytes) { - _thumbnailSubject.addWhenMoreRecent(timestamp, id, ( + void onThumbnailChange(int sequence, String id, Uint8List? bytes) { + _thumbnailSubject.addWhenMoreRecent(sequence, id, ( tabId: id, bytes: bytes, )); } @override - void onFindResults(int timestamp, String id, List results) { - _findResultsSubject.addWhenMoreRecent(timestamp, id, ( + void onFindResults(int sequence, String id, List results) { + _findResultsSubject.addWhenMoreRecent(sequence, id, ( tabId: id, results: results.nonNulls.toList(), )); } @override - void onLongPress(int timestamp, String id, HitResult hitResult) { - _longPressSubject.addWhenMoreRecent(timestamp, id, ( + void onLongPress(int sequence, String id, HitResult hitResult) { + _longPressSubject.addWhenMoreRecent(sequence, id, ( tabId: id, hitResult: hitResult, )); } @override - void onTabAdded(int timestamp, String tabId) { - _tabAddedSubject.addWhenMoreRecent(timestamp, null, tabId); + void onTabAdded(int sequence, String tabId) { + _tabAddedSubject.addWhenMoreRecent(sequence, null, tabId); } // @override - // void onScrollChange(int timestamp, String tabId, int scrollY) { - // _scrollEventSubject.addWhenMoreRecent(timestamp, tabId, ( + // void onScrollChange(int sequence, String tabId, int scrollY) { + // _scrollEventSubject.addWhenMoreRecent(sequence, tabId, ( // tabId: tabId, // scrollY: scrollY, // )); // } @override - void onPreferenceChange(int timestamp, GeckoPref value) { - _prefUpdateSubject.addWhenMoreRecent(timestamp, value.name, value); + void onPreferenceChange(int sequence, GeckoPref value) { + _prefUpdateSubject.addWhenMoreRecent(sequence, value.name, value); } @override void onContainerSiteAssignment( - int timestamp, + int sequence, ContainerSiteAssignment details, ) { _siteAssignementSubject.addWhenMoreRecent( - timestamp, + sequence, details.requestId, details, ); } @override + void onMlProgress(int sequence, MlProgressData progress) { + _mlProgressSubject.addWhenMoreRecent(sequence, null, progress); + } + void onMlProgress(int timestamp, MlProgressData progress) { _mlProgressSubject.addWhenMoreRecent(timestamp, null, progress); } @@ -236,5 +236,6 @@ class GeckoEventService extends GeckoStateEvents { await _prefUpdateSubject.close(); await _siteAssignementSubject.close(); await _mlProgressSubject.close(); + await _manifestUpdateSubject.close(); } } diff --git a/packages/flutter_mozilla_components/lib/src/domain/services/gecko_readerable.dart b/packages/flutter_mozilla_components/lib/src/domain/services/gecko_readerable.dart index ff317967..077dfb20 100644 --- a/packages/flutter_mozilla_components/lib/src/domain/services/gecko_readerable.dart +++ b/packages/flutter_mozilla_components/lib/src/domain/services/gecko_readerable.dart @@ -27,8 +27,8 @@ class GeckoReaderableService extends ReaderViewController { } @override - void appearanceButtonVisibility(int timestamp, bool visible) { - _appearanceVisibility.addWhenMoreRecent(timestamp, null, visible); + void appearanceButtonVisibility(int sequence, bool visible) { + _appearanceVisibility.addWhenMoreRecent(sequence, null, visible); } GeckoReaderableService.setUp({ diff --git a/packages/flutter_mozilla_components/lib/src/domain/services/gecko_suggestions.dart b/packages/flutter_mozilla_components/lib/src/domain/services/gecko_suggestions.dart index ca96720e..c186d450 100644 --- a/packages/flutter_mozilla_components/lib/src/domain/services/gecko_suggestions.dart +++ b/packages/flutter_mozilla_components/lib/src/domain/services/gecko_suggestions.dart @@ -54,12 +54,12 @@ class GeckoSuggestionsService extends GeckoSuggestionEvents { @override void onSuggestionResult( - int timestamp, + int sequence, GeckoSuggestionType suggestionType, List suggestions, ) { _suggestionsSubject.addWhenMoreRecent( - timestamp, + sequence, suggestionType, suggestions, ); diff --git a/packages/flutter_mozilla_components/lib/src/domain/services/gecko_tab_content.dart b/packages/flutter_mozilla_components/lib/src/domain/services/gecko_tab_content.dart index 0f226701..46ca1bbb 100644 --- a/packages/flutter_mozilla_components/lib/src/domain/services/gecko_tab_content.dart +++ b/packages/flutter_mozilla_components/lib/src/domain/services/gecko_tab_content.dart @@ -28,8 +28,8 @@ class GeckoTabContentService extends GeckoTabContentEvents { } @override - void onContentUpdate(int timestamp, TabContent content) { - _contentSubject.addWhenMoreRecent(timestamp, content.tabId, content); + void onContentUpdate(int sequence, TabContent content) { + _contentSubject.addWhenMoreRecent(sequence, content.tabId, content); } Future dispose() async { diff --git a/packages/flutter_mozilla_components/lib/src/domain/services/gecko_viewport.dart b/packages/flutter_mozilla_components/lib/src/domain/services/gecko_viewport.dart index 1a2cc257..1f0fdd39 100644 --- a/packages/flutter_mozilla_components/lib/src/domain/services/gecko_viewport.dart +++ b/packages/flutter_mozilla_components/lib/src/domain/services/gecko_viewport.dart @@ -101,12 +101,12 @@ class GeckoViewportService extends GeckoViewportEvents { @override void onKeyboardVisibilityChanged( - int timestamp, + int sequence, int heightPx, bool isVisible, bool isAnimating, ) { - _keyboardSubject.addWhenMoreRecent(timestamp, null, ( + _keyboardSubject.addWhenMoreRecent(sequence, null, ( heightPx: heightPx, isVisible: isVisible, isAnimating: isAnimating, diff --git a/packages/flutter_mozilla_components/lib/src/extensions/subject.dart b/packages/flutter_mozilla_components/lib/src/extensions/subject.dart index 49b0721a..264438cf 100644 --- a/packages/flutter_mozilla_components/lib/src/extensions/subject.dart +++ b/packages/flutter_mozilla_components/lib/src/extensions/subject.dart @@ -9,17 +9,29 @@ import 'package:rxdart/rxdart.dart'; final _lastEventTimes = >{}; extension SubjectAddRecent on Subject { - void addWhenMoreRecent(int timestamp, dynamic identifier, T value) { + /// Adds [value] to this Subject only if [sequence] is greater than + /// the last sequence number for this [identifier]. + /// + /// Uses atomic event sequence numbers (from native EventSequence) to ensure + /// events are processed in order and prevent out-of-order updates. + /// Sequence numbers are monotonically increasing integers (0, 1, 2, ...). + void addWhenMoreRecent(int sequence, dynamic identifier, T value) { _lastEventTimes[this] ??= {}; - if ((_lastEventTimes[this]?[identifier] ?? 0) < timestamp) { - _lastEventTimes[this]![identifier] = timestamp; + if ((_lastEventTimes[this]?[identifier] ?? 0) < sequence) { + _lastEventTimes[this]![identifier] = sequence; add(value); } } + /// Updates this BehaviorSubject only if [sequence] is greater than + /// the last sequence number for this [identifier]. + /// + /// Uses atomic event sequence numbers (from native EventSequence) to ensure + /// events are processed in order and prevent out-of-order updates. + /// Sequence numbers are monotonically increasing integers (0, 1, 2, ...). void updateWhenMoreRecent( - int timestamp, + int sequence, dynamic identifier, T Function(T? currentValue) update, ) { @@ -27,8 +39,8 @@ extension SubjectAddRecent on Subject { _lastEventTimes[this] ??= {}; - if ((_lastEventTimes[this]?[identifier] ?? 0) < timestamp) { - _lastEventTimes[this]![identifier] = timestamp; + if ((_lastEventTimes[this]?[identifier] ?? 0) < sequence) { + _lastEventTimes[this]![identifier] = sequence; add(update((this as BehaviorSubject).valueOrNull)); } } diff --git a/packages/flutter_mozilla_components/lib/src/pigeons/gecko.g.dart b/packages/flutter_mozilla_components/lib/src/pigeons/gecko.g.dart index 3fdee041..015a26c8 100644 --- a/packages/flutter_mozilla_components/lib/src/pigeons/gecko.g.dart +++ b/packages/flutter_mozilla_components/lib/src/pigeons/gecko.g.dart @@ -3762,6 +3762,398 @@ class TrackingProtectionException { ; } +/// Represents an icon from a PWA manifest. +class PwaIcon { + PwaIcon({ + required this.src, + this.sizes, + this.type, + }); + + String src; + + String? sizes; + + String? type; + + List _toList() { + return [ + src, + sizes, + type, + ]; + } + + Object encode() { + return _toList(); } + + static PwaIcon decode(Object result) { + result as List; + return PwaIcon( + src: result[0]! as String, + sizes: result[1] as String?, + type: result[2] as String?, + ); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! PwaIcon || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(encode(), other.encode()); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => Object.hashAll(_toList()) +; +} + +/// Represents a file entry in share target params. +class ShareTargetFiles { + ShareTargetFiles({ + required this.name, + required this.accept, + }); + + String name; + + List accept; + + List _toList() { + return [ + name, + accept, + ]; + } + + Object encode() { + return _toList(); } + + static ShareTargetFiles decode(Object result) { + result as List; + return ShareTargetFiles( + name: result[0]! as String, + accept: (result[1] as List?)!.cast(), + ); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! ShareTargetFiles || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(encode(), other.encode()); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => Object.hashAll(_toList()) +; +} + +/// Represents share target params. +class ShareTargetParams { + ShareTargetParams({ + this.title, + this.text, + this.url, + required this.files, + }); + + String? title; + + String? text; + + String? url; + + List files; + + List _toList() { + return [ + title, + text, + url, + files, + ]; + } + + Object encode() { + return _toList(); } + + static ShareTargetParams decode(Object result) { + result as List; + return ShareTargetParams( + title: result[0] as String?, + text: result[1] as String?, + url: result[2] as String?, + files: (result[3] as List?)!.cast(), + ); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! ShareTargetParams || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(encode(), other.encode()); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => Object.hashAll(_toList()) +; +} + +/// Represents a share target for PWA. +class ShareTarget { + ShareTarget({ + required this.action, + this.method, + this.encType, + this.params, + }); + + String action; + + String? method; + + String? encType; + + ShareTargetParams? params; + + List _toList() { + return [ + action, + method, + encType, + params, + ]; + } + + Object encode() { + return _toList(); } + + static ShareTarget decode(Object result) { + result as List; + return ShareTarget( + action: result[0]! as String, + method: result[1] as String?, + encType: result[2] as String?, + params: result[3] as ShareTargetParams?, + ); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! ShareTarget || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(encode(), other.encode()); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => Object.hashAll(_toList()) +; +} + +/// Represents an external application resource. +class ExternalApplicationResource { + ExternalApplicationResource({ + required this.platform, + this.url, + this.id, + this.minVersion, + }); + + String platform; + + String? url; + + String? id; + + String? minVersion; + + List _toList() { + return [ + platform, + url, + id, + minVersion, + ]; + } + + Object encode() { + return _toList(); } + + static ExternalApplicationResource decode(Object result) { + result as List; + return ExternalApplicationResource( + platform: result[0]! as String, + url: result[1] as String?, + id: result[2] as String?, + minVersion: result[3] as String?, + ); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! ExternalApplicationResource || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(encode(), other.encode()); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => Object.hashAll(_toList()) +; +} + +/// Represents a PWA web app manifest. +/// +/// Mirrors Mozilla Android Components' WebAppManifest structure. +/// https://firefox-source-docs.mozilla.org/mobile/android/geckoview/api/mozilla.components.concept.engine.manifest.WebAppManifest.html +class PwaManifest { + PwaManifest({ + required this.startUrl, + this.name, + this.shortName, + this.display, + this.themeColor, + this.backgroundColor, + this.scope, + this.description, + required this.icons, + this.dir, + this.lang, + this.orientation, + required this.relatedApplications, + required this.preferRelatedApplications, + this.shareTarget, + required this.currentUrl, + }); + + String startUrl; + + String? name; + + String? shortName; + + String? display; + + String? themeColor; + + String? backgroundColor; + + String? scope; + + String? description; + + List icons; + + String? dir; + + String? lang; + + String? orientation; + + List relatedApplications; + + bool preferRelatedApplications; + + ShareTarget? shareTarget; + + /// The URL of the page when the manifest was detected. + /// Used for HTTPS/installability checks. + String currentUrl; + + List _toList() { + return [ + startUrl, + name, + shortName, + display, + themeColor, + backgroundColor, + scope, + description, + icons, + dir, + lang, + orientation, + relatedApplications, + preferRelatedApplications, + shareTarget, + currentUrl, + ]; + } + + Object encode() { + return _toList(); } + + static PwaManifest decode(Object result) { + result as List; + return PwaManifest( + startUrl: result[0]! as String, + name: result[1] as String?, + shortName: result[2] as String?, + display: result[3] as String?, + themeColor: result[4] as String?, + backgroundColor: result[5] as String?, + scope: result[6] as String?, + description: result[7] as String?, + icons: (result[8] as List?)!.cast(), + dir: result[9] as String?, + lang: result[10] as String?, + orientation: result[11] as String?, + relatedApplications: (result[12] as List?)!.cast(), + preferRelatedApplications: result[13]! as bool, + shareTarget: result[14] as ShareTarget?, + currentUrl: result[15]! as String, + ); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! PwaManifest || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(encode(), other.encode()); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => Object.hashAll(_toList()) +; +} + class _PigeonCodec extends StandardMessageCodec { const _PigeonCodec(); @@ -4025,6 +4417,24 @@ class _PigeonCodec extends StandardMessageCodec { } else if (value is TrackingProtectionException) { buffer.putUint8(213); writeValue(buffer, value.encode()); + } else if (value is PwaIcon) { + buffer.putUint8(214); + writeValue(buffer, value.encode()); + } else if (value is ShareTargetFiles) { + buffer.putUint8(215); + writeValue(buffer, value.encode()); + } else if (value is ShareTargetParams) { + buffer.putUint8(216); + writeValue(buffer, value.encode()); + } else if (value is ShareTarget) { + buffer.putUint8(217); + writeValue(buffer, value.encode()); + } else if (value is ExternalApplicationResource) { + buffer.putUint8(218); + writeValue(buffer, value.encode()); + } else if (value is PwaManifest) { + buffer.putUint8(219); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -4234,6 +4644,18 @@ class _PigeonCodec extends StandardMessageCodec { return SitePermissions.decode(readValue(buffer)!); case 213: return TrackingProtectionException.decode(readValue(buffer)!); + case 214: + return PwaIcon.decode(readValue(buffer)!); + case 215: + return ShareTargetFiles.decode(readValue(buffer)!); + case 216: + return ShareTargetParams.decode(readValue(buffer)!); + case 217: + return ShareTarget.decode(readValue(buffer)!); + case 218: + return ExternalApplicationResource.decode(readValue(buffer)!); + case 219: + return PwaManifest.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); } @@ -6022,39 +6444,41 @@ class GeckoCookieApi { abstract class GeckoStateEvents { static const MessageCodec pigeonChannelCodec = _PigeonCodec(); - void onViewReadyStateChange(int timestamp, bool state); + void onViewReadyStateChange(int sequence, bool state); - void onEngineReadyStateChange(int timestamp, bool state); + void onEngineReadyStateChange(int sequence, bool state); - void onIconUpdate(int timestamp, String url, Uint8List bytes); + void onIconUpdate(int sequence, String url, Uint8List bytes); - void onTabAdded(int timestamp, String tabId); + void onTabAdded(int sequence, String tabId); - void onTabListChange(int timestamp, List tabIds); + void onTabListChange(int sequence, List tabIds); - void onSelectedTabChange(int timestamp, String? id); + void onSelectedTabChange(int sequence, String? id); - void onTabContentStateChange(int timestamp, TabContentState state); + void onTabContentStateChange(int sequence, TabContentState state); - void onHistoryStateChange(int timestamp, String id, HistoryState state); + void onHistoryStateChange(int sequence, String id, HistoryState state); - void onReaderableStateChange(int timestamp, String id, ReaderableState state); + void onReaderableStateChange(int sequence, String id, ReaderableState state); - void onSecurityInfoStateChange(int timestamp, String id, SecurityInfoState state); + void onSecurityInfoStateChange(int sequence, String id, SecurityInfoState state); - void onIconChange(int timestamp, String id, Uint8List? bytes); + void onIconChange(int sequence, String id, Uint8List? bytes); - void onThumbnailChange(int timestamp, String id, Uint8List? bytes); + void onThumbnailChange(int sequence, String id, Uint8List? bytes); - void onFindResults(int timestamp, String id, List results); + void onFindResults(int sequence, String id, List results); - void onLongPress(int timestamp, String id, HitResult hitResult); + void onLongPress(int sequence, String id, HitResult hitResult); - void onPreferenceChange(int timestamp, GeckoPref value); + void onPreferenceChange(int sequence, GeckoPref value); - void onContainerSiteAssignment(int timestamp, ContainerSiteAssignment details); + void onContainerSiteAssignment(int sequence, ContainerSiteAssignment details); - void onMlProgress(int timestamp, MlProgressData progress); + void onMlProgress(int sequence, MlProgressData progress); + + void onManifestUpdate(int sequence, String tabId, PwaManifest? manifest); static void setUp(GeckoStateEvents? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) { messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; @@ -6069,14 +6493,14 @@ abstract class GeckoStateEvents { assert(message != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onViewReadyStateChange was null.'); final List args = (message as List?)!; - final int? arg_timestamp = (args[0] as int?); - assert(arg_timestamp != null, + final int? arg_sequence = (args[0] as int?); + assert(arg_sequence != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onViewReadyStateChange was null, expected non-null int.'); final bool? arg_state = (args[1] as bool?); assert(arg_state != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onViewReadyStateChange was null, expected non-null bool.'); try { - api.onViewReadyStateChange(arg_timestamp!, arg_state!); + api.onViewReadyStateChange(arg_sequence!, arg_state!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -6097,14 +6521,14 @@ abstract class GeckoStateEvents { assert(message != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onEngineReadyStateChange was null.'); final List args = (message as List?)!; - final int? arg_timestamp = (args[0] as int?); - assert(arg_timestamp != null, + final int? arg_sequence = (args[0] as int?); + assert(arg_sequence != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onEngineReadyStateChange was null, expected non-null int.'); final bool? arg_state = (args[1] as bool?); assert(arg_state != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onEngineReadyStateChange was null, expected non-null bool.'); try { - api.onEngineReadyStateChange(arg_timestamp!, arg_state!); + api.onEngineReadyStateChange(arg_sequence!, arg_state!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -6125,8 +6549,8 @@ abstract class GeckoStateEvents { assert(message != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onIconUpdate was null.'); final List args = (message as List?)!; - final int? arg_timestamp = (args[0] as int?); - assert(arg_timestamp != null, + final int? arg_sequence = (args[0] as int?); + assert(arg_sequence != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onIconUpdate was null, expected non-null int.'); final String? arg_url = (args[1] as String?); assert(arg_url != null, @@ -6135,7 +6559,7 @@ abstract class GeckoStateEvents { assert(arg_bytes != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onIconUpdate was null, expected non-null Uint8List.'); try { - api.onIconUpdate(arg_timestamp!, arg_url!, arg_bytes!); + api.onIconUpdate(arg_sequence!, arg_url!, arg_bytes!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -6156,14 +6580,14 @@ abstract class GeckoStateEvents { assert(message != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onTabAdded was null.'); final List args = (message as List?)!; - final int? arg_timestamp = (args[0] as int?); - assert(arg_timestamp != null, + final int? arg_sequence = (args[0] as int?); + assert(arg_sequence != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onTabAdded was null, expected non-null int.'); final String? arg_tabId = (args[1] as String?); assert(arg_tabId != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onTabAdded was null, expected non-null String.'); try { - api.onTabAdded(arg_timestamp!, arg_tabId!); + api.onTabAdded(arg_sequence!, arg_tabId!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -6184,14 +6608,14 @@ abstract class GeckoStateEvents { assert(message != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onTabListChange was null.'); final List args = (message as List?)!; - final int? arg_timestamp = (args[0] as int?); - assert(arg_timestamp != null, + final int? arg_sequence = (args[0] as int?); + assert(arg_sequence != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onTabListChange was null, expected non-null int.'); final List? arg_tabIds = (args[1] as List?)?.cast(); assert(arg_tabIds != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onTabListChange was null, expected non-null List.'); try { - api.onTabListChange(arg_timestamp!, arg_tabIds!); + api.onTabListChange(arg_sequence!, arg_tabIds!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -6212,12 +6636,12 @@ abstract class GeckoStateEvents { assert(message != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onSelectedTabChange was null.'); final List args = (message as List?)!; - final int? arg_timestamp = (args[0] as int?); - assert(arg_timestamp != null, + final int? arg_sequence = (args[0] as int?); + assert(arg_sequence != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onSelectedTabChange was null, expected non-null int.'); final String? arg_id = (args[1] as String?); try { - api.onSelectedTabChange(arg_timestamp!, arg_id); + api.onSelectedTabChange(arg_sequence!, arg_id); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -6238,14 +6662,14 @@ abstract class GeckoStateEvents { assert(message != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onTabContentStateChange was null.'); final List args = (message as List?)!; - final int? arg_timestamp = (args[0] as int?); - assert(arg_timestamp != null, + final int? arg_sequence = (args[0] as int?); + assert(arg_sequence != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onTabContentStateChange was null, expected non-null int.'); final TabContentState? arg_state = (args[1] as TabContentState?); assert(arg_state != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onTabContentStateChange was null, expected non-null TabContentState.'); try { - api.onTabContentStateChange(arg_timestamp!, arg_state!); + api.onTabContentStateChange(arg_sequence!, arg_state!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -6266,8 +6690,8 @@ abstract class GeckoStateEvents { assert(message != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onHistoryStateChange was null.'); final List args = (message as List?)!; - final int? arg_timestamp = (args[0] as int?); - assert(arg_timestamp != null, + final int? arg_sequence = (args[0] as int?); + assert(arg_sequence != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onHistoryStateChange was null, expected non-null int.'); final String? arg_id = (args[1] as String?); assert(arg_id != null, @@ -6276,7 +6700,7 @@ abstract class GeckoStateEvents { assert(arg_state != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onHistoryStateChange was null, expected non-null HistoryState.'); try { - api.onHistoryStateChange(arg_timestamp!, arg_id!, arg_state!); + api.onHistoryStateChange(arg_sequence!, arg_id!, arg_state!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -6297,8 +6721,8 @@ abstract class GeckoStateEvents { assert(message != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onReaderableStateChange was null.'); final List args = (message as List?)!; - final int? arg_timestamp = (args[0] as int?); - assert(arg_timestamp != null, + final int? arg_sequence = (args[0] as int?); + assert(arg_sequence != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onReaderableStateChange was null, expected non-null int.'); final String? arg_id = (args[1] as String?); assert(arg_id != null, @@ -6307,7 +6731,7 @@ abstract class GeckoStateEvents { assert(arg_state != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onReaderableStateChange was null, expected non-null ReaderableState.'); try { - api.onReaderableStateChange(arg_timestamp!, arg_id!, arg_state!); + api.onReaderableStateChange(arg_sequence!, arg_id!, arg_state!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -6328,8 +6752,8 @@ abstract class GeckoStateEvents { assert(message != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onSecurityInfoStateChange was null.'); final List args = (message as List?)!; - final int? arg_timestamp = (args[0] as int?); - assert(arg_timestamp != null, + final int? arg_sequence = (args[0] as int?); + assert(arg_sequence != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onSecurityInfoStateChange was null, expected non-null int.'); final String? arg_id = (args[1] as String?); assert(arg_id != null, @@ -6338,7 +6762,7 @@ abstract class GeckoStateEvents { assert(arg_state != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onSecurityInfoStateChange was null, expected non-null SecurityInfoState.'); try { - api.onSecurityInfoStateChange(arg_timestamp!, arg_id!, arg_state!); + api.onSecurityInfoStateChange(arg_sequence!, arg_id!, arg_state!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -6359,15 +6783,15 @@ abstract class GeckoStateEvents { assert(message != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onIconChange was null.'); final List args = (message as List?)!; - final int? arg_timestamp = (args[0] as int?); - assert(arg_timestamp != null, + final int? arg_sequence = (args[0] as int?); + assert(arg_sequence != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onIconChange was null, expected non-null int.'); final String? arg_id = (args[1] as String?); assert(arg_id != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onIconChange was null, expected non-null String.'); final Uint8List? arg_bytes = (args[2] as Uint8List?); try { - api.onIconChange(arg_timestamp!, arg_id!, arg_bytes); + api.onIconChange(arg_sequence!, arg_id!, arg_bytes); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -6388,15 +6812,15 @@ abstract class GeckoStateEvents { assert(message != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onThumbnailChange was null.'); final List args = (message as List?)!; - final int? arg_timestamp = (args[0] as int?); - assert(arg_timestamp != null, + final int? arg_sequence = (args[0] as int?); + assert(arg_sequence != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onThumbnailChange was null, expected non-null int.'); final String? arg_id = (args[1] as String?); assert(arg_id != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onThumbnailChange was null, expected non-null String.'); final Uint8List? arg_bytes = (args[2] as Uint8List?); try { - api.onThumbnailChange(arg_timestamp!, arg_id!, arg_bytes); + api.onThumbnailChange(arg_sequence!, arg_id!, arg_bytes); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -6417,8 +6841,8 @@ abstract class GeckoStateEvents { assert(message != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onFindResults was null.'); final List args = (message as List?)!; - final int? arg_timestamp = (args[0] as int?); - assert(arg_timestamp != null, + final int? arg_sequence = (args[0] as int?); + assert(arg_sequence != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onFindResults was null, expected non-null int.'); final String? arg_id = (args[1] as String?); assert(arg_id != null, @@ -6427,7 +6851,7 @@ abstract class GeckoStateEvents { assert(arg_results != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onFindResults was null, expected non-null List.'); try { - api.onFindResults(arg_timestamp!, arg_id!, arg_results!); + api.onFindResults(arg_sequence!, arg_id!, arg_results!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -6448,8 +6872,8 @@ abstract class GeckoStateEvents { assert(message != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onLongPress was null.'); final List args = (message as List?)!; - final int? arg_timestamp = (args[0] as int?); - assert(arg_timestamp != null, + final int? arg_sequence = (args[0] as int?); + assert(arg_sequence != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onLongPress was null, expected non-null int.'); final String? arg_id = (args[1] as String?); assert(arg_id != null, @@ -6458,7 +6882,7 @@ abstract class GeckoStateEvents { assert(arg_hitResult != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onLongPress was null, expected non-null HitResult.'); try { - api.onLongPress(arg_timestamp!, arg_id!, arg_hitResult!); + api.onLongPress(arg_sequence!, arg_id!, arg_hitResult!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -6479,14 +6903,14 @@ abstract class GeckoStateEvents { assert(message != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onPreferenceChange was null.'); final List args = (message as List?)!; - final int? arg_timestamp = (args[0] as int?); - assert(arg_timestamp != null, + final int? arg_sequence = (args[0] as int?); + assert(arg_sequence != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onPreferenceChange was null, expected non-null int.'); final GeckoPref? arg_value = (args[1] as GeckoPref?); assert(arg_value != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onPreferenceChange was null, expected non-null GeckoPref.'); try { - api.onPreferenceChange(arg_timestamp!, arg_value!); + api.onPreferenceChange(arg_sequence!, arg_value!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -6507,14 +6931,14 @@ abstract class GeckoStateEvents { assert(message != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onContainerSiteAssignment was null.'); final List args = (message as List?)!; - final int? arg_timestamp = (args[0] as int?); - assert(arg_timestamp != null, + final int? arg_sequence = (args[0] as int?); + assert(arg_sequence != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onContainerSiteAssignment was null, expected non-null int.'); final ContainerSiteAssignment? arg_details = (args[1] as ContainerSiteAssignment?); assert(arg_details != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onContainerSiteAssignment was null, expected non-null ContainerSiteAssignment.'); try { - api.onContainerSiteAssignment(arg_timestamp!, arg_details!); + api.onContainerSiteAssignment(arg_sequence!, arg_details!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -6535,14 +6959,43 @@ abstract class GeckoStateEvents { assert(message != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onMlProgress was null.'); final List args = (message as List?)!; - final int? arg_timestamp = (args[0] as int?); - assert(arg_timestamp != null, + final int? arg_sequence = (args[0] as int?); + assert(arg_sequence != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onMlProgress was null, expected non-null int.'); final MlProgressData? arg_progress = (args[1] as MlProgressData?); assert(arg_progress != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onMlProgress was null, expected non-null MlProgressData.'); try { - api.onMlProgress(arg_timestamp!, arg_progress!); + api.onMlProgress(arg_sequence!, arg_progress!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onManifestUpdate$messageChannelSuffix', pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + pigeonVar_channel.setMessageHandler(null); + } else { + pigeonVar_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onManifestUpdate was null.'); + final List args = (message as List?)!; + final int? arg_sequence = (args[0] as int?); + assert(arg_sequence != null, + 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onManifestUpdate was null, expected non-null int.'); + final String? arg_tabId = (args[1] as String?); + assert(arg_tabId != null, + 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onManifestUpdate was null, expected non-null String.'); + final PwaManifest? arg_manifest = (args[2] as PwaManifest?); + try { + api.onManifestUpdate(arg_sequence!, arg_tabId!, arg_manifest); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -6654,7 +7107,7 @@ class ReaderViewEvents { abstract class ReaderViewController { static const MessageCodec pigeonChannelCodec = _PigeonCodec(); - void appearanceButtonVisibility(int timestamp, bool visible); + void appearanceButtonVisibility(int sequence, bool visible); static void setUp(ReaderViewController? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) { messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; @@ -6669,14 +7122,14 @@ abstract class ReaderViewController { assert(message != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.ReaderViewController.appearanceButtonVisibility was null.'); final List args = (message as List?)!; - final int? arg_timestamp = (args[0] as int?); - assert(arg_timestamp != null, + final int? arg_sequence = (args[0] as int?); + assert(arg_sequence != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.ReaderViewController.appearanceButtonVisibility was null, expected non-null int.'); final bool? arg_visible = (args[1] as bool?); assert(arg_visible != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.ReaderViewController.appearanceButtonVisibility was null, expected non-null bool.'); try { - api.appearanceButtonVisibility(arg_timestamp!, arg_visible!); + api.appearanceButtonVisibility(arg_sequence!, arg_visible!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -6846,11 +7299,11 @@ class GeckoAddonsApi { abstract class GeckoAddonEvents { static const MessageCodec pigeonChannelCodec = _PigeonCodec(); - void onUpsertWebExtensionAction(int timestamp, String extensionId, WebExtensionActionType actionType, WebExtensionData extensionData); + void onUpsertWebExtensionAction(int sequence, String extensionId, WebExtensionActionType actionType, WebExtensionData extensionData); - void onRemoveWebExtensionAction(int timestamp, String extensionId, WebExtensionActionType actionType); + void onRemoveWebExtensionAction(int sequence, String extensionId, WebExtensionActionType actionType); - void onUpdateWebExtensionIcon(int timestamp, String extensionId, WebExtensionActionType actionType, Uint8List icon); + void onUpdateWebExtensionIcon(int sequence, String extensionId, WebExtensionActionType actionType, Uint8List icon); static void setUp(GeckoAddonEvents? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) { messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; @@ -6865,8 +7318,8 @@ abstract class GeckoAddonEvents { assert(message != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoAddonEvents.onUpsertWebExtensionAction was null.'); final List args = (message as List?)!; - final int? arg_timestamp = (args[0] as int?); - assert(arg_timestamp != null, + final int? arg_sequence = (args[0] as int?); + assert(arg_sequence != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoAddonEvents.onUpsertWebExtensionAction was null, expected non-null int.'); final String? arg_extensionId = (args[1] as String?); assert(arg_extensionId != null, @@ -6878,7 +7331,7 @@ abstract class GeckoAddonEvents { assert(arg_extensionData != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoAddonEvents.onUpsertWebExtensionAction was null, expected non-null WebExtensionData.'); try { - api.onUpsertWebExtensionAction(arg_timestamp!, arg_extensionId!, arg_actionType!, arg_extensionData!); + api.onUpsertWebExtensionAction(arg_sequence!, arg_extensionId!, arg_actionType!, arg_extensionData!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -6899,8 +7352,8 @@ abstract class GeckoAddonEvents { assert(message != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoAddonEvents.onRemoveWebExtensionAction was null.'); final List args = (message as List?)!; - final int? arg_timestamp = (args[0] as int?); - assert(arg_timestamp != null, + final int? arg_sequence = (args[0] as int?); + assert(arg_sequence != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoAddonEvents.onRemoveWebExtensionAction was null, expected non-null int.'); final String? arg_extensionId = (args[1] as String?); assert(arg_extensionId != null, @@ -6909,7 +7362,7 @@ abstract class GeckoAddonEvents { assert(arg_actionType != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoAddonEvents.onRemoveWebExtensionAction was null, expected non-null WebExtensionActionType.'); try { - api.onRemoveWebExtensionAction(arg_timestamp!, arg_extensionId!, arg_actionType!); + api.onRemoveWebExtensionAction(arg_sequence!, arg_extensionId!, arg_actionType!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -6930,8 +7383,8 @@ abstract class GeckoAddonEvents { assert(message != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoAddonEvents.onUpdateWebExtensionIcon was null.'); final List args = (message as List?)!; - final int? arg_timestamp = (args[0] as int?); - assert(arg_timestamp != null, + final int? arg_sequence = (args[0] as int?); + assert(arg_sequence != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoAddonEvents.onUpdateWebExtensionIcon was null, expected non-null int.'); final String? arg_extensionId = (args[1] as String?); assert(arg_extensionId != null, @@ -6943,7 +7396,7 @@ abstract class GeckoAddonEvents { assert(arg_icon != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoAddonEvents.onUpdateWebExtensionIcon was null, expected non-null Uint8List.'); try { - api.onUpdateWebExtensionIcon(arg_timestamp!, arg_extensionId!, arg_actionType!, arg_icon!); + api.onUpdateWebExtensionIcon(arg_sequence!, arg_extensionId!, arg_actionType!, arg_icon!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -7017,7 +7470,7 @@ class GeckoSuggestionApi { abstract class GeckoSuggestionEvents { static const MessageCodec pigeonChannelCodec = _PigeonCodec(); - void onSuggestionResult(int timestamp, GeckoSuggestionType suggestionType, List suggestions); + void onSuggestionResult(int sequence, GeckoSuggestionType suggestionType, List suggestions); static void setUp(GeckoSuggestionEvents? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) { messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; @@ -7032,8 +7485,8 @@ abstract class GeckoSuggestionEvents { assert(message != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoSuggestionEvents.onSuggestionResult was null.'); final List args = (message as List?)!; - final int? arg_timestamp = (args[0] as int?); - assert(arg_timestamp != null, + final int? arg_sequence = (args[0] as int?); + assert(arg_sequence != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoSuggestionEvents.onSuggestionResult was null, expected non-null int.'); final GeckoSuggestionType? arg_suggestionType = (args[1] as GeckoSuggestionType?); assert(arg_suggestionType != null, @@ -7042,7 +7495,7 @@ abstract class GeckoSuggestionEvents { assert(arg_suggestions != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoSuggestionEvents.onSuggestionResult was null, expected non-null List.'); try { - api.onSuggestionResult(arg_timestamp!, arg_suggestionType!, arg_suggestions!); + api.onSuggestionResult(arg_sequence!, arg_suggestionType!, arg_suggestions!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -7058,7 +7511,7 @@ abstract class GeckoSuggestionEvents { abstract class GeckoTabContentEvents { static const MessageCodec pigeonChannelCodec = _PigeonCodec(); - void onContentUpdate(int timestamp, TabContent content); + void onContentUpdate(int sequence, TabContent content); static void setUp(GeckoTabContentEvents? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) { messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; @@ -7073,14 +7526,14 @@ abstract class GeckoTabContentEvents { assert(message != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoTabContentEvents.onContentUpdate was null.'); final List args = (message as List?)!; - final int? arg_timestamp = (args[0] as int?); - assert(arg_timestamp != null, + final int? arg_sequence = (args[0] as int?); + assert(arg_sequence != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoTabContentEvents.onContentUpdate was null, expected non-null int.'); final TabContent? arg_content = (args[1] as TabContent?); assert(arg_content != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoTabContentEvents.onContentUpdate was null, expected non-null TabContent.'); try { - api.onContentUpdate(arg_timestamp!, arg_content!); + api.onContentUpdate(arg_sequence!, arg_content!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -7501,7 +7954,7 @@ class GeckoDownloadsApi { abstract class BrowserExtensionEvents { static const MessageCodec pigeonChannelCodec = _PigeonCodec(); - void onFeedRequested(int timestamp, String url); + void onFeedRequested(int sequence, String url); static void setUp(BrowserExtensionEvents? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) { messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; @@ -7516,14 +7969,14 @@ abstract class BrowserExtensionEvents { assert(message != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.BrowserExtensionEvents.onFeedRequested was null.'); final List args = (message as List?)!; - final int? arg_timestamp = (args[0] as int?); - assert(arg_timestamp != null, + final int? arg_sequence = (args[0] as int?); + assert(arg_sequence != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.BrowserExtensionEvents.onFeedRequested was null, expected non-null int.'); final String? arg_url = (args[1] as String?); assert(arg_url != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.BrowserExtensionEvents.onFeedRequested was null, expected non-null String.'); try { - api.onFeedRequested(arg_timestamp!, arg_url!); + api.onFeedRequested(arg_sequence!, arg_url!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -7674,11 +8127,11 @@ abstract class GeckoViewportEvents { /// This is detected natively using WindowInsets API and provides /// accurate keyboard height information. /// - /// [timestamp] Event timestamp for ordering. + /// [sequence] Event sequence number for ordering. /// [heightPx] Keyboard height in pixels (0 when hidden). /// [isVisible] Whether the keyboard is currently visible. /// [isAnimating] Whether the keyboard is currently animating. - void onKeyboardVisibilityChanged(int timestamp, int heightPx, bool isVisible, bool isAnimating); + void onKeyboardVisibilityChanged(int sequence, int heightPx, bool isVisible, bool isAnimating); static void setUp(GeckoViewportEvents? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) { messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; @@ -7693,8 +8146,8 @@ abstract class GeckoViewportEvents { assert(message != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoViewportEvents.onKeyboardVisibilityChanged was null.'); final List args = (message as List?)!; - final int? arg_timestamp = (args[0] as int?); - assert(arg_timestamp != null, + final int? arg_sequence = (args[0] as int?); + assert(arg_sequence != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoViewportEvents.onKeyboardVisibilityChanged was null, expected non-null int.'); final int? arg_heightPx = (args[1] as int?); assert(arg_heightPx != null, @@ -7706,7 +8159,7 @@ abstract class GeckoViewportEvents { assert(arg_isAnimating != null, 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoViewportEvents.onKeyboardVisibilityChanged was null, expected non-null bool.'); try { - api.onKeyboardVisibilityChanged(arg_timestamp!, arg_heightPx!, arg_isVisible!, arg_isAnimating!); + api.onKeyboardVisibilityChanged(arg_sequence!, arg_heightPx!, arg_isVisible!, arg_isAnimating!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -8409,3 +8862,86 @@ class GeckoAppLinksApi { } } } + +/// API for PWA (Progressive Web App) installation and management. +/// +/// Wraps Mozilla Android Components' WebAppUseCases and ManifestStorage +/// to provide PWA install and query functionality to Flutter. +class GeckoPwaApi { + /// Constructor for [GeckoPwaApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + GeckoPwaApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + final BinaryMessenger? pigeonVar_binaryMessenger; + + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); + + final String pigeonVar_messageChannelSuffix; + + /// Installs the current page as a PWA (adds to home screen). + /// + /// Creates an Android shortcut with profile and container metadata embedded + /// in the intent extras. This ensures the PWA opens with the same profile + /// and container context that was active during installation. + /// + /// The [tabId] identifies which tab to install from. If null, uses the selected tab. + /// The [profileUuid] is the UUID of the current user profile. + /// The [contextId] is the container's contextual identity (optional, null for default container). + /// Returns true if installation was successful. + Future installWebApp(String? tabId, String profileUuid, String? contextId) async { + final pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoPwaApi.installWebApp$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([tabId, profileUuid, contextId]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as bool?)!; + } + } + + /// Returns a list of all installed PWA manifests. + Future> getInstalledWebApps() async { + final pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoPwaApi.getInstalledWebApps$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as List?)!.cast(); + } + } +} diff --git a/packages/flutter_mozilla_components/pigeons/gecko.dart b/packages/flutter_mozilla_components/pigeons/gecko.dart index 335d79a2..fd35f463 100644 --- a/packages/flutter_mozilla_components/pigeons/gecko.dart +++ b/packages/flutter_mozilla_components/pigeons/gecko.dart @@ -1392,36 +1392,35 @@ class ContainerSiteAssignment { @FlutterApi() abstract class GeckoStateEvents { - void onViewReadyStateChange(int timestamp, bool state); - void onEngineReadyStateChange(int timestamp, bool state); - void onIconUpdate(int timestamp, String url, Uint8List bytes); + void onViewReadyStateChange(int sequence, bool state); + void onEngineReadyStateChange(int sequence, bool state); + void onIconUpdate(int sequence, String url, Uint8List bytes); - void onTabAdded(int timestamp, String tabId); + void onTabAdded(int sequence, String tabId); - void onTabListChange(int timestamp, List tabIds); - void onSelectedTabChange(int timestamp, String? id); + void onTabListChange(int sequence, List tabIds); + void onSelectedTabChange(int sequence, String? id); - void onTabContentStateChange(int timestamp, TabContentState state); - void onHistoryStateChange(int timestamp, String id, HistoryState state); - void onReaderableStateChange(int timestamp, String id, ReaderableState state); + void onTabContentStateChange(int sequence, TabContentState state); + void onHistoryStateChange(int sequence, String id, HistoryState state); + void onReaderableStateChange(int sequence, String id, ReaderableState state); void onSecurityInfoStateChange( - int timestamp, + int sequence, String id, SecurityInfoState state, ); - void onIconChange(int timestamp, String id, Uint8List? bytes); - void onThumbnailChange(int timestamp, String id, Uint8List? bytes); + void onIconChange(int sequence, String id, Uint8List? bytes); + void onThumbnailChange(int sequence, String id, Uint8List? bytes); - void onFindResults(int timestamp, String id, List results); - void onLongPress(int timestamp, String id, HitResult hitResult); + void onFindResults(int sequence, String id, List results); + void onLongPress(int sequence, String id, HitResult hitResult); - // void onScrollChange(int timestamp, String tabId, int scrollY); - void onPreferenceChange(int timestamp, GeckoPref value); + // void onScrollChange(int sequence, String tabId, int scrollY); + void onPreferenceChange(int sequence, GeckoPref value); - void onContainerSiteAssignment( - int timestamp, - ContainerSiteAssignment details, - ); + void onContainerSiteAssignment(int sequence, ContainerSiteAssignment details); + + void onMlProgress(int sequence, MlProgressData progress); void onMlProgress(int timestamp, MlProgressData progress); } @@ -1439,7 +1438,7 @@ abstract class ReaderViewEvents { @FlutterApi() abstract class ReaderViewController { - void appearanceButtonVisibility(int timestamp, bool visible); + void appearanceButtonVisibility(int sequence, bool visible); } @HostApi() @@ -1465,20 +1464,20 @@ abstract class GeckoAddonsApi { @FlutterApi() abstract class GeckoAddonEvents { void onUpsertWebExtensionAction( - int timestamp, + int sequence, String extensionId, WebExtensionActionType actionType, WebExtensionData extensionData, ); void onRemoveWebExtensionAction( - int timestamp, + int sequence, String extensionId, WebExtensionActionType actionType, ); void onUpdateWebExtensionIcon( - int timestamp, + int sequence, String extensionId, WebExtensionActionType actionType, Uint8List icon, @@ -1495,7 +1494,7 @@ abstract class GeckoSuggestionApi { @FlutterApi() abstract class GeckoSuggestionEvents { void onSuggestionResult( - int timestamp, + int sequence, GeckoSuggestionType suggestionType, List suggestions, ); @@ -1503,7 +1502,7 @@ abstract class GeckoSuggestionEvents { @FlutterApi() abstract class GeckoTabContentEvents { - void onContentUpdate(int timestamp, TabContent content); + void onContentUpdate(int sequence, TabContent content); } @HostApi() @@ -1579,7 +1578,7 @@ abstract class GeckoDownloadsApi { @FlutterApi() abstract class BrowserExtensionEvents { - void onFeedRequested(int timestamp, String url); + void onFeedRequested(int sequence, String url); } class GeckoHeader { @@ -1737,12 +1736,12 @@ abstract class GeckoViewportEvents { /// This is detected natively using WindowInsets API and provides /// accurate keyboard height information. /// - /// [timestamp] Event timestamp for ordering. + /// [sequence] Event sequence number for ordering. /// [heightPx] Keyboard height in pixels (0 when hidden). /// [isVisible] Whether the keyboard is currently visible. /// [isAnimating] Whether the keyboard is currently animating. void onKeyboardVisibilityChanged( - int timestamp, + int sequence, int heightPx, bool isVisible, bool isAnimating, diff --git a/packages/simple_intent_receiver/android/src/main/kotlin/eu/weblibre/simple_intent_receiver/IntentReceiver.kt b/packages/simple_intent_receiver/android/src/main/kotlin/eu/weblibre/simple_intent_receiver/IntentReceiver.kt index 9bcdefaa..781e7863 100644 --- a/packages/simple_intent_receiver/android/src/main/kotlin/eu/weblibre/simple_intent_receiver/IntentReceiver.kt +++ b/packages/simple_intent_receiver/android/src/main/kotlin/eu/weblibre/simple_intent_receiver/IntentReceiver.kt @@ -22,11 +22,13 @@ package eu.weblibre.simple_intent_receiver import io.flutter.plugin.common.BinaryMessenger import eu.weblibre.simple_intent_receiver.pigeons.IntentEvents import eu.weblibre.simple_intent_receiver.pigeons.Intent as PigeonIntent +import eu.weblibre.flutter_mozilla_components.ext.EventSequence class IntentReceiver(messenger: BinaryMessenger) { private val intentEvents: IntentEvents = IntentEvents(messenger) - fun sendIntent(timestamp: Long, intent: PigeonIntent) { - intentEvents.onIntentReceived(timestamp, intent) { } + fun sendIntent(intent: PigeonIntent) { + intentEvents.onIntentReceived(EventSequence.next(), intent) { } } } +} diff --git a/packages/simple_intent_receiver/android/src/main/kotlin/eu/weblibre/simple_intent_receiver/SimpleIntentReceiverPlugin.kt b/packages/simple_intent_receiver/android/src/main/kotlin/eu/weblibre/simple_intent_receiver/SimpleIntentReceiverPlugin.kt index c012b4ef..c9dc16fe 100644 --- a/packages/simple_intent_receiver/android/src/main/kotlin/eu/weblibre/simple_intent_receiver/SimpleIntentReceiverPlugin.kt +++ b/packages/simple_intent_receiver/android/src/main/kotlin/eu/weblibre/simple_intent_receiver/SimpleIntentReceiverPlugin.kt @@ -114,7 +114,7 @@ class SimpleIntentReceiverPlugin: FlutterPlugin, ActivityAware, PluginRegistry.N } val pigeonIntent = convertToPigeonIntent(intent) - intentReceiver?.sendIntent(System.currentTimeMillis(), pigeonIntent) + intentReceiver?.sendIntent(pigeonIntent) return true } diff --git a/packages/simple_intent_receiver/android/src/main/kotlin/eu/weblibre/simple_intent_receiver/pigeons/Intent.g.kt b/packages/simple_intent_receiver/android/src/main/kotlin/eu/weblibre/simple_intent_receiver/pigeons/Intent.g.kt index 459d2f29..a309f908 100644 --- a/packages/simple_intent_receiver/android/src/main/kotlin/eu/weblibre/simple_intent_receiver/pigeons/Intent.g.kt +++ b/packages/simple_intent_receiver/android/src/main/kotlin/eu/weblibre/simple_intent_receiver/pigeons/Intent.g.kt @@ -133,12 +133,12 @@ class IntentEvents(private val binaryMessenger: BinaryMessenger, private val mes IntentPigeonCodec() } } - fun onIntentReceived(timestampArg: Long, intentArg: Intent, callback: (Result) -> Unit) + fun onIntentReceived(sequenceArg: Long, intentArg: Intent, callback: (Result) -> Unit) { val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" val channelName = "dev.flutter.pigeon.simple_intent_receiver.IntentEvents.onIntentReceived$separatedMessageChannelSuffix" val channel = BasicMessageChannel(binaryMessenger, channelName, codec) - channel.send(listOf(timestampArg, intentArg)) { + channel.send(listOf(sequenceArg, intentArg)) { if (it is List<*>) { if (it.size > 1) { callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) diff --git a/packages/simple_intent_receiver/lib/src/intent_receiver.dart b/packages/simple_intent_receiver/lib/src/intent_receiver.dart index e8280d8e..6a90845d 100644 --- a/packages/simple_intent_receiver/lib/src/intent_receiver.dart +++ b/packages/simple_intent_receiver/lib/src/intent_receiver.dart @@ -29,8 +29,8 @@ class IntentReceiver extends IntentEvents { Stream get events => _controller.stream; @override - void onIntentReceived(int timestamp, Intent intent) { - if (_lastAdded == null || timestamp > _lastAdded!) { + void onIntentReceived(int sequence, Intent intent) { + if (_lastAdded == null || sequence > _lastAdded!) { _controller.add(intent); } } diff --git a/packages/simple_intent_receiver/lib/src/pigeons/intent.g.dart b/packages/simple_intent_receiver/lib/src/pigeons/intent.g.dart index 0bfc83f0..e0a42997 100644 --- a/packages/simple_intent_receiver/lib/src/pigeons/intent.g.dart +++ b/packages/simple_intent_receiver/lib/src/pigeons/intent.g.dart @@ -128,7 +128,7 @@ class _PigeonCodec extends StandardMessageCodec { abstract class IntentEvents { static const MessageCodec pigeonChannelCodec = _PigeonCodec(); - void onIntentReceived(int timestamp, Intent intent); + void onIntentReceived(int sequence, Intent intent); static void setUp(IntentEvents? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) { messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; @@ -143,14 +143,14 @@ abstract class IntentEvents { assert(message != null, 'Argument for dev.flutter.pigeon.simple_intent_receiver.IntentEvents.onIntentReceived was null.'); final List args = (message as List?)!; - final int? arg_timestamp = (args[0] as int?); - assert(arg_timestamp != null, + final int? arg_sequence = (args[0] as int?); + assert(arg_sequence != null, 'Argument for dev.flutter.pigeon.simple_intent_receiver.IntentEvents.onIntentReceived was null, expected non-null int.'); final Intent? arg_intent = (args[1] as Intent?); assert(arg_intent != null, 'Argument for dev.flutter.pigeon.simple_intent_receiver.IntentEvents.onIntentReceived was null, expected non-null Intent.'); try { - api.onIntentReceived(arg_timestamp!, arg_intent!); + api.onIntentReceived(arg_sequence!, arg_intent!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); diff --git a/packages/simple_intent_receiver/pigeons/intent.dart b/packages/simple_intent_receiver/pigeons/intent.dart index 39fa82f6..9299c715 100644 --- a/packages/simple_intent_receiver/pigeons/intent.dart +++ b/packages/simple_intent_receiver/pigeons/intent.dart @@ -51,5 +51,5 @@ class Intent { ) @FlutterApi() abstract class IntentEvents { - void onIntentReceived(int timestamp, Intent intent); + void onIntentReceived(int sequence, Intent intent); }