Skip to content

Commit be05756

Browse files
committed
app links setting
1 parent aeb8a8d commit be05756

File tree

18 files changed

+1124
-760
lines changed

18 files changed

+1124
-760
lines changed

app/lib/core/routing/routes.g.dart

Lines changed: 472 additions & 472 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/lib/features/geckoview/features/browser/domain/providers.dart

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
*/
2020
import 'package:collection/collection.dart';
2121
import 'package:fast_equatable/fast_equatable.dart';
22+
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
2223
import 'package:nullability/nullability.dart';
2324
import 'package:riverpod/riverpod.dart';
2425
import 'package:riverpod_annotation/riverpod_annotation.dart';
@@ -375,3 +376,18 @@ EquatableValue<List<TabPreview>> filteredTabPreviews(
375376
.toList(),
376377
);
377378
}
379+
380+
@Riverpod()
381+
class AppLinksModeNotifier extends _$AppLinksModeNotifier {
382+
final _service = GeckoEngineSettingsService();
383+
384+
Future<void> setMode(AppLinksMode mode) async {
385+
await _service.setAppLinksMode(mode);
386+
ref.invalidateSelf();
387+
}
388+
389+
@override
390+
Future<AppLinksMode> build() {
391+
return _service.getAppLinksMode();
392+
}
393+
}

app/lib/features/geckoview/features/browser/domain/providers.g.dart

Lines changed: 45 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/lib/features/geckoview/features/browser/domain/services/engine_settings_replication.dart

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@ import 'package:weblibre/features/user/domain/repositories/general_settings.dart
3030
part 'engine_settings_replication.g.dart';
3131

