Skip to content

Commit

Permalink
swipe to refresh
Browse files Browse the repository at this point in the history
  • Loading branch information
Codel1417 committed Apr 6, 2024
1 parent 16ac0e3 commit 926be52
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 28 deletions.
110 changes: 89 additions & 21 deletions lib/Frontend/Widgets/known_gear_scan_controller.dart
Original file line number Diff line number Diff line change
@@ -1,44 +1,112 @@
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_reactive_ble/flutter_reactive_ble.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:hive_flutter/adapters.dart';
import 'package:logging/logging.dart' as log;
import 'package:multi_value_listenable_builder/multi_value_listenable_builder.dart';
import 'package:sentry_hive/sentry_hive.dart';
import 'package:tail_app/Backend/Definitions/Device/device_definition.dart';
import 'package:tail_app/constants.dart';

import '../../Backend/Bluetooth/bluetooth_manager.dart';

final knownGearScanControllerLogger = log.Logger('KnownGearScanController');

class KnownGearScanController extends ConsumerWidget {
class KnownGearScanController extends ConsumerStatefulWidget {
const KnownGearScanController({super.key, required this.child});

final Widget child;

@override
Widget build(BuildContext context, WidgetRef ref) {
ConsumerState<KnownGearScanController> createState() => _KnownGearScanControllerState();
}

class _KnownGearScanControllerState extends ConsumerState<KnownGearScanController> {
bool shouldScan = false;
final Duration scanDurationTimeout = const Duration(seconds: 30);
Timer? scanTimeout;

@override
Widget build(BuildContext context) {
Map<String, BaseStatefulDevice> knownDevices = ref.watch(knownDevicesProvider);
if (knownDevices.isNotEmpty) {
return MultiValueListenableBuilder(
valueListenables: knownDevices.values.map((e) => e.deviceConnectionState).toList(),
builder: (BuildContext context, List<dynamic> values, Widget? child) {
// Check if all known devices are connected, stop passive scanning if true
knownGearScanControllerLogger.info("Device connectivity state updated");
if (!values.every((element) => element == DeviceConnectionState.connected)) {
// Verify scanning can start
knownGearScanControllerLogger.info("Not all gear connected");
if (ref.watch(btStatusProvider).valueOrNull == BleStatus.ready) {
//when running, automatically reconnects to devices
knownGearScanControllerLogger.info("Scanning for gear");
ref.watch(scanForDevicesProvider);
}
} else {
knownGearScanControllerLogger.info("All devices connected");
}
return child!;
return ValueListenableBuilder(
builder: (BuildContext context, alwaysScan, Widget? child) {
return MultiValueListenableBuilder(
valueListenables: knownDevices.values.map((e) => e.deviceConnectionState).toList(),
builder: (BuildContext context, List<dynamic> values, Widget? child) {
// Check if all known devices are connected, stop passive scanning if true
knownGearScanControllerLogger.info("Device connectivity state updated");
if (!values.every((element) => element == DeviceConnectionState.connected) && (SentryHive.box(settings).get(alwaysScanning, defaultValue: alwaysScanningDefault) || shouldScan)) {
// Verify scanning can start
knownGearScanControllerLogger.info("Not all gear connected");
if (ref.watch(btStatusProvider).valueOrNull == BleStatus.ready) {
//when running, automatically reconnects to devices
knownGearScanControllerLogger.info("Scanning for gear");
ref.watch(scanForDevicesProvider);
}
} else {
knownGearScanControllerLogger.info("All devices connected");
if (shouldScan) {
setState(
() {
shouldScan = false;
},
);
}
}
return Stack(
children: [
AnimatedCrossFade(firstChild: Container(), secondChild: const LinearProgressIndicator(), crossFadeState: shouldScan ? CrossFadeState.showSecond : CrossFadeState.showFirst, duration: animationTransitionDuration),
NotificationListener<OverscrollNotification>(
onNotification: (OverscrollNotification notification) {
knownGearScanControllerLogger.info('Overscroll ${notification.overscroll}');
if (notification.overscroll < 2 && notification.overscroll > -2) {
// ignore, don't do anything
return false;
}
startScanTimer();
return true;
},
child: child!),
],
);
},
child: widget.child,
);
},
valueListenable: SentryHive.box(settings).listenable(keys: [alwaysScanning]),
);
}
return widget.child;
}

void startScanTimer() {
if (scanTimeout != null && scanTimeout!.isActive) {
return;
}
knownGearScanControllerLogger.info('Starting scan timer');
scanTimeout = Timer(
scanDurationTimeout,
() {
knownGearScanControllerLogger.info('Scan timer finished');
if (mounted) {
setState(
() {
shouldScan = false;
},
);
}
},
);
if (mounted) {
setState(
() {
shouldScan = true;
},
child: child,
);
}
return child;
}
}
4 changes: 4 additions & 0 deletions lib/Frontend/intn_defs.dart
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ String settingsHapticsToggleTitle() => Intl.message('Haptic Feedback', name: 'se

String settingsHapticsToggleSubTitle() => Intl.message('Enable vibration when an action is tapped', name: 'settingsHapticsToggleSubTitle', desc: 'Settings page haptic feedback toggle subtitle');

String settingsAlwaysScanningToggleTitle() => Intl.message('Always scan for known gear', name: 'settingsAlwaysScanningToggleTitle', desc: 'Settings page always scanning toggle title');

String settingsAlwaysScanningToggleSubTitle() => Intl.message('Continuously scan for known gear. When disabled, you must swipe on the gear bar to scan.', name: 'settingsHapticsToggleSubTitle', desc: 'Settings page always scanning toggle subtitle');

String settingsKeepScreenOnToggleTitle() => Intl.message('Keep Screen On', name: 'settingsKeepScreenOnToggleTitle', desc: 'Settings page Keep Awake toggle title');

String settingsKeepScreenOnToggleSubTitle() => Intl.message('This mode stops the screen from switching off whilst your gear is connected', name: 'settingsKeepScreenOnToggleSubTitle', desc: 'Settings page Keep Awake toggle subtitle');
Expand Down
32 changes: 26 additions & 6 deletions lib/Frontend/pages/settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,10 @@ class _SettingsState extends ConsumerState<Settings> {
return AlertDialog(
title: Text(
settingsAppColor(),
style: Theme.of(context).textTheme.titleLarge,
style: Theme
.of(context)
.textTheme
.titleLarge,
),
actions: [
TextButton(
Expand Down Expand Up @@ -107,7 +110,7 @@ class _SettingsState extends ConsumerState<Settings> {
value: SentryHive.box(settings).get(showAccurateBattery, defaultValue: showAccurateBatteryDefault),
onChanged: (bool value) {
setState(
() {
() {
SentryHive.box(settings).put(showAccurateBattery, value);
},
);
Expand All @@ -122,7 +125,7 @@ class _SettingsState extends ConsumerState<Settings> {
value: SentryHive.box(settings).get(largerActionCardSize, defaultValue: largerActionCardSizeDefault),
onChanged: (bool value) {
setState(
() {
() {
SentryHive.box(settings).put(largerActionCardSize, value);
},
);
Expand All @@ -137,7 +140,7 @@ class _SettingsState extends ConsumerState<Settings> {
value: SentryHive.box(settings).get(hideTutorialCards, defaultValue: hideTutorialCardsDefault),
onChanged: (bool value) {
setState(
() {
() {
SentryHive.box(settings).put(hideTutorialCards, value);
},
);
Expand All @@ -147,6 +150,19 @@ class _SettingsState extends ConsumerState<Settings> {
const ListTile(
title: Divider(),
),
ListTile(
title: Text(settingsAlwaysScanningToggleTitle()),
leading: const Icon(Icons.bluetooth_searching),
subtitle: Text(settingsAlwaysScanningToggleSubTitle()),
trailing: Switch(
value: SentryHive.box(settings).get(alwaysScanning, defaultValue: alwaysScanningDefault),
onChanged: (bool value) {
setState(() {
SentryHive.box(settings).put(alwaysScanning, value);
});
},
),
),
ListTile(
title: Text(settingsHapticsToggleTitle()),
leading: const Icon(Icons.vibration),
Expand All @@ -169,7 +185,11 @@ class _SettingsState extends ConsumerState<Settings> {
onChanged: (bool value) {
setState(() {
SentryHive.box(settings).put(keepAwake, value);
if (ref.read(knownDevicesProvider).values.where((element) => element.deviceConnectionState.value == DeviceConnectionState.connected).isNotEmpty) {
if (ref
.read(knownDevicesProvider)
.values
.where((element) => element.deviceConnectionState.value == DeviceConnectionState.connected)
.isNotEmpty) {
if (value) {
WakelockPlus.enable();
} else {
Expand All @@ -188,7 +208,7 @@ class _SettingsState extends ConsumerState<Settings> {
value: SentryHive.box(settings).get(kitsuneModeToggle, defaultValue: kitsuneModeDefault),
onChanged: (bool value) {
setState(
() {
() {
SentryHive.box(settings).put(kitsuneModeToggle, value);
},
);
Expand Down
1 change: 1 addition & 0 deletions lib/Frontend/pages/shell.dart
Original file line number Diff line number Diff line change
Expand Up @@ -648,6 +648,7 @@ class _DeviceStatusWidgetState extends ConsumerState<DeviceStatusWidget> {
child: Padding(
padding: const EdgeInsets.all(8.0),
child: SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
scrollDirection: Axis.horizontal,
child: Row(
children: ref
Expand Down
3 changes: 2 additions & 1 deletion lib/constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const String largerActionCardSize = 'largerActionCardSize';
const String hideTutorialCards = 'hideTutorialCards';
const String hasCompletedOnboarding = 'hasCompletedOnboarding';
const String showDebugging = 'showDebugging';
const String alwaysScanning = 'alwaysScanning';

// Settings Default value
const bool kitsuneModeDefault = false;
Expand All @@ -37,7 +38,7 @@ const bool largerActionCardSizeDefault = false;
const bool hideTutorialCardsDefault = false;
const bool hasCompletedOnboardingDefault = false;
const bool showDebuggingDefault = false;

const bool alwaysScanningDefault = true;
// Triggers labels
const String triggerBox = 'triggers';

Expand Down

0 comments on commit 926be52

Please sign in to comment.