improve back navigation and ask tabs to close when originated from intent
This commit is contained in:
@@ -27,19 +27,30 @@ import io.flutter.embedding.engine.dart.DartExecutor
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
|
||||
class MainActivity: FlutterFragmentActivity() {
|
||||
private val CHANNEL = "eu.weblibre.flutter_mozilla_components/trim_memory"
|
||||
private val TRIM_MEMORY_CHANNEL = "eu.weblibre.flutter_mozilla_components/trim_memory"
|
||||
private val ACTIVITY_CHANNEL = "eu.weblibre.gecko/activity"
|
||||
private val ENGINE_ID = "engine_id"
|
||||
private var channel: MethodChannel? = null
|
||||
private var trimMemoryChannel: MethodChannel? = null
|
||||
|
||||
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
|
||||
super.configureFlutterEngine(flutterEngine)
|
||||
|
||||
channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
|
||||
trimMemoryChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, TRIM_MEMORY_CHANNEL)
|
||||
|
||||
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, ACTIVITY_CHANNEL).setMethodCallHandler { call, result ->
|
||||
when (call.method) {
|
||||
"moveTaskToBack" -> {
|
||||
moveTaskToBack(true)
|
||||
result.success(null)
|
||||
}
|
||||
else -> result.notImplemented()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTrimMemory(level: Int) {
|
||||
super.onTrimMemory(level)
|
||||
channel?.invokeMethod("onTrimMemory", level)
|
||||
trimMemoryChannel?.invokeMethod("onTrimMemory", level)
|
||||
}
|
||||
|
||||
override fun provideFlutterEngine(context: Context): FlutterEngine {
|
||||
|
||||
@@ -54,6 +54,10 @@ class TabRepository extends _$TabRepository {
|
||||
return _tabFromIntent.contains(tabId);
|
||||
}
|
||||
|
||||
void clearLaunchedFromIntent(String tabId) {
|
||||
_tabFromIntent.remove(tabId);
|
||||
}
|
||||
|
||||
Future<String?> _resolveParentIdForContext({
|
||||
required String? parentId,
|
||||
required String? targetContextId,
|
||||
|
||||
@@ -41,7 +41,7 @@ final class TabRepositoryProvider
|
||||
}
|
||||
}
|
||||
|
||||
String _$tabRepositoryHash() => r'f0ff983df4115bfed7ff3dbc8994592bc46a1daa';
|
||||
String _$tabRepositoryHash() => r'b300b88bc7817b533be46d8d0d933786b10212fb';
|
||||
|
||||
abstract class _$TabRepository extends $Notifier<void> {
|
||||
void build();
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2024-2026 Fabian Freund.
|
||||
*
|
||||
* This file is part of WebLibre
|
||||
* (see https://weblibre.eu).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// Shows a dialog asking the user whether to keep a tab that was opened from another app.
|
||||
///
|
||||
/// Returns true if the user wants to keep the tab, false if they want to discard it.
|
||||
Future<bool?> showKeepTabDialog(BuildContext context) {
|
||||
return showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('Keep tab?'),
|
||||
content: const Text(
|
||||
'This tab was opened from another app. Do you want to keep it or discard it?',
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(false),
|
||||
child: const Text('Discard'),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () => Navigator.of(context).pop(true),
|
||||
child: const Text('Keep'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -21,7 +21,6 @@ import 'dart:async';
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
@@ -42,6 +41,7 @@ import 'package:weblibre/features/geckoview/domain/repositories/tab.dart';
|
||||
import 'package:weblibre/features/geckoview/features/browser/domain/entities/sheet.dart';
|
||||
import 'package:weblibre/features/geckoview/features/browser/presentation/controllers/tab_bar_dismissable.dart';
|
||||
import 'package:weblibre/features/geckoview/features/browser/presentation/controllers/tab_view_controllers.dart';
|
||||
import 'package:weblibre/features/geckoview/features/browser/presentation/dialogs/keep_tab_dialog.dart';
|
||||
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/browser_modules/bottom_app_bar.dart';
|
||||
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/browser_modules/browser_fab.dart';
|
||||
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/browser_modules/browser_view.dart';
|
||||
@@ -57,6 +57,7 @@ import 'package:weblibre/features/geckoview/features/find_in_page/presentation/w
|
||||
import 'package:weblibre/features/geckoview/features/readerview/presentation/controllers/readerable.dart';
|
||||
import 'package:weblibre/features/user/data/models/general_settings.dart';
|
||||
import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
|
||||
import 'package:weblibre/utils/move_to_background.dart';
|
||||
import 'package:weblibre/utils/ui_helper.dart' as ui_helper;
|
||||
|
||||
/// Callback for toolbar animation progress updates.
|
||||
@@ -810,9 +811,26 @@ class _Browser extends HookConsumerWidget {
|
||||
if (ref
|
||||
.read(tabRepositoryProvider.notifier)
|
||||
.hasLaunchedFromIntent(tabState?.id)) {
|
||||
//Mark back as unhandled and navigator will pop
|
||||
await SystemNavigator.pop();
|
||||
return false;
|
||||
if (!context.mounted) return false;
|
||||
|
||||
final keep = await showKeepTabDialog(context);
|
||||
if (keep == true) {
|
||||
ref
|
||||
.read(tabRepositoryProvider.notifier)
|
||||
.clearLaunchedFromIntent(tabState!.id);
|
||||
|
||||
await moveToBackground();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (tabState != null) {
|
||||
await ref
|
||||
.read(tabRepositoryProvider.notifier)
|
||||
.closeTab(tabState.id);
|
||||
}
|
||||
|
||||
await moveToBackground();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle double back to close (if enabled)
|
||||
@@ -836,9 +854,8 @@ class _Browser extends HookConsumerWidget {
|
||||
|
||||
return true;
|
||||
} else {
|
||||
//Mark back as unhandled and navigator will pop
|
||||
await SystemNavigator.pop();
|
||||
return false;
|
||||
await moveToBackground();
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
lastBackButtonPress.value = DateTime.now();
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2024-2026 Fabian Freund.
|
||||
*
|
||||
* This file is part of WebLibre
|
||||
* (see https://weblibre.eu).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
const _channel = MethodChannel('eu.weblibre.gecko/activity');
|
||||
|
||||
/// Moves the app to the background without finishing the activity.
|
||||
/// Unlike [SystemNavigator.pop], this keeps the Flutter engine attached,
|
||||
/// avoiding crashes when the user returns to the app.
|
||||
Future<void> moveToBackground() async {
|
||||
await _channel.invokeMethod('moveTaskToBack');
|
||||
}
|
||||
Reference in New Issue
Block a user