Skip to content
Open

Demo #10

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions physicalapp/lib/login.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class _LoginPageState extends State<LoginPage> {
final prefs = await SharedPreferences.getInstance();
await prefs.setInt('user_id', userId);
await prefs.setString('username', email);
await prefs.setInt('demo', 1);

Navigator.pushReplacement(
context,
Expand Down
43 changes: 24 additions & 19 deletions physicalapp/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -68,25 +68,30 @@ class _MainPageState extends State<MainPage> {
Future<void> getGoal() async {
final prefs = await SharedPreferences.getInstance();
final userId = prefs.getInt('user_id');
final response = await http.get(
Uri.parse('${dotenv.env['BASE_URL']}/goal/${userId}'),
);

if (response.statusCode == 200) {
final data = jsonDecode(response.body);

// check questionare
if (data['goal_dist'] < 0) {
WidgetsBinding.instance.addPostFrameCallback((_) {
Navigator.pushReplacementNamed(context, '/instruction');
});
}
else{
setState(() {
curr_goal_dist = data['goal_dist'];
curr_goal_pace = data['goal_pace'];
});
}
final demo = prefs.getInt('demo');

if(demo == 1){
print("First Demo");
setState(() {
curr_goal_dist = 5.0;
curr_goal_pace = 390;
});
await prefs.setInt('demo', 2);
}
else if(demo == 2){
print("Second Demo");
setState(() {
curr_goal_dist = 6.0;
curr_goal_pace = 390;
});
await prefs.setInt('demo', 3);
}
else{
print("Third Demo");
setState(() {
curr_goal_dist = 5.5;
curr_goal_pace = 420;
});
}
}

Expand Down
101 changes: 52 additions & 49 deletions physicalapp/lib/pages/run.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import 'package:shared_preferences/shared_preferences.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:audioplayers/audioplayers.dart';
import 'package:lottie/lottie.dart';
import 'dart:math';

class RunPage extends StatefulWidget {
final double goalDistance;
Expand All @@ -33,9 +34,17 @@ class _RunPageState extends State<RunPage> with SingleTickerProviderStateMixin {
final List<Duration> _splits = [];
Duration _lastSplitElapsed = Duration.zero;
final List<double> _recentPaces = [];
final int _paceCheckPeriod = 5;
final int _paceCheckPeriod = 10;
final AudioPlayer _audioPlayer = AudioPlayer();
double _progress = 0.0;
final random = Random(1);

double ACCELERATION_RATE = 50.0; // (5x)
double rate = 300; // 5'00"

bool already_slower = false;
bool already_faster = false;
bool first_demo = false;

@override
void initState() {
Expand All @@ -58,73 +67,64 @@ class _RunPageState extends State<RunPage> with SingleTickerProviderStateMixin {
}
}

// first demo or second demo
if(widget.goalDistance == 5){
first_demo = true;
print('First Demo');
}

_activeStartTime = DateTime.now();
_isPaused = false;

_timer = Timer.periodic(const Duration(seconds: 1), (_) {
_timer = Timer.periodic(const Duration(milliseconds: 200), (_) {
if (!_isPaused && _activeStartTime != null) {
setState(() {});
}
});
_pace = rate + random.nextDouble() * 60;

_positionStream = Geolocator.getPositionStream(
locationSettings: const LocationSettings(
accuracy: LocationAccuracy.best,
distanceFilter: 5,
),
).listen((Position position) {
if (!_isPaused) {
if (_lastPosition != null) {
final distance = Geolocator.distanceBetween(
_lastPosition!.latitude,
_lastPosition!.longitude,
position.latitude,
position.longitude,
);

_totalDistance += distance;
_distanceSinceLastSplit += distance;

if (_distanceSinceLastSplit >= 1000.0) {
final currentElapsed = _activeDuration +
(_activeStartTime == null ? Duration.zero : DateTime.now().difference(_activeStartTime!));

final splitDuration = currentElapsed - _lastSplitElapsed;
_splits.add(splitDuration);

_lastSplitElapsed = currentElapsed;
_distanceSinceLastSplit = 0.0;
}
final distance = (1000 / _pace) * ACCELERATION_RATE;

_totalDistance += distance;
_distanceSinceLastSplit += distance;

if (_distanceSinceLastSplit >= 1000.0) {
final currentElapsed = _activeDuration +
(_activeStartTime == null ? Duration.zero : DateTime.now().difference(_activeStartTime!)) * 5 * ACCELERATION_RATE;
final splitDuration = currentElapsed - _lastSplitElapsed;
_splits.add(splitDuration);

_lastSplitElapsed = currentElapsed;
_distanceSinceLastSplit = 0.0;
}
_lastPosition = position;
_pace = position.speed == 0 ? -1 : (1000 / position.speed);

if(_pace > 0){
if(_pace > 0 && first_demo){
_recentPaces.add(_pace);

if (_recentPaces.length > _paceCheckPeriod) {
_recentPaces.removeAt(0);

if(average(_recentPaces) > (widget.goalPace + 15)){
if(average(_recentPaces) > (widget.goalPace + 15) && !already_faster){
print('Alert: Too slow!');
_audioPlayer.play(AssetSource('audio/faster.mp3'));
_recentPaces.clear();
already_faster = true;
rate = 360; // 6'00"
}
else if(average(_recentPaces) < (widget.goalPace - 15)){
else if(average(_recentPaces) < (widget.goalPace - 15) && !already_slower){
print('Alert: Too fast!');
_audioPlayer.play(AssetSource('audio/slower.mp3'));
_recentPaces.clear();
already_slower = true;
rate = 420; // 7'00"
}
}
}

setState(() {
_progress = (_totalDistance / 1000 / widget.goalDistance).clamp(0.0, 1.0);
});
}
});
}


void _pauseTracking() {
setState(() {
if (!_isPaused && _activeStartTime != null) {
Expand All @@ -134,7 +134,7 @@ class _RunPageState extends State<RunPage> with SingleTickerProviderStateMixin {
_timer?.cancel();
} else {
_activeStartTime = DateTime.now();
_timer = Timer.periodic(const Duration(seconds: 1), (_) {
_timer = Timer.periodic(const Duration(milliseconds: 100), (_) {
setState(() {});
});
}
Expand Down Expand Up @@ -184,11 +184,11 @@ class _RunPageState extends State<RunPage> with SingleTickerProviderStateMixin {
final runData = {
'user_id': userId,
'start_time': today.toIso8601String(),
'duration_seconds': _activeDuration.inSeconds,
'duration_seconds': totalRunTime.inSeconds,
'distance_km': _totalDistance / 1000,
'end_latitude': 0.0,
'end_longitude': 0.0,
'average_pace_seconds_per_km': _totalDistance > 0 ? (_activeDuration.inSeconds / (_totalDistance / 1000)).round() : 0,
'average_pace_seconds_per_km': _totalDistance > 0 ? ((_activeDuration.inSeconds * 5 * ACCELERATION_RATE) / (_totalDistance / 1000) + 40).round() : 0,
'split_paces': _splits.map((d) => d.inSeconds).toList(),
'goal_state': check_goal(),
'goal_dist': widget.goalDistance,
Expand Down Expand Up @@ -219,10 +219,13 @@ class _RunPageState extends State<RunPage> with SingleTickerProviderStateMixin {
super.dispose();
}

Duration get totalRunTime {
return (_activeDuration +
(_activeStartTime == null ? Duration.zero : DateTime.now().difference(_activeStartTime!))) * ACCELERATION_RATE * 5;
}

@override
Widget build(BuildContext context) {
final totalRunTime = _activeDuration +
(_activeStartTime == null ? Duration.zero : DateTime.now().difference(_activeStartTime!));

return Scaffold(
appBar: AppBar(title: const Text('Running'),
Expand Down Expand Up @@ -336,9 +339,9 @@ class _RunPageState extends State<RunPage> with SingleTickerProviderStateMixin {
style: ElevatedButton.styleFrom(shape: const CircleBorder(), padding: const EdgeInsets.all(20)),
child: const Icon(Icons.pause, size: 32),
),
],
],
),
),
),
);
);
}
}
}