implemented selection api

This commit is contained in:
Fabian Freund
2024-10-25 06:46:39 +02:00
parent 8d351e55c0
commit 6a0f0eb08c
15 changed files with 565 additions and 427 deletions
@@ -2,11 +2,77 @@ import 'dart:async';
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart'; import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
import 'package:lensai/core/logger.dart'; import 'package:lensai/core/logger.dart';
import 'package:lensai/features/bangs/domain/providers.dart';
import 'package:lensai/features/geckoview/domain/providers/tab_session.dart'; import 'package:lensai/features/geckoview/domain/providers/tab_session.dart';
import 'package:lensai/features/geckoview/domain/repositories/tab.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:share_plus/share_plus.dart';
import 'package:url_launcher/url_launcher.dart';
part 'providers.g.dart'; part 'providers.g.dart';
@Riverpod(keepAlive: true)
GeckoSelectionActionService selectionActionService(
SelectionActionServiceRef ref,
) {
final service = GeckoSelectionActionService.setUp();
unawaited(
service.setActions([
SearchAction((text) async {
final defaultSearchBang =
await ref.read(kagiSearchBangDataProvider.future);
if (defaultSearchBang != null) {
await ref
.read(tabRepositoryProvider.notifier)
.addTab(url: defaultSearchBang.getUrl(text));
} else {
logger.e('No default search bang found');
}
}),
PrivateSearchAction((text) async {
final defaultSearchBang =
await ref.read(kagiSearchBangDataProvider.future);
if (defaultSearchBang != null) {
await ref.read(tabRepositoryProvider.notifier).addTab(
url: defaultSearchBang.getUrl(text),
private: true,
);
} else {
logger.e('No default search bang found');
}
}),
ShareAction((text) async {
await Share.share(text);
}),
CallAction((text) async {
final uri = Uri.tryParse('tel:${text.replaceAll(' ', '')}');
if (uri != null) {
final canLaunch = await canLaunchUrl(uri);
if (canLaunch) {
await launchUrl(uri);
}
}
}),
EmailAction((text) async {
final uri = Uri.tryParse('mailto:$text');
if (uri != null) {
final canLaunch = await canLaunchUrl(uri);
if (canLaunch) {
await launchUrl(uri);
}
}
}),
]),
);
return service;
}
@Riverpod(keepAlive: true) @Riverpod(keepAlive: true)
GeckoEventService eventService(EventServiceRef ref) { GeckoEventService eventService(EventServiceRef ref) {
final service = GeckoEventService.setUp(); final service = GeckoEventService.setUp();
@@ -54,6 +120,6 @@ class EngineReadyState extends _$EngineReadyState {
} }
@Riverpod() @Riverpod()
TabSession selectedTabSessionNotifier(SelectedTabSessionNotifierRef ref) { Raw<TabSession> selectedTabSessionNotifier(SelectedTabSessionNotifierRef ref) {
return ref.watch(tabSessionProvider(null).notifier); return ref.watch(tabSessionProvider(null).notifier);
} }
@@ -6,6 +6,23 @@ part of 'providers.dart';
// RiverpodGenerator // RiverpodGenerator
// ************************************************************************** // **************************************************************************
String _$selectionActionServiceHash() =>
r'c150f6003dcea80b18fc063dd998ab45ce27d1a1';
/// See also [selectionActionService].
@ProviderFor(selectionActionService)
final selectionActionServiceProvider =
Provider<GeckoSelectionActionService>.internal(
selectionActionService,
name: r'selectionActionServiceProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$selectionActionServiceHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef SelectionActionServiceRef = ProviderRef<GeckoSelectionActionService>;
String _$eventServiceHash() => r'5aa357fdf0d217677a9a66ecb50417ac18929cad'; String _$eventServiceHash() => r'5aa357fdf0d217677a9a66ecb50417ac18929cad';
/// See also [eventService]. /// See also [eventService].
@@ -21,12 +38,12 @@ final eventServiceProvider = Provider<GeckoEventService>.internal(
typedef EventServiceRef = ProviderRef<GeckoEventService>; typedef EventServiceRef = ProviderRef<GeckoEventService>;
String _$selectedTabSessionNotifierHash() => String _$selectedTabSessionNotifierHash() =>
r'1b0fe1afa87c0b09fc8407ceb91946fffaef5214'; r'ea9959e871dd0c3a8b80152eafe726c925094106';
/// See also [selectedTabSessionNotifier]. /// See also [selectedTabSessionNotifier].
@ProviderFor(selectedTabSessionNotifier) @ProviderFor(selectedTabSessionNotifier)
final selectedTabSessionNotifierProvider = final selectedTabSessionNotifierProvider =
AutoDisposeProvider<TabSession>.internal( AutoDisposeProvider<Raw<TabSession>>.internal(
selectedTabSessionNotifier, selectedTabSessionNotifier,
name: r'selectedTabSessionNotifierProvider', name: r'selectedTabSessionNotifierProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
@@ -36,7 +53,7 @@ final selectedTabSessionNotifierProvider =
allTransitiveDependencies: null, allTransitiveDependencies: null,
); );
typedef SelectedTabSessionNotifierRef = AutoDisposeProviderRef<TabSession>; typedef SelectedTabSessionNotifierRef = AutoDisposeProviderRef<Raw<TabSession>>;
String _$engineReadyStateHash() => r'c682333e2e07cf0635aa7ae793a2088ca648c950'; String _$engineReadyStateHash() => r'c682333e2e07cf0635aa7ae793a2088ca648c950';
/// See also [EngineReadyState]. /// See also [EngineReadyState].
@@ -607,7 +607,12 @@ class BrowserScreen extends HookConsumerWidget {
SafeArea( SafeArea(
child: Stack( child: Stack(
children: [ children: [
GeckoView( Consumer(
builder: (context, ref, child) {
//Initialize dependencies
ref.watch(selectionActionServiceProvider);
return GeckoView(
preInitializationStep: () async { preInitializationStep: () async {
await ref await ref
.read(eventServiceProvider) .read(eventServiceProvider)
@@ -623,6 +628,8 @@ class BrowserScreen extends HookConsumerWidget {
}, },
); );
}, },
);
},
), ),
Positioned( Positioned(
bottom: 0, bottom: 0,
@@ -8,7 +8,6 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import eu.lensai.flutter_mozilla_components.feature.DefaultSelectionActionDelegate import eu.lensai.flutter_mozilla_components.feature.DefaultSelectionActionDelegate
import eu.lensai.flutter_mozilla_components.integration.ReaderViewIntegration import eu.lensai.flutter_mozilla_components.integration.ReaderViewIntegration
import eu.lensai.flutter_mozilla_components.pigeons.SelectionAction
import mozilla.components.browser.thumbnails.BrowserThumbnails import mozilla.components.browser.thumbnails.BrowserThumbnails
import mozilla.components.concept.engine.EngineView import mozilla.components.concept.engine.EngineView
import mozilla.components.feature.media.fullscreen.MediaSessionFullscreenFeature import mozilla.components.feature.media.fullscreen.MediaSessionFullscreenFeature
@@ -31,9 +30,7 @@ class BrowserFragment(private val context: Context) : BaseBrowserFragment(), Use
override fun createEngine(components: Components): EngineView { override fun createEngine(components: Components): EngineView {
return components.engine.createView(context).apply { return components.engine.createView(context).apply {
// selectionActionDelegate = DefaultSelectionActionDelegate( selectionActionDelegate = components.selectionAction
// components.selectionAction
// )
} }
} }
@@ -15,7 +15,6 @@ import eu.lensai.flutter_mozilla_components.pigeons.HistoryState
import eu.lensai.flutter_mozilla_components.pigeons.ReaderViewController import eu.lensai.flutter_mozilla_components.pigeons.ReaderViewController
import eu.lensai.flutter_mozilla_components.pigeons.ReaderableState import eu.lensai.flutter_mozilla_components.pigeons.ReaderableState
import eu.lensai.flutter_mozilla_components.pigeons.SecurityInfoState import eu.lensai.flutter_mozilla_components.pigeons.SecurityInfoState
import eu.lensai.flutter_mozilla_components.pigeons.SelectionAction
import eu.lensai.flutter_mozilla_components.pigeons.TabContentState import eu.lensai.flutter_mozilla_components.pigeons.TabContentState
import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.debounce
@@ -35,6 +34,7 @@ import mozilla.components.concept.engine.DefaultSettings
import mozilla.components.concept.engine.Engine import mozilla.components.concept.engine.Engine
import mozilla.components.concept.engine.EngineView import mozilla.components.concept.engine.EngineView
import mozilla.components.concept.engine.mediaquery.PreferredColorScheme import mozilla.components.concept.engine.mediaquery.PreferredColorScheme
import mozilla.components.concept.engine.selection.SelectionActionDelegate
import mozilla.components.concept.fetch.Client import mozilla.components.concept.fetch.Client
import mozilla.components.feature.addons.AddonManager import mozilla.components.feature.addons.AddonManager
import mozilla.components.feature.addons.amo.AMOAddonsProvider import mozilla.components.feature.addons.amo.AMOAddonsProvider
@@ -79,7 +79,7 @@ open class DefaultComponents(
private val applicationContext: Context, private val applicationContext: Context,
val flutterEvents: GeckoStateEvents, val flutterEvents: GeckoStateEvents,
val readerViewController: ReaderViewController, val readerViewController: ReaderViewController,
val selectionAction: SelectionAction, val selectionAction: SelectionActionDelegate,
) { ) {
companion object { companion object {
const val SAMPLE_BROWSER_PREFERENCES = "sample_browser_preferences" const val SAMPLE_BROWSER_PREFERENCES = "sample_browser_preferences"
@@ -9,20 +9,23 @@ import eu.lensai.flutter_mozilla_components.api.GeckoCookieApiImpl
import eu.lensai.flutter_mozilla_components.api.GeckoEngineSettingsApiImpl import eu.lensai.flutter_mozilla_components.api.GeckoEngineSettingsApiImpl
import eu.lensai.flutter_mozilla_components.api.GeckoFindApiImpl import eu.lensai.flutter_mozilla_components.api.GeckoFindApiImpl
import eu.lensai.flutter_mozilla_components.api.GeckoIconsApiImpl import eu.lensai.flutter_mozilla_components.api.GeckoIconsApiImpl
import eu.lensai.flutter_mozilla_components.api.GeckoSelectionActionControllerImpl
import eu.lensai.flutter_mozilla_components.api.GeckoSessionApiImpl import eu.lensai.flutter_mozilla_components.api.GeckoSessionApiImpl
import eu.lensai.flutter_mozilla_components.api.GeckoTabsApiImpl import eu.lensai.flutter_mozilla_components.api.GeckoTabsApiImpl
import eu.lensai.flutter_mozilla_components.feature.CookieManagerFeature import eu.lensai.flutter_mozilla_components.feature.CookieManagerFeature
import eu.lensai.flutter_mozilla_components.feature.DefaultSelectionActionDelegate
import eu.lensai.flutter_mozilla_components.pigeons.GeckoBrowserApi import eu.lensai.flutter_mozilla_components.pigeons.GeckoBrowserApi
import eu.lensai.flutter_mozilla_components.pigeons.GeckoCookieApi import eu.lensai.flutter_mozilla_components.pigeons.GeckoCookieApi
import eu.lensai.flutter_mozilla_components.pigeons.GeckoEngineSettingsApi import eu.lensai.flutter_mozilla_components.pigeons.GeckoEngineSettingsApi
import eu.lensai.flutter_mozilla_components.pigeons.GeckoFindApi import eu.lensai.flutter_mozilla_components.pigeons.GeckoFindApi
import eu.lensai.flutter_mozilla_components.pigeons.GeckoIconsApi import eu.lensai.flutter_mozilla_components.pigeons.GeckoIconsApi
import eu.lensai.flutter_mozilla_components.pigeons.GeckoSelectionActionController
import eu.lensai.flutter_mozilla_components.pigeons.GeckoSelectionActionEvents
import eu.lensai.flutter_mozilla_components.pigeons.GeckoSessionApi import eu.lensai.flutter_mozilla_components.pigeons.GeckoSessionApi
import eu.lensai.flutter_mozilla_components.pigeons.GeckoStateEvents import eu.lensai.flutter_mozilla_components.pigeons.GeckoStateEvents
import eu.lensai.flutter_mozilla_components.pigeons.GeckoTabsApi import eu.lensai.flutter_mozilla_components.pigeons.GeckoTabsApi
import eu.lensai.flutter_mozilla_components.pigeons.ReaderViewController import eu.lensai.flutter_mozilla_components.pigeons.ReaderViewController
import eu.lensai.flutter_mozilla_components.pigeons.ReaderViewEvents import eu.lensai.flutter_mozilla_components.pigeons.ReaderViewEvents
import eu.lensai.flutter_mozilla_components.pigeons.SelectionAction
import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware import io.flutter.embedding.engine.plugins.activity.ActivityAware
@@ -30,6 +33,7 @@ import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
import mozilla.components.browser.engine.gecko.GeckoEngine import mozilla.components.browser.engine.gecko.GeckoEngine
import mozilla.components.browser.engine.gecko.fetch.GeckoViewFetchClient import mozilla.components.browser.engine.gecko.fetch.GeckoViewFetchClient
import mozilla.components.concept.engine.Engine import mozilla.components.concept.engine.Engine
import mozilla.components.concept.engine.selection.SelectionActionDelegate
import mozilla.components.experiment.NimbusExperimentDelegate import mozilla.components.experiment.NimbusExperimentDelegate
import mozilla.components.feature.webcompat.WebCompatFeature import mozilla.components.feature.webcompat.WebCompatFeature
import mozilla.components.lib.crash.handler.CrashHandlerService import mozilla.components.lib.crash.handler.CrashHandlerService
@@ -45,7 +49,7 @@ class Components(
private val applicationContext: Context, private val applicationContext: Context,
flutterEvents: GeckoStateEvents, flutterEvents: GeckoStateEvents,
readerViewController: ReaderViewController, readerViewController: ReaderViewController,
selectionAction: SelectionAction, selectionAction: SelectionActionDelegate,
) : DefaultComponents( ) : DefaultComponents(
applicationContext, applicationContext,
flutterEvents, flutterEvents,
@@ -97,10 +101,12 @@ class FlutterMozillaComponentsPlugin: FlutterPlugin, ActivityAware {
_flutterPluginBinding = flutterPluginBinding _flutterPluginBinding = flutterPluginBinding
_flutterEvents = GeckoStateEvents(_flutterPluginBinding.binaryMessenger) _flutterEvents = GeckoStateEvents(_flutterPluginBinding.binaryMessenger)
val selectionActionEvents = GeckoSelectionActionEvents(_flutterPluginBinding.binaryMessenger)
val selectionActionDelegate = DefaultSelectionActionDelegate(selectionActionEvents)
val readerViewController = val readerViewController =
ReaderViewController(_flutterPluginBinding.binaryMessenger) ReaderViewController(_flutterPluginBinding.binaryMessenger)
val selectionActionDelegate = SelectionAction(_flutterPluginBinding.binaryMessenger)
GlobalComponents.setUp( GlobalComponents.setUp(
flutterPluginBinding.applicationContext, flutterPluginBinding.applicationContext,
@@ -109,10 +115,6 @@ class FlutterMozillaComponentsPlugin: FlutterPlugin, ActivityAware {
selectionActionDelegate selectionActionDelegate
) )
val intent = Intent(flutterPluginBinding.applicationContext, NotificationActivity::class.java)
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
flutterPluginBinding.applicationContext.startActivity(intent)
GeckoBrowserApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoBrowserApiImpl { GeckoBrowserApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoBrowserApiImpl {
showNativeFragment() showNativeFragment()
}) })
@@ -123,11 +125,18 @@ class FlutterMozillaComponentsPlugin: FlutterPlugin, ActivityAware {
GeckoIconsApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoIconsApiImpl()) GeckoIconsApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoIconsApiImpl())
GeckoCookieApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoCookieApiImpl()) GeckoCookieApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoCookieApiImpl())
GeckoFindApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoFindApiImpl()) GeckoFindApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoFindApiImpl())
GeckoSelectionActionController.setUp(_flutterPluginBinding.binaryMessenger, GeckoSelectionActionControllerImpl(
selectionActionDelegate
))
ReaderViewEvents.setUp( ReaderViewEvents.setUp(
_flutterPluginBinding.binaryMessenger, _flutterPluginBinding.binaryMessenger,
GlobalComponents.components!!.readerViewEvents GlobalComponents.components!!.readerViewEvents
) )
val intent = Intent(flutterPluginBinding.applicationContext, NotificationActivity::class.java)
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
flutterPluginBinding.applicationContext.startActivity(intent)
} }
private fun showNativeFragment() { private fun showNativeFragment() {
@@ -3,11 +3,11 @@ package eu.lensai.flutter_mozilla_components
import android.content.Context import android.content.Context
import eu.lensai.flutter_mozilla_components.pigeons.GeckoStateEvents import eu.lensai.flutter_mozilla_components.pigeons.GeckoStateEvents
import eu.lensai.flutter_mozilla_components.pigeons.ReaderViewController import eu.lensai.flutter_mozilla_components.pigeons.ReaderViewController
import eu.lensai.flutter_mozilla_components.pigeons.SelectionAction
import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import mozilla.components.concept.engine.selection.SelectionActionDelegate
import mozilla.components.feature.addons.update.GlobalAddonDependencyProvider import mozilla.components.feature.addons.update.GlobalAddonDependencyProvider
import mozilla.components.support.base.facts.Facts import mozilla.components.support.base.facts.Facts
import mozilla.components.support.base.facts.processor.LogFactProcessor import mozilla.components.support.base.facts.processor.LogFactProcessor
@@ -35,7 +35,7 @@ object GlobalComponents {
applicationContext: Context, applicationContext: Context,
flutterEvents: GeckoStateEvents, flutterEvents: GeckoStateEvents,
readerViewController: ReaderViewController, readerViewController: ReaderViewController,
selectionAction: SelectionAction selectionAction: SelectionActionDelegate
) { ) {
val newComponents = Components( val newComponents = Components(
applicationContext, applicationContext,
@@ -0,0 +1,13 @@
package eu.lensai.flutter_mozilla_components.api
import eu.lensai.flutter_mozilla_components.feature.DefaultSelectionActionDelegate
import eu.lensai.flutter_mozilla_components.pigeons.CustomSelectionAction
import eu.lensai.flutter_mozilla_components.pigeons.GeckoSelectionActionController
class GeckoSelectionActionControllerImpl(
private val selectionActionDelegate: DefaultSelectionActionDelegate
) : GeckoSelectionActionController {
override fun setActions(actions: List<CustomSelectionAction>) {
selectionActionDelegate.actions = actions.associateBy { it.id }
}
}
@@ -1,51 +1,49 @@
package eu.lensai.flutter_mozilla_components.feature package eu.lensai.flutter_mozilla_components.feature
import eu.lensai.flutter_mozilla_components.pigeons.SelectionAction import android.util.Patterns
import kotlinx.coroutines.runBlocking import eu.lensai.flutter_mozilla_components.pigeons.CustomSelectionAction
import kotlinx.coroutines.suspendCancellableCoroutine import eu.lensai.flutter_mozilla_components.pigeons.GeckoSelectionActionEvents
import eu.lensai.flutter_mozilla_components.pigeons.SelectionPattern
import mozilla.components.concept.engine.selection.SelectionActionDelegate import mozilla.components.concept.engine.selection.SelectionActionDelegate
import kotlin.coroutines.resume
class DefaultSelectionActionDelegate( class DefaultSelectionActionDelegate(
private val selectionAction: SelectionAction private val selectionActionEvents: GeckoSelectionActionEvents,
private val actionSorter: ((Array<String>) -> Array<String>)? = null,
) : SelectionActionDelegate { ) : SelectionActionDelegate {
override fun getActionTitle(id: String): CharSequence? = runBlocking { var actions: Map<String, CustomSelectionAction> = emptyMap();
suspendCancellableCoroutine { continuation ->
selectionAction.getActionTitle(id) { result -> override fun getActionTitle(id: String): CharSequence? {
continuation.resume(result.getOrNull()) return actions[id]?.title;
} }
override fun getAllActions(): Array<String> {
return actions.values.map { x -> x.id }.toTypedArray();
}
override fun isActionAvailable(id: String, selectedText: String): Boolean {
val action = actions[id];
if (action != null) {
return when(action.pattern) {
SelectionPattern.PHONE -> Patterns.PHONE.matcher(selectedText.trim()).matches()
SelectionPattern.EMAIL -> Patterns.EMAIL_ADDRESS.matcher(selectedText.trim()).matches()
null -> true
} }
} }
override fun getAllActions(): Array<String> = runBlocking { return false
suspendCancellableCoroutine { continuation ->
selectionAction.getAllActions() { result ->
continuation.resume(result.getOrNull()!!.toTypedArray())
}
}
} }
override fun isActionAvailable(id: String, selectedText: String): Boolean = runBlocking { override fun performAction(id: String, selectedText: String): Boolean {
suspendCancellableCoroutine { continuation -> val action = actions[id];
selectionAction.isActionAvailable(id, selectedText) { result -> if (action != null) {
continuation.resume(result.getOrNull()!!) selectionActionEvents.performSelectionAction(id, selectedText) { _ -> }
} return true
}
} }
override fun performAction(id: String, selectedText: String): Boolean = runBlocking { return false
suspendCancellableCoroutine { continuation ->
selectionAction.performAction(id, selectedText) { result ->
continuation.resume(result.getOrNull()!!)
}
}
} }
override fun sortedActions(actions: Array<String>): Array<String> = runBlocking { override fun sortedActions(actions: Array<String>): Array<String> {
suspendCancellableCoroutine { continuation -> return actionSorter?.invoke(actions) ?: actions
selectionAction.sortedActions(actions.toList()) { result ->
continuation.resume(result.getOrNull()!!.toTypedArray())
}
}
} }
} }
@@ -132,6 +132,17 @@ enum class CookieSameSiteStatus(val raw: Int) {
} }
} }
enum class SelectionPattern(val raw: Int) {
PHONE(0),
EMAIL(1);
companion object {
fun ofRaw(raw: Int): SelectionPattern? {
return values().firstOrNull { it.raw == raw }
}
}
}
/** /**
* Translation options that map to the Gecko Translations Options. * Translation options that map to the Gecko Translations Options.
* *
@@ -889,6 +900,30 @@ data class FindResultState (
) )
} }
} }
/** Generated class from Pigeon that represents data sent in messages. */
data class CustomSelectionAction (
val id: String,
val title: String,
val pattern: SelectionPattern? = null
)
{
companion object {
fun fromList(pigeonVar_list: List<Any?>): CustomSelectionAction {
val id = pigeonVar_list[0] as String
val title = pigeonVar_list[1] as String
val pattern = pigeonVar_list[2] as SelectionPattern?
return CustomSelectionAction(id, title, pattern)
}
}
fun toList(): List<Any?> {
return listOf(
id,
title,
pattern,
)
}
}
private open class GeckoPigeonCodec : StandardMessageCodec() { private open class GeckoPigeonCodec : StandardMessageCodec() {
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
return when (type) { return when (type) {
@@ -918,120 +953,130 @@ private open class GeckoPigeonCodec : StandardMessageCodec() {
} }
} }
134.toByte() -> { 134.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as Long?)?.let {
TranslationOptions.fromList(it) SelectionPattern.ofRaw(it.toInt())
} }
} }
135.toByte() -> { 135.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
ReaderState.fromList(it) TranslationOptions.fromList(it)
} }
} }
136.toByte() -> { 136.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
LastMediaAccessState.fromList(it) ReaderState.fromList(it)
} }
} }
137.toByte() -> { 137.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
HistoryMetadataKey.fromList(it) LastMediaAccessState.fromList(it)
} }
} }
138.toByte() -> { 138.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
PackageCategoryValue.fromList(it) HistoryMetadataKey.fromList(it)
} }
} }
139.toByte() -> { 139.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
ExternalPackage.fromList(it) PackageCategoryValue.fromList(it)
} }
} }
140.toByte() -> { 140.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
LoadUrlFlagsValue.fromList(it) ExternalPackage.fromList(it)
} }
} }
141.toByte() -> { 141.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
SourceValue.fromList(it) LoadUrlFlagsValue.fromList(it)
} }
} }
142.toByte() -> { 142.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
TabState.fromList(it) SourceValue.fromList(it)
} }
} }
143.toByte() -> { 143.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
RecoverableTab.fromList(it) TabState.fromList(it)
} }
} }
144.toByte() -> { 144.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
RecoverableBrowserState.fromList(it) RecoverableTab.fromList(it)
} }
} }
145.toByte() -> { 145.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
IconRequest.fromList(it) RecoverableBrowserState.fromList(it)
} }
} }
146.toByte() -> { 146.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
ResourceSize.fromList(it) IconRequest.fromList(it)
} }
} }
147.toByte() -> { 147.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
Resource.fromList(it) ResourceSize.fromList(it)
} }
} }
148.toByte() -> { 148.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
IconResult.fromList(it) Resource.fromList(it)
} }
} }
149.toByte() -> { 149.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
CookiePartitionKey.fromList(it) IconResult.fromList(it)
} }
} }
150.toByte() -> { 150.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
Cookie.fromList(it) CookiePartitionKey.fromList(it)
} }
} }
151.toByte() -> { 151.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
HistoryItem.fromList(it) Cookie.fromList(it)
} }
} }
152.toByte() -> { 152.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
HistoryState.fromList(it) HistoryItem.fromList(it)
} }
} }
153.toByte() -> { 153.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
ReaderableState.fromList(it) HistoryState.fromList(it)
} }
} }
154.toByte() -> { 154.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
SecurityInfoState.fromList(it) ReaderableState.fromList(it)
} }
} }
155.toByte() -> { 155.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
TabContentState.fromList(it) SecurityInfoState.fromList(it)
} }
} }
156.toByte() -> { 156.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
TabContentState.fromList(it)
}
}
157.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
FindResultState.fromList(it) FindResultState.fromList(it)
} }
} }
158.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
CustomSelectionAction.fromList(it)
}
}
else -> super.readValueOfType(type, buffer) else -> super.readValueOfType(type, buffer)
} }
} }
@@ -1057,98 +1102,106 @@ private open class GeckoPigeonCodec : StandardMessageCodec() {
stream.write(133) stream.write(133)
writeValue(stream, value.raw) writeValue(stream, value.raw)
} }
is TranslationOptions -> { is SelectionPattern -> {
stream.write(134) stream.write(134)
writeValue(stream, value.toList()) writeValue(stream, value.raw)
} }
is ReaderState -> { is TranslationOptions -> {
stream.write(135) stream.write(135)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is LastMediaAccessState -> { is ReaderState -> {
stream.write(136) stream.write(136)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is HistoryMetadataKey -> { is LastMediaAccessState -> {
stream.write(137) stream.write(137)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is PackageCategoryValue -> { is HistoryMetadataKey -> {
stream.write(138) stream.write(138)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is ExternalPackage -> { is PackageCategoryValue -> {
stream.write(139) stream.write(139)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is LoadUrlFlagsValue -> { is ExternalPackage -> {
stream.write(140) stream.write(140)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is SourceValue -> { is LoadUrlFlagsValue -> {
stream.write(141) stream.write(141)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is TabState -> { is SourceValue -> {
stream.write(142) stream.write(142)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is RecoverableTab -> { is TabState -> {
stream.write(143) stream.write(143)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is RecoverableBrowserState -> { is RecoverableTab -> {
stream.write(144) stream.write(144)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is IconRequest -> { is RecoverableBrowserState -> {
stream.write(145) stream.write(145)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is ResourceSize -> { is IconRequest -> {
stream.write(146) stream.write(146)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is Resource -> { is ResourceSize -> {
stream.write(147) stream.write(147)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is IconResult -> { is Resource -> {
stream.write(148) stream.write(148)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is CookiePartitionKey -> { is IconResult -> {
stream.write(149) stream.write(149)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is Cookie -> { is CookiePartitionKey -> {
stream.write(150) stream.write(150)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is HistoryItem -> { is Cookie -> {
stream.write(151) stream.write(151)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is HistoryState -> { is HistoryItem -> {
stream.write(152) stream.write(152)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is ReaderableState -> { is HistoryState -> {
stream.write(153) stream.write(153)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is SecurityInfoState -> { is ReaderableState -> {
stream.write(154) stream.write(154)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is TabContentState -> { is SecurityInfoState -> {
stream.write(155) stream.write(155)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is FindResultState -> { is TabContentState -> {
stream.write(156) stream.write(156)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is FindResultState -> {
stream.write(157)
writeValue(stream, value.toList())
}
is CustomSelectionAction -> {
stream.write(158)
writeValue(stream, value.toList())
}
else -> super.writeValue(stream, value) else -> super.writeValue(stream, value)
} }
} }
@@ -2454,133 +2507,59 @@ class ReaderViewController(private val binaryMessenger: BinaryMessenger, private
} }
} }
} }
/** Generated class from Pigeon that represents Flutter messages that can be called from Kotlin. */ /** Generated interface from Pigeon that represents a handler of messages from Flutter. */
class SelectionAction(private val binaryMessenger: BinaryMessenger, private val messageChannelSuffix: String = "") { interface GeckoSelectionActionController {
fun setActions(actions: List<CustomSelectionAction>)
companion object { companion object {
/** The codec used by SelectionAction. */ /** The codec used by GeckoSelectionActionController. */
val codec: MessageCodec<Any?> by lazy {
GeckoPigeonCodec()
}
/** Sets up an instance of `GeckoSelectionActionController` to handle messages through the `binaryMessenger`. */
@JvmOverloads
fun setUp(binaryMessenger: BinaryMessenger, api: GeckoSelectionActionController?, messageChannelSuffix: String = "") {
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoSelectionActionController.setActions$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val actionsArg = args[0] as List<CustomSelectionAction>
val wrapped: List<Any?> = try {
api.setActions(actionsArg)
listOf(null)
} catch (exception: Throwable) {
wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
}
}
}
/** Generated class from Pigeon that represents Flutter messages that can be called from Kotlin. */
class GeckoSelectionActionEvents(private val binaryMessenger: BinaryMessenger, private val messageChannelSuffix: String = "") {
companion object {
/** The codec used by GeckoSelectionActionEvents. */
val codec: MessageCodec<Any?> by lazy { val codec: MessageCodec<Any?> by lazy {
GeckoPigeonCodec() GeckoPigeonCodec()
} }
} }
/** fun performSelectionAction(idArg: String, selectedTextArg: String, callback: (Result<Unit>) -> Unit)
* Gets Strings representing all possible selection actions.
*
* @returns String IDs for each action that could possibly be shown in the context menu. This
* array must include all actions, available or not, and must not change over the class lifetime.
*/
fun getAllActions(callback: (Result<List<String>>) -> Unit)
{ {
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.flutter_mozilla_components.SelectionAction.getAllActions$separatedMessageChannelSuffix" val channelName = "dev.flutter.pigeon.flutter_mozilla_components.GeckoSelectionActionEvents.performSelectionAction$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(null) {
if (it is List<*>) {
if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
} else if (it[0] == null) {
callback(Result.failure(FlutterError("null-error", "Flutter api returned null value for non-null return value.", "")))
} else {
val output = it[0] as List<String>
callback(Result.success(output))
}
} else {
callback(Result.failure(createConnectionError(channelName)))
}
}
}
/**
* Checks if an action can be shown on a new selection context menu.
*
* @returns whether or not the the custom action with the id of [id] is currently available
* which may be informed by [selectedText].
*/
fun isActionAvailable(idArg: String, selectedTextArg: String, callback: (Result<Boolean>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.flutter_mozilla_components.SelectionAction.isActionAvailable$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec) val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(idArg, selectedTextArg)) { channel.send(listOf(idArg, selectedTextArg)) {
if (it is List<*>) { if (it is List<*>) {
if (it.size > 1) { if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
} else if (it[0] == null) {
callback(Result.failure(FlutterError("null-error", "Flutter api returned null value for non-null return value.", "")))
} else { } else {
val output = it[0] as Boolean callback(Result.success(Unit))
callback(Result.success(output))
}
} else {
callback(Result.failure(createConnectionError(channelName)))
}
}
}
/**
* Gets a title to be shown in the selection context menu.
*
* @returns the text that should be shown on the action.
*/
fun getActionTitle(idArg: String, callback: (Result<String?>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.flutter_mozilla_components.SelectionAction.getActionTitle$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(idArg)) {
if (it is List<*>) {
if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
} else {
val output = it[0] as String?
callback(Result.success(output))
}
} else {
callback(Result.failure(createConnectionError(channelName)))
}
}
}
/**
* Should perform the action with the id of [id].
*
* @returns [true] if the action was consumed.
*/
fun performAction(idArg: String, selectedTextArg: String, callback: (Result<Boolean>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.flutter_mozilla_components.SelectionAction.performAction$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(idArg, selectedTextArg)) {
if (it is List<*>) {
if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
} else if (it[0] == null) {
callback(Result.failure(FlutterError("null-error", "Flutter api returned null value for non-null return value.", "")))
} else {
val output = it[0] as Boolean
callback(Result.success(output))
}
} else {
callback(Result.failure(createConnectionError(channelName)))
}
}
}
/**
* Takes in a list of actions and sorts them.
*
* @returns the sorted list.
*/
fun sortedActions(actionsArg: List<String>, callback: (Result<List<String>>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.flutter_mozilla_components.SelectionAction.sortedActions$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(actionsArg)) {
if (it is List<*>) {
if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
} else if (it[0] == null) {
callback(Result.failure(FlutterError("null-error", "Flutter api returned null value for non-null return value.", "")))
} else {
val output = it[0] as List<String>
callback(Result.success(output))
} }
} else { } else {
callback(Result.failure(createConnectionError(channelName))) callback(Result.failure(createConnectionError(channelName)))
@@ -1,12 +1,14 @@
//export 'src/domain/services/gecko_browser.dart'; //export 'src/domain/services/gecko_browser.dart';
export 'src/data/models/load_url_flags.dart'; export 'src/data/models/load_url_flags.dart';
export 'src/data/models/source.dart'; export 'src/data/models/source.dart';
export 'src/domain/entities/default_selection_actions.dart';
export 'src/domain/services/gecko_cookie.dart'; export 'src/domain/services/gecko_cookie.dart';
export 'src/domain/services/gecko_engine_settings.dart'; export 'src/domain/services/gecko_engine_settings.dart';
export 'src/domain/services/gecko_event.dart'; export 'src/domain/services/gecko_event.dart';
export 'src/domain/services/gecko_find_in_page.dart'; export 'src/domain/services/gecko_find_in_page.dart';
export 'src/domain/services/gecko_icon.dart'; export 'src/domain/services/gecko_icon.dart';
export 'src/domain/services/gecko_readerable.dart'; export 'src/domain/services/gecko_readerable.dart';
export 'src/domain/services/gecko_selection_action.dart';
export 'src/domain/services/gecko_session.dart'; export 'src/domain/services/gecko_session.dart';
export 'src/domain/services/gecko_tab.dart'; export 'src/domain/services/gecko_tab.dart';
export 'src/geckoview_widget.dart'; export 'src/geckoview_widget.dart';
@@ -0,0 +1,61 @@
import 'package:flutter_mozilla_components/src/pigeons/gecko.g.dart';
typedef PerformAction = void Function(String selectedText);
class BaseSelectionAction extends CustomSelectionAction {
final PerformAction performAction;
BaseSelectionAction({
required super.id,
required super.title,
required this.performAction,
super.pattern,
});
}
class CallAction extends BaseSelectionAction {
CallAction(PerformAction action)
: super(
id: 'CUSTOM_CONTEXT_MENU_CALL',
title: 'Call',
pattern: SelectionPattern.phone,
performAction: action,
);
}
class EmailAction extends BaseSelectionAction {
EmailAction(PerformAction action)
: super(
id: 'CUSTOM_CONTEXT_MENU_EMAIL',
title: 'Email',
pattern: SelectionPattern.email,
performAction: action,
);
}
class SearchAction extends BaseSelectionAction {
SearchAction(PerformAction action)
: super(
id: 'CUSTOM_CONTEXT_MENU_SEARCH',
title: 'Search',
performAction: action,
);
}
class PrivateSearchAction extends BaseSelectionAction {
PrivateSearchAction(PerformAction action)
: super(
id: 'CUSTOM_CONTEXT_MENU_SEARCH_PRIVATELY',
title: 'Private Search',
performAction: action,
);
}
class ShareAction extends BaseSelectionAction {
ShareAction(PerformAction action)
: super(
id: 'CUSTOM_CONTEXT_MENU_SHARE',
title: 'Share',
performAction: action,
);
}
@@ -0,0 +1,38 @@
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:flutter_mozilla_components/src/domain/entities/default_selection_actions.dart';
import 'package:flutter_mozilla_components/src/pigeons/gecko.g.dart';
final _apiInstance = GeckoSelectionActionController();
class GeckoSelectionActionService extends GeckoSelectionActionEvents {
final GeckoSelectionActionController _api;
List<BaseSelectionAction> _actions;
List<BaseSelectionAction> get actions => _actions;
Future<void> setActions(List<BaseSelectionAction> value) async {
_actions = value;
await _api.setActions(actions);
}
GeckoSelectionActionService.setUp({
BinaryMessenger? binaryMessenger,
String messageChannelSuffix = '',
GeckoSelectionActionController? api,
}) : _api = api ?? _apiInstance,
_actions = [] {
GeckoSelectionActionEvents.setUp(
this,
binaryMessenger: binaryMessenger,
messageChannelSuffix: messageChannelSuffix,
);
}
@override
void performSelectionAction(String id, String selectedText) {
_actions.firstWhere((x) => x.id == id).performAction(selectedText);
}
}
@@ -78,6 +78,11 @@ enum CookieSameSiteStatus {
unspecified, unspecified,
} }
enum SelectionPattern {
phone,
email,
}
/// Translation options that map to the Gecko Translations Options. /// Translation options that map to the Gecko Translations Options.
/// ///
/// @property downloadModel If the necessary models should be downloaded on request. If false, then /// @property downloadModel If the necessary models should be downloaded on request. If false, then
@@ -986,6 +991,37 @@ class FindResultState {
} }
} }
class CustomSelectionAction {
CustomSelectionAction({
required this.id,
required this.title,
this.pattern,
});
String id;
String title;
SelectionPattern? pattern;
Object encode() {
return <Object?>[
id,
title,
pattern,
];
}
static CustomSelectionAction decode(Object result) {
result as List<Object?>;
return CustomSelectionAction(
id: result[0]! as String,
title: result[1]! as String,
pattern: result[2] as SelectionPattern?,
);
}
}
class _PigeonCodec extends StandardMessageCodec { class _PigeonCodec extends StandardMessageCodec {
const _PigeonCodec(); const _PigeonCodec();
@@ -1009,75 +1045,81 @@ class _PigeonCodec extends StandardMessageCodec {
} else if (value is CookieSameSiteStatus) { } else if (value is CookieSameSiteStatus) {
buffer.putUint8(133); buffer.putUint8(133);
writeValue(buffer, value.index); writeValue(buffer, value.index);
} else if (value is TranslationOptions) { } else if (value is SelectionPattern) {
buffer.putUint8(134); buffer.putUint8(134);
writeValue(buffer, value.encode()); writeValue(buffer, value.index);
} else if (value is ReaderState) { } else if (value is TranslationOptions) {
buffer.putUint8(135); buffer.putUint8(135);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is LastMediaAccessState) { } else if (value is ReaderState) {
buffer.putUint8(136); buffer.putUint8(136);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is HistoryMetadataKey) { } else if (value is LastMediaAccessState) {
buffer.putUint8(137); buffer.putUint8(137);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is PackageCategoryValue) { } else if (value is HistoryMetadataKey) {
buffer.putUint8(138); buffer.putUint8(138);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is ExternalPackage) { } else if (value is PackageCategoryValue) {
buffer.putUint8(139); buffer.putUint8(139);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is LoadUrlFlagsValue) { } else if (value is ExternalPackage) {
buffer.putUint8(140); buffer.putUint8(140);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is SourceValue) { } else if (value is LoadUrlFlagsValue) {
buffer.putUint8(141); buffer.putUint8(141);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is TabState) { } else if (value is SourceValue) {
buffer.putUint8(142); buffer.putUint8(142);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is RecoverableTab) { } else if (value is TabState) {
buffer.putUint8(143); buffer.putUint8(143);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is RecoverableBrowserState) { } else if (value is RecoverableTab) {
buffer.putUint8(144); buffer.putUint8(144);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is IconRequest) { } else if (value is RecoverableBrowserState) {
buffer.putUint8(145); buffer.putUint8(145);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is ResourceSize) { } else if (value is IconRequest) {
buffer.putUint8(146); buffer.putUint8(146);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is Resource) { } else if (value is ResourceSize) {
buffer.putUint8(147); buffer.putUint8(147);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is IconResult) { } else if (value is Resource) {
buffer.putUint8(148); buffer.putUint8(148);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is CookiePartitionKey) { } else if (value is IconResult) {
buffer.putUint8(149); buffer.putUint8(149);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is Cookie) { } else if (value is CookiePartitionKey) {
buffer.putUint8(150); buffer.putUint8(150);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is HistoryItem) { } else if (value is Cookie) {
buffer.putUint8(151); buffer.putUint8(151);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is HistoryState) { } else if (value is HistoryItem) {
buffer.putUint8(152); buffer.putUint8(152);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is ReaderableState) { } else if (value is HistoryState) {
buffer.putUint8(153); buffer.putUint8(153);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is SecurityInfoState) { } else if (value is ReaderableState) {
buffer.putUint8(154); buffer.putUint8(154);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is TabContentState) { } else if (value is SecurityInfoState) {
buffer.putUint8(155); buffer.putUint8(155);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is FindResultState) { } else if (value is TabContentState) {
buffer.putUint8(156); buffer.putUint8(156);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is FindResultState) {
buffer.putUint8(157);
writeValue(buffer, value.encode());
} else if (value is CustomSelectionAction) {
buffer.putUint8(158);
writeValue(buffer, value.encode());
} else { } else {
super.writeValue(buffer, value); super.writeValue(buffer, value);
} }
@@ -1102,51 +1144,56 @@ class _PigeonCodec extends StandardMessageCodec {
final int? value = readValue(buffer) as int?; final int? value = readValue(buffer) as int?;
return value == null ? null : CookieSameSiteStatus.values[value]; return value == null ? null : CookieSameSiteStatus.values[value];
case 134: case 134:
return TranslationOptions.decode(readValue(buffer)!); final int? value = readValue(buffer) as int?;
return value == null ? null : SelectionPattern.values[value];
case 135: case 135:
return ReaderState.decode(readValue(buffer)!); return TranslationOptions.decode(readValue(buffer)!);
case 136: case 136:
return LastMediaAccessState.decode(readValue(buffer)!); return ReaderState.decode(readValue(buffer)!);
case 137: case 137:
return HistoryMetadataKey.decode(readValue(buffer)!); return LastMediaAccessState.decode(readValue(buffer)!);
case 138: case 138:
return PackageCategoryValue.decode(readValue(buffer)!); return HistoryMetadataKey.decode(readValue(buffer)!);
case 139: case 139:
return ExternalPackage.decode(readValue(buffer)!); return PackageCategoryValue.decode(readValue(buffer)!);
case 140: case 140:
return LoadUrlFlagsValue.decode(readValue(buffer)!); return ExternalPackage.decode(readValue(buffer)!);
case 141: case 141:
return SourceValue.decode(readValue(buffer)!); return LoadUrlFlagsValue.decode(readValue(buffer)!);
case 142: case 142:
return TabState.decode(readValue(buffer)!); return SourceValue.decode(readValue(buffer)!);
case 143: case 143:
return RecoverableTab.decode(readValue(buffer)!); return TabState.decode(readValue(buffer)!);
case 144: case 144:
return RecoverableBrowserState.decode(readValue(buffer)!); return RecoverableTab.decode(readValue(buffer)!);
case 145: case 145:
return IconRequest.decode(readValue(buffer)!); return RecoverableBrowserState.decode(readValue(buffer)!);
case 146: case 146:
return ResourceSize.decode(readValue(buffer)!); return IconRequest.decode(readValue(buffer)!);
case 147: case 147:
return Resource.decode(readValue(buffer)!); return ResourceSize.decode(readValue(buffer)!);
case 148: case 148:
return IconResult.decode(readValue(buffer)!); return Resource.decode(readValue(buffer)!);
case 149: case 149:
return CookiePartitionKey.decode(readValue(buffer)!); return IconResult.decode(readValue(buffer)!);
case 150: case 150:
return Cookie.decode(readValue(buffer)!); return CookiePartitionKey.decode(readValue(buffer)!);
case 151: case 151:
return HistoryItem.decode(readValue(buffer)!); return Cookie.decode(readValue(buffer)!);
case 152: case 152:
return HistoryState.decode(readValue(buffer)!); return HistoryItem.decode(readValue(buffer)!);
case 153: case 153:
return ReaderableState.decode(readValue(buffer)!); return HistoryState.decode(readValue(buffer)!);
case 154: case 154:
return SecurityInfoState.decode(readValue(buffer)!); return ReaderableState.decode(readValue(buffer)!);
case 155: case 155:
return TabContentState.decode(readValue(buffer)!); return SecurityInfoState.decode(readValue(buffer)!);
case 156: case 156:
return TabContentState.decode(readValue(buffer)!);
case 157:
return FindResultState.decode(readValue(buffer)!); return FindResultState.decode(readValue(buffer)!);
case 158:
return CustomSelectionAction.decode(readValue(buffer)!);
default: default:
return super.readValueOfType(type, buffer); return super.readValueOfType(type, buffer);
} }
@@ -2735,155 +2782,69 @@ abstract class ReaderViewController {
} }
} }
abstract class SelectionAction { class GeckoSelectionActionController {
/// Constructor for [GeckoSelectionActionController]. 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.
GeckoSelectionActionController({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''})
: pigeonVar_binaryMessenger = binaryMessenger,
pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : '';
final BinaryMessenger? pigeonVar_binaryMessenger;
static const MessageCodec<Object?> pigeonChannelCodec = _PigeonCodec(); static const MessageCodec<Object?> pigeonChannelCodec = _PigeonCodec();
/// Gets Strings representing all possible selection actions. final String pigeonVar_messageChannelSuffix;
///
/// @returns String IDs for each action that could possibly be shown in the context menu. This
/// array must include all actions, available or not, and must not change over the class lifetime.
List<String> getAllActions();
/// Checks if an action can be shown on a new selection context menu. Future<void> setActions(List<CustomSelectionAction> actions) async {
/// final String pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoSelectionActionController.setActions$pigeonVar_messageChannelSuffix';
/// @returns whether or not the the custom action with the id of [id] is currently available final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
/// which may be informed by [selectedText]. pigeonVar_channelName,
bool isActionAvailable(String id, String selectedText); pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final List<Object?>? pigeonVar_replyList =
await pigeonVar_channel.send(<Object?>[actions]) as List<Object?>?;
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 {
return;
}
}
}
/// Gets a title to be shown in the selection context menu. abstract class GeckoSelectionActionEvents {
/// static const MessageCodec<Object?> pigeonChannelCodec = _PigeonCodec();
/// @returns the text that should be shown on the action.
String? getActionTitle(String id);
/// Should perform the action with the id of [id]. void performSelectionAction(String id, String selectedText);
///
/// @returns [true] if the action was consumed.
bool performAction(String id, String selectedText);
/// Takes in a list of actions and sorts them. static void setUp(GeckoSelectionActionEvents? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) {
///
/// @returns the sorted list.
List<String> sortedActions(List<String> actions);
static void setUp(SelectionAction? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) {
messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : '';
{ {
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>( final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.flutter_mozilla_components.SelectionAction.getAllActions$messageChannelSuffix', pigeonChannelCodec, 'dev.flutter.pigeon.flutter_mozilla_components.GeckoSelectionActionEvents.performSelectionAction$messageChannelSuffix', pigeonChannelCodec,
binaryMessenger: binaryMessenger);
if (api == null) {
pigeonVar_channel.setMessageHandler(null);
} else {
pigeonVar_channel.setMessageHandler((Object? message) async {
try {
final List<String> output = api.getAllActions();
return wrapResponse(result: output);
} on PlatformException catch (e) {
return wrapResponse(error: e);
} catch (e) {
return wrapResponse(error: PlatformException(code: 'error', message: e.toString()));
}
});
}
}
{
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.flutter_mozilla_components.SelectionAction.isActionAvailable$messageChannelSuffix', pigeonChannelCodec,
binaryMessenger: binaryMessenger); binaryMessenger: binaryMessenger);
if (api == null) { if (api == null) {
pigeonVar_channel.setMessageHandler(null); pigeonVar_channel.setMessageHandler(null);
} else { } else {
pigeonVar_channel.setMessageHandler((Object? message) async { pigeonVar_channel.setMessageHandler((Object? message) async {
assert(message != null, assert(message != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.SelectionAction.isActionAvailable was null.'); 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoSelectionActionEvents.performSelectionAction was null.');
final List<Object?> args = (message as List<Object?>?)!; final List<Object?> args = (message as List<Object?>?)!;
final String? arg_id = (args[0] as String?); final String? arg_id = (args[0] as String?);
assert(arg_id != null, assert(arg_id != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.SelectionAction.isActionAvailable was null, expected non-null String.'); 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoSelectionActionEvents.performSelectionAction was null, expected non-null String.');
final String? arg_selectedText = (args[1] as String?); final String? arg_selectedText = (args[1] as String?);
assert(arg_selectedText != null, assert(arg_selectedText != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.SelectionAction.isActionAvailable was null, expected non-null String.'); 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoSelectionActionEvents.performSelectionAction was null, expected non-null String.');
try { try {
final bool output = api.isActionAvailable(arg_id!, arg_selectedText!); api.performSelectionAction(arg_id!, arg_selectedText!);
return wrapResponse(result: output); return wrapResponse(empty: true);
} on PlatformException catch (e) {
return wrapResponse(error: e);
} catch (e) {
return wrapResponse(error: PlatformException(code: 'error', message: e.toString()));
}
});
}
}
{
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.flutter_mozilla_components.SelectionAction.getActionTitle$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.SelectionAction.getActionTitle was null.');
final List<Object?> args = (message as List<Object?>?)!;
final String? arg_id = (args[0] as String?);
assert(arg_id != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.SelectionAction.getActionTitle was null, expected non-null String.');
try {
final String? output = api.getActionTitle(arg_id!);
return wrapResponse(result: output);
} on PlatformException catch (e) {
return wrapResponse(error: e);
} catch (e) {
return wrapResponse(error: PlatformException(code: 'error', message: e.toString()));
}
});
}
}
{
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.flutter_mozilla_components.SelectionAction.performAction$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.SelectionAction.performAction was null.');
final List<Object?> args = (message as List<Object?>?)!;
final String? arg_id = (args[0] as String?);
assert(arg_id != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.SelectionAction.performAction was null, expected non-null String.');
final String? arg_selectedText = (args[1] as String?);
assert(arg_selectedText != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.SelectionAction.performAction was null, expected non-null String.');
try {
final bool output = api.performAction(arg_id!, arg_selectedText!);
return wrapResponse(result: output);
} on PlatformException catch (e) {
return wrapResponse(error: e);
} catch (e) {
return wrapResponse(error: PlatformException(code: 'error', message: e.toString()));
}
});
}
}
{
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.flutter_mozilla_components.SelectionAction.sortedActions$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.SelectionAction.sortedActions was null.');
final List<Object?> args = (message as List<Object?>?)!;
final List<String>? arg_actions = (args[0] as List<Object?>?)?.cast<String>();
assert(arg_actions != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.SelectionAction.sortedActions was null, expected non-null List<String>.');
try {
final List<String> output = api.sortedActions(arg_actions!);
return wrapResponse(result: output);
} on PlatformException catch (e) { } on PlatformException catch (e) {
return wrapResponse(error: e); return wrapResponse(error: e);
} catch (e) { } catch (e) {
@@ -469,6 +469,16 @@ class FindResultState {
); );
} }
enum SelectionPattern { phone, email }
class CustomSelectionAction {
final String id;
final String title;
final SelectionPattern? pattern;
CustomSelectionAction(this.id, this.title, this.pattern);
}
// /// Represents all the different supported types of data that can be found from long clicking // /// Represents all the different supported types of data that can be found from long clicking
// /// an element. // /// an element.
// sealed class HitResult { // sealed class HitResult {
@@ -816,32 +826,12 @@ abstract class ReaderViewController {
void appearanceButtonVisibility(bool visible); void appearanceButtonVisibility(bool visible);
} }
@FlutterApi() @HostApi()
abstract class SelectionAction { abstract class GeckoSelectionActionController {
/// Gets Strings representing all possible selection actions. void setActions(List<CustomSelectionAction> actions);
/// }
/// @returns String IDs for each action that could possibly be shown in the context menu. This
/// array must include all actions, available or not, and must not change over the class lifetime. @FlutterApi()
List<String> getAllActions(); abstract class GeckoSelectionActionEvents {
void performSelectionAction(String id, String selectedText);
/// Checks if an action can be shown on a new selection context menu.
///
/// @returns whether or not the the custom action with the id of [id] is currently available
/// which may be informed by [selectedText].
bool isActionAvailable(String id, String selectedText);
/// Gets a title to be shown in the selection context menu.
///
/// @returns the text that should be shown on the action.
String? getActionTitle(String id);
/// Should perform the action with the id of [id].
///
/// @returns [true] if the action was consumed.
bool performAction(String id, String selectedText);
/// Takes in a list of actions and sorts them.
///
/// @returns the sorted list.
List<String> sortedActions(List<String> actions);
} }