Skip to content

Customizable Time Limits for Alarm Challenges #734

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
128 changes: 68 additions & 60 deletions lib/app/data/models/alarm_model.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import 'dart:convert';

import 'package:cloud_firestore/cloud_firestore.dart' as firestore;

import 'package:isar/isar.dart';
import 'package:ultimate_alarm_clock/app/data/models/user_model.dart';
import 'package:ultimate_alarm_clock/app/utils/utils.dart';
Expand Down Expand Up @@ -59,57 +58,63 @@ class AlarmModel {
late int guardianTimer;
late String guardian;
late bool isCall;

// New optional field for a custom challenge time limit in seconds.
int? challengeTimeLimit;

@ignore
Map? offsetDetails;

AlarmModel(
{required this.alarmTime,
required this.alarmID,
this.sharedUserIds = const [],
required this.ownerId,
required this.ownerName,
required this.lastEditedUserId,
required this.mutexLock,
this.isEnabled = true,
required this.days,
required this.intervalToAlarm,
required this.isActivityEnabled,
required this.minutesSinceMidnight,
required this.isLocationEnabled,
required this.isSharedAlarmEnabled,
required this.isWeatherEnabled,
required this.location,
required this.weatherTypes,
required this.isMathsEnabled,
required this.mathsDifficulty,
required this.numMathsQuestions,
required this.isShakeEnabled,
required this.shakeTimes,
required this.isQrEnabled,
required this.qrValue,
required this.isPedometerEnabled,
required this.numberOfSteps,
required this.activityInterval,
this.offsetDetails = const {},
required this.mainAlarmTime,
required this.label,
required this.isOneTime,
required this.snoozeDuration,
required this.gradient,
required this.ringtoneName,
required this.note,
required this.deleteAfterGoesOff,
required this.showMotivationalQuote,
required this.volMax,
required this.volMin,
required this.activityMonitor,
required this.ringOn,
required this.alarmDate,
required this.profile,
required this.isGuardian,
required this.guardianTimer,
required this.guardian,
required this.isCall});
AlarmModel({
required this.alarmTime,
required this.alarmID,
this.sharedUserIds = const [],
required this.ownerId,
required this.ownerName,
required this.lastEditedUserId,
required this.mutexLock,
this.isEnabled = true,
required this.days,
required this.intervalToAlarm,
required this.isActivityEnabled,
required this.minutesSinceMidnight,
required this.isLocationEnabled,
required this.isSharedAlarmEnabled,
required this.isWeatherEnabled,
required this.location,
required this.weatherTypes,
required this.isMathsEnabled,
required this.mathsDifficulty,
required this.numMathsQuestions,
required this.isShakeEnabled,
required this.shakeTimes,
required this.isQrEnabled,
required this.qrValue,
required this.isPedometerEnabled,
required this.numberOfSteps,
required this.activityInterval,
this.offsetDetails = const {},
required this.mainAlarmTime,
required this.label,
required this.isOneTime,
required this.snoozeDuration,
required this.gradient,
required this.ringtoneName,
required this.note,
required this.deleteAfterGoesOff,
required this.showMotivationalQuote,
required this.volMax,
required this.volMin,
required this.activityMonitor,
required this.ringOn,
required this.alarmDate,
required this.profile,
required this.isGuardian,
required this.guardianTimer,
required this.guardian,
required this.isCall,
this.challengeTimeLimit,
});

AlarmModel.fromDocumentSnapshot({
required firestore.DocumentSnapshot documentSnapshot,
Expand All @@ -122,7 +127,6 @@ class AlarmModel {
if (isSharedAlarmEnabled && user != null) {
mainAlarmTime = documentSnapshot['alarmTime'];
// Using offsetted time only if it is enabled

alarmTime = (offsetDetails![user.id]['offsetDuration'] != 0)
? offsetDetails![user.id]['offsettedTime']
: documentSnapshot['alarmTime'];
Expand Down Expand Up @@ -179,6 +183,11 @@ class AlarmModel {
guardianTimer = documentSnapshot['guardianTimer'];
guardian = documentSnapshot['guardian'];
isCall = documentSnapshot['isCall'];

// Read the optional challengeTimeLimit if available.
challengeTimeLimit = documentSnapshot.data().toString().contains('challengeTimeLimit')
? documentSnapshot['challengeTimeLimit']
: null;
}

AlarmModel fromMapSQFlite(Map<String, dynamic> map) {
Expand Down Expand Up @@ -231,6 +240,7 @@ class AlarmModel {
guardian: map['guardian'],
isCall: map['isCall'] == 1,
ringOn: map['ringOn'] == 1,
challengeTimeLimit: map['challengeTimeLimit'],
);
}

Expand Down Expand Up @@ -283,6 +293,7 @@ class AlarmModel {
'guardianTimer': guardianTimer,
'guardian': guardian,
'isCall': isCall ? 1 : 0,
'challengeTimeLimit': challengeTimeLimit,
};
}

Expand Down Expand Up @@ -338,14 +349,13 @@ class AlarmModel {
guardian = alarmData['guardian'];
isCall = alarmData['isCall'];
ringOn = alarmData['ringOn'];
}

AlarmModel.fromJson(String alarmData, UserModel? user) {
AlarmModel.fromMap(jsonDecode(alarmData));
// Read challengeTimeLimit if available.
challengeTimeLimit = alarmData['challengeTimeLimit'];
}

static String toJson(AlarmModel alarmRecord) {
return jsonEncode(AlarmModel.toMap(alarmRecord));
String toJson() {
return jsonEncode(AlarmModel.toMap(this));
}

static Map<String, dynamic> toMap(AlarmModel alarmRecord) {
Expand Down Expand Up @@ -390,12 +400,14 @@ class AlarmModel {
'volMax': alarmRecord.volMax,
'activityMonitor': alarmRecord.activityMonitor,
'alarmDate': alarmRecord.alarmDate,
'ringOn': alarmRecord.ringOn,
'profile': alarmRecord.profile,
'isGuardian': alarmRecord.isGuardian,
'guardianTimer': alarmRecord.guardianTimer,
'guardian': alarmRecord.guardian,
'isCall': alarmRecord.isCall,
'ringOn': alarmRecord.ringOn
'ringOn': alarmRecord.ringOn,
'challengeTimeLimit': alarmRecord.challengeTimeLimit,
};

if (alarmRecord.isSharedAlarmEnabled) {
Expand All @@ -406,17 +418,13 @@ class AlarmModel {
}

String boolListToString(List<bool> boolList) {
// Rotate the list to start with Sunday
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bring back these comments, helps us keep track of the logic.

var rotatedList =
[boolList.last] + boolList.sublist(0, boolList.length - 1);
// Convert the list of bools to a string of 1s and 0s
return rotatedList.map((b) => b ? '1' : '0').join();
}

List<bool> stringToBoolList(String s) {
// Rotate the string to start with Monday
final rotatedString = s.substring(1) + s[0];
// Convert the rotated string to a list of boolean values
return rotatedString.split('').map((c) => c == '1').toList();
}
}
13 changes: 12 additions & 1 deletion lib/app/data/providers/secure_storage_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ class SecureStorageProvider {
);
}

//check 24 hrs enabled
// Check 24 hrs enabled
Future<bool> read24HoursEnabled({required String key}) async {
return await _secureStorage.read(key: key) == 'true';
}
Expand Down Expand Up @@ -212,4 +212,15 @@ class SecureStorageProvider {
value: timerId.toString(),
);
}


Future<void> writeChallengeTimeLimit(int timeLimit) async {
await _secureStorage.write(key: 'challengeTimeLimit', value: timeLimit.toString());
}


Future<int> readChallengeTimeLimit() async {
String? timeLimit = await _secureStorage.read(key: 'challengeTimeLimit');
return timeLimit != null ? int.parse(timeLimit) : 30;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import 'package:ultimate_alarm_clock/app/data/models/alarm_model.dart';
import 'package:ultimate_alarm_clock/app/utils/audio_utils.dart';
import 'package:ultimate_alarm_clock/app/utils/constants.dart';
import 'package:ultimate_alarm_clock/app/utils/utils.dart';
import 'package:ultimate_alarm_clock/app/data/providers/secure_storage_provider.dart'; // Import secure storage provider

class AlarmChallengeController extends GetxController {
AlarmModel alarmRecord = Get.arguments;
Expand Down Expand Up @@ -42,6 +43,7 @@ class AlarmChallengeController extends GetxController {
bool shouldProcessStepCount = false;

late Stream<StepCount> _stepCountStream;
Timer? _timer;

void onButtonPressed(String buttonText) {
displayValue.value += buttonText;
Expand Down Expand Up @@ -89,7 +91,7 @@ class AlarmChallengeController extends GetxController {
@override
void onInit() async {
super.onInit();
_startTimer();
await _startTimer();

String ringtoneName = alarmRecord.ringtoneName;

Expand Down Expand Up @@ -199,29 +201,23 @@ class AlarmChallengeController extends GetxController {
}
}

void _startTimer() async {
const duration = Duration(seconds: 15);
const totalIterations = 1500000;
const decrement = 0.000001;

for (var i = totalIterations; i > 0; i--) {
if (!isTimerEnabled) {
debugPrint('THIS IS THE BUG');
break;
}
if (progress.value <= 0.0) {
Future<void> _startTimer() async {
int challengeTimeLimit = await SecureStorageProvider().readChallengeTimeLimit();
progress.value = 1.0;
_timer = Timer.periodic(Duration(milliseconds: 100), (timer) {
if (progress.value > 0) {
progress.value -= 1 / (challengeTimeLimit * 10);
} else {
timer.cancel();
shouldProcessStepCount = false;
Get.until((route) => route.settings.name == '/alarm-ring');
break;
}
await Future.delayed(duration ~/ i);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was done like this to keep the timer decreasing smoothly.

progress.value -= decrement;
}
});
}

restartTimer() {
progress.value = 1.0; // Reset the progress to its initial value
_startTimer(); // Start a new timer
_timer?.cancel();
_startTimer();
}

isChallengesComplete() {
Expand All @@ -235,7 +231,7 @@ class AlarmChallengeController extends GetxController {
@override
void onClose() async {
super.onClose();

_timer?.cancel();
shouldProcessStepCount = false;

String ringtoneName = alarmRecord.ringtoneName;
Expand All @@ -255,4 +251,3 @@ class AlarmChallengeController extends GetxController {
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ class AlarmChallengeView extends GetView<AlarmChallengeController> {

@override
Widget build(BuildContext context) {
// var width = Get.width;
// var height = Get.height;
final double width = MediaQuery.of(context).size.width;
final double height = MediaQuery.of(context).size.height;

Expand Down Expand Up @@ -157,7 +155,7 @@ class AlarmChallengeView extends GetView<AlarmChallengeController> {
),
Obx(
() => Icon(
controller.isQrOngoing.value ==
controller.isMathsOngoing.value ==
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why have you changed the flag?

Status.completed
? Icons.done
: Icons.arrow_forward_ios_sharp,
Expand Down
Loading