3232
/// Checks if any Custom ETP setting changed between two EngineSettings instances.
33-
bool _customEtpSettingsChanged(GeckoEngineSettings? previous, GeckoEngineSettings current) {
33+
bool _customEtpSettingsChanged(
34+
GeckoEngineSettings? previous,
35+
GeckoEngineSettings current,
36+
) {
3437
if (previous == null) return true;
3538
return previous.blockCookies != current.blockCookies ||
3639
previous.customCookiePolicy != current.customCookiePolicy ||
@@ -39,8 +42,10 @@ bool _customEtpSettingsChanged(GeckoEngineSettings? previous, GeckoEngineSetting
3942
previous.blockCryptominers != current.blockCryptominers ||
4043
previous.blockFingerprinters != current.blockFingerprinters ||
4144
previous.blockRedirectTrackers != current.blockRedirectTrackers ||
42-
previous.blockSuspectedFingerprinters != current.blockSuspectedFingerprinters ||
43-
previous.suspectedFingerprintersScope != current.suspectedFingerprintersScope ||
45+
previous.blockSuspectedFingerprinters !=
46+
current.blockSuspectedFingerprinters ||
47+
previous.suspectedFingerprintersScope !=
48+
current.suspectedFingerprintersScope ||
4449
previous.allowListBaseline != current.allowListBaseline ||
4550
previous.allowListConvenience != current.allowListConvenience;
4651
}
@@ -107,14 +112,18 @@ class EngineSettingsReplicationService
107112
await _service.javascriptEnabled(settings.javascriptEnabled);
108113
}
109114
// Check if tracking protection policy mode changed OR any custom ETP setting changed
110-
final policyModeChanged = previous.value?.trackingProtectionPolicy !=
115+
final policyModeChanged =
116+
previous.value?.trackingProtectionPolicy !=
111117
settings.trackingProtectionPolicy;
112-
final customSettingsChanged = settings.trackingProtectionPolicy == TrackingProtectionPolicy.custom &&
118+
final customSettingsChanged =
119+
settings.trackingProtectionPolicy ==
120+
TrackingProtectionPolicy.custom &&
113121
_customEtpSettingsChanged(previous.value, settings);
114122

115123
if (policyModeChanged || customSettingsChanged) {
116124
// Always send full custom settings when in CUSTOM mode
117-
if (settings.trackingProtectionPolicy == TrackingProtectionPolicy.custom) {
125+
if (settings.trackingProtectionPolicy ==
126+
TrackingProtectionPolicy.custom) {
118127
await _service.customTrackingProtectionPolicy(
119128
trackingProtectionPolicy: settings.trackingProtectionPolicy,
120129
blockCookies: settings.blockCookies,
@@ -124,8 +133,10 @@ class EngineSettingsReplicationService
124133
blockCryptominers: settings.blockCryptominers,
125134
blockFingerprinters: settings.blockFingerprinters,
126135
blockRedirectTrackers: settings.blockRedirectTrackers,
127-
blockSuspectedFingerprinters: settings.blockSuspectedFingerprinters,
128-
suspectedFingerprintersScope: settings.suspectedFingerprintersScope,
136+
blockSuspectedFingerprinters:
137+
settings.blockSuspectedFingerprinters,
138+
suspectedFingerprintersScope:
139+
settings.suspectedFingerprintersScope,
129140
allowListBaseline: settings.allowListBaseline,
130141
allowListConvenience: settings.allowListConvenience,
131142
);

app/lib/features/geckoview/features/browser/domain/services/engine_settings_replication.g.dart

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/lib/features/geckoview/features/browser/presentation/providers/site_settings_badge_provider.g.dart

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/lib/features/settings/presentation/screens/tabs_behavior_settings.dart

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@
2020
import 'package:fading_scroll/fading_scroll.dart';
2121
import 'package:flutter/material.dart';
2222
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
23+
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
2324
import 'package:hooks_riverpod/hooks_riverpod.dart';
2425
import 'package:weblibre/core/design/app_colors.dart';
2526
import 'package:weblibre/core/routing/routes.dart';
27+
import 'package:weblibre/features/geckoview/features/browser/domain/providers.dart';
2628
import 'package:weblibre/features/settings/presentation/controllers/save_settings.dart';
2729
import 'package:weblibre/features/settings/presentation/widgets/sections.dart';
2830
import 'package:weblibre/features/user/data/models/general_settings.dart';
@@ -65,6 +67,7 @@ class _TabCreationSection extends StatelessWidget {
6567
SettingSection(name: 'Tab Creation'),
6668
_NewTabDefaultSection(),
6769
_ExternalLinkHandlingSection(),
70+
_AppLinksModeSection(),
6871
],
6972
);
7073
}
@@ -340,3 +343,63 @@ class _TabBarSwipeBehaviorSection extends HookConsumerWidget {
340343
);
341344
}
342345
}
346+
347+
class _AppLinksModeSection extends HookConsumerWidget {
348+
const _AppLinksModeSection();
349+
350+
@override
351+
Widget build(BuildContext context, WidgetRef ref) {
352+
final appLinksMode = ref.watch(
353+
appLinksModeProvider.select((value) => value.value),
354+
);
355+
356+
return Padding(
357+
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8),
358+
child: Column(
359+
mainAxisSize: MainAxisSize.min,
360+
crossAxisAlignment: CrossAxisAlignment.start,
361+
children: [
362+
const ListTile(
363+
title: Text('Open Links in Apps'),
364+
subtitle: Text(
365+
'Choose how links that can be opened in other apps are handled',
366+
),
367+
leading: Icon(MdiIcons.openInApp),
368+
contentPadding: EdgeInsets.zero,
369+
),
370+
RadioGroup(
371+
groupValue: appLinksMode,
372+
onChanged: (value) async {
373+
if (value != null) {
374+
await ref.read(appLinksModeProvider.notifier).setMode(value);
375+
}
376+
},
377+
child: const Column(
378+
children: [
379+
RadioListTile.adaptive(
380+
value: AppLinksMode.always,
381+
title: Text('Always'),
382+
subtitle: Text(
383+
'Always open links in their native apps without asking',
384+
),
385+
),
386+
RadioListTile.adaptive(
387+
value: AppLinksMode.ask,
388+
title: Text('Ask before opening'),
389+
subtitle: Text('Show a prompt before opening links in apps'),
390+
),
391+
RadioListTile.adaptive(
392+
value: AppLinksMode.never,
393+
title: Text('Never'),
394+
subtitle: Text(
395+
'Always open links in the browser instead of apps',
396+
),
397+
),
398+
],
399+
),
400+
),
401+
],
402+
),
403+
);
404+
}
405+
}

packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/BaseBrowserFragment.kt

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ import android.widget.FrameLayout
1919
import androidx.activity.result.ActivityResultLauncher
2020
import androidx.activity.result.contract.ActivityResultContracts
2121
import androidx.annotation.CallSuper
22+
import androidx.core.content.edit
2223
import androidx.fragment.app.Fragment
24+
import androidx.preference.PreferenceManager
2325
import eu.weblibre.flutter_mozilla_components.addons.WebExtensionActionPopupActivity
2426
import eu.weblibre.flutter_mozilla_components.addons.WebExtensionPromptFeature
2527
import eu.weblibre.flutter_mozilla_components.databinding.FragmentBrowserBinding
@@ -297,8 +299,13 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Activit
297299
sessionId = sessionId,
298300
fragmentManager = parentFragmentManager,
299301
loadUrlUseCase = components.useCases.sessionUseCases.loadUrl,
300-
launchInApp = { true },
301-
shouldPrompt = { true },
302+
launchInApp = { GlobalComponents.shouldOpenLinksInApp() },
303+
shouldPrompt = { GlobalComponents.shouldPromptOpenLinksInApp() },
304+
alwaysOpenCheckboxAction = {
305+
GlobalComponents.engineSettingsApi?.setAppLinksMode(
306+
eu.weblibre.flutter_mozilla_components.pigeons.AppLinksMode.ALWAYS
307+
)
308+
},
302309
failedToLaunchAction = { fallbackUrl ->
303310
fallbackUrl?.let {
304311
val appLinksUseCases = components.useCases.appLinksUseCases

packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/GlobalComponents.kt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import eu.weblibre.flutter_mozilla_components.pigeons.GeckoTabContentEvents
1717
import eu.weblibre.flutter_mozilla_components.pigeons.GeckoViewportEvents
1818
import eu.weblibre.flutter_mozilla_components.pigeons.ReaderViewController
1919
import eu.weblibre.flutter_mozilla_components.api.GeckoViewportApiImpl
20+
import eu.weblibre.flutter_mozilla_components.api.GeckoEngineSettingsApiImpl
2021
import kotlinx.coroutines.DelicateCoroutinesApi
2122
import kotlinx.coroutines.Dispatchers
2223
import kotlinx.coroutines.GlobalScope
@@ -53,6 +54,25 @@ object GlobalComponents {
5354
// Viewport API for applying pending settings when engineView becomes available
5455
var viewportApi: GeckoViewportApiImpl? = null
5556

57+
// Engine settings API for managing engine-specific settings
58+
var engineSettingsApi: GeckoEngineSettingsApiImpl? = null
59+
60+
fun shouldOpenLinksInApp(): Boolean {
61+
return when (engineSettingsApi!!.getAppLinksMode()) {
62+
eu.weblibre.flutter_mozilla_components.pigeons.AppLinksMode.ALWAYS -> true
63+
eu.weblibre.flutter_mozilla_components.pigeons.AppLinksMode.ASK -> true
64+
eu.weblibre.flutter_mozilla_components.pigeons.AppLinksMode.NEVER -> false
65+
}
66+
}
67+
68+
fun shouldPromptOpenLinksInApp(): Boolean {
69+
return when (engineSettingsApi!!.getAppLinksMode()) {
70+
eu.weblibre.flutter_mozilla_components.pigeons.AppLinksMode.ALWAYS -> false
71+
eu.weblibre.flutter_mozilla_components.pigeons.AppLinksMode.ASK -> true
72+
eu.weblibre.flutter_mozilla_components.pigeons.AppLinksMode.NEVER -> false
73+
}
74+
}
75+
5676
@DelicateCoroutinesApi
5777
private fun restoreBrowserState(newComponents: Components) =
5878
GlobalScope.launch(Dispatchers.Main) {

packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoBrowserApiImpl.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,10 +231,12 @@ class GeckoBrowserApiImpl : GeckoBrowserApi {
231231
addonCollection
232232
)
233233

234+
val engineSettingsApiImpl = GeckoEngineSettingsApiImpl()
234235
GeckoEngineSettingsApi.setUp(
235236
_flutterPluginBinding.binaryMessenger,
236-
GeckoEngineSettingsApiImpl()
237+
engineSettingsApiImpl
237238
)
239+
GlobalComponents.engineSettingsApi = engineSettingsApiImpl
238240
GeckoAddonsApi.setUp(
239241
_flutterPluginBinding.binaryMessenger,
240242
GeckoAddonsApiImpl(profileApplicationContext)

0 commit comments

Comments
 (0)