From dd92351db6f14ec8925d7aa7947ddd0ce3727d1e Mon Sep 17 00:00:00 2001 From: AryanSarafDev Date: Fri, 17 May 2024 15:05:33 +0530 Subject: [PATCH 1/5] timer progress --- lib/app/data/models/timer_model.dart | 45 +- lib/app/data/models/timer_model.g.dart | 1374 +++++++++++++++++ lib/app/data/providers/isar_provider.dart | 185 ++- .../timer/controllers/timer_controller.dart | 79 +- .../modules/timer/views/timer_animation.dart | 263 ++++ lib/app/modules/timer/views/timer_view.dart | 1176 +++++++------- .../controllers/timer_ring_controller.dart | 11 +- lib/app/utils/utils.dart | 25 +- pubspec.lock | 67 +- 9 files changed, 2586 insertions(+), 639 deletions(-) create mode 100644 lib/app/data/models/timer_model.g.dart create mode 100644 lib/app/modules/timer/views/timer_animation.dart diff --git a/lib/app/data/models/timer_model.dart b/lib/app/data/models/timer_model.dart index 6b60c0c7..63b765b5 100644 --- a/lib/app/data/models/timer_model.dart +++ b/lib/app/data/models/timer_model.dart @@ -1,16 +1,47 @@ import 'package:isar/isar.dart'; +part 'timer_model.g.dart'; +@collection class TimerModel { - Id isarId = Isar.autoIncrement; + Id timerId = Isar.autoIncrement; late String timerTime; late String mainTimerTime; + late String timeElapsed ; late int intervalToAlarm; late String ringtoneName; + late String timerName; + late int isPaused ; - TimerModel({ - required this.intervalToAlarm, - required this.mainTimerTime, - required this.ringtoneName, - required this.timerTime, - }); + TimerModel( + { + required this.intervalToAlarm, + required this.mainTimerTime, + required this.ringtoneName, + required this.timerTime, + required this.timerName, + this.isPaused = 0, + this.timeElapsed = '00:00:00'}); + + Map toMap() { + return { + 'id': timerId, + 'timerTime': timerTime, + 'mainTimerTime': mainTimerTime, + 'intervalToAlarm': intervalToAlarm, + 'ringtoneName': ringtoneName, + 'timerName': timerName, + 'isPaused': isPaused, + }; + } + + // Extract a TimerModel object from a Map object. + TimerModel.fromMap(Map map) { + timerId = map['id']; + timerTime = map['timerTime']; + mainTimerTime = map['mainTimerTime']; + intervalToAlarm = map['intervalToAlarm']; + ringtoneName = map['ringtoneName']; + timerName = map['timerName']; + isPaused = map['isPaused']; + } } diff --git a/lib/app/data/models/timer_model.g.dart b/lib/app/data/models/timer_model.g.dart new file mode 100644 index 00000000..9bb98bcd --- /dev/null +++ b/lib/app/data/models/timer_model.g.dart @@ -0,0 +1,1374 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'timer_model.dart'; + +// ************************************************************************** +// IsarCollectionGenerator +// ************************************************************************** + +// coverage:ignore-file +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types + +extension GetTimerModelCollection on Isar { + IsarCollection get timerModels => this.collection(); +} + +const TimerModelSchema = CollectionSchema( + name: r'TimerModel', + id: 1326376837060457485, + properties: { + r'intervalToAlarm': PropertySchema( + id: 0, + name: r'intervalToAlarm', + type: IsarType.long, + ), + r'isPaused': PropertySchema( + id: 1, + name: r'isPaused', + type: IsarType.long, + ), + r'mainTimerTime': PropertySchema( + id: 2, + name: r'mainTimerTime', + type: IsarType.string, + ), + r'ringtoneName': PropertySchema( + id: 3, + name: r'ringtoneName', + type: IsarType.string, + ), + r'timeElapsed': PropertySchema( + id: 4, + name: r'timeElapsed', + type: IsarType.string, + ), + r'timerName': PropertySchema( + id: 5, + name: r'timerName', + type: IsarType.string, + ), + r'timerTime': PropertySchema( + id: 6, + name: r'timerTime', + type: IsarType.string, + ) + }, + estimateSize: _timerModelEstimateSize, + serialize: _timerModelSerialize, + deserialize: _timerModelDeserialize, + deserializeProp: _timerModelDeserializeProp, + idName: r'timerId', + indexes: {}, + links: {}, + embeddedSchemas: {}, + getId: _timerModelGetId, + getLinks: _timerModelGetLinks, + attach: _timerModelAttach, + version: '3.1.0+1', +); + +int _timerModelEstimateSize( + TimerModel object, + List offsets, + Map> allOffsets, +) { + var bytesCount = offsets.last; + bytesCount += 3 + object.mainTimerTime.length * 3; + bytesCount += 3 + object.ringtoneName.length * 3; + bytesCount += 3 + object.timeElapsed.length * 3; + bytesCount += 3 + object.timerName.length * 3; + bytesCount += 3 + object.timerTime.length * 3; + return bytesCount; +} + +void _timerModelSerialize( + TimerModel object, + IsarWriter writer, + List offsets, + Map> allOffsets, +) { + writer.writeLong(offsets[0], object.intervalToAlarm); + writer.writeLong(offsets[1], object.isPaused); + writer.writeString(offsets[2], object.mainTimerTime); + writer.writeString(offsets[3], object.ringtoneName); + writer.writeString(offsets[4], object.timeElapsed); + writer.writeString(offsets[5], object.timerName); + writer.writeString(offsets[6], object.timerTime); +} + +TimerModel _timerModelDeserialize( + Id id, + IsarReader reader, + List offsets, + Map> allOffsets, +) { + final object = TimerModel( + intervalToAlarm: reader.readLong(offsets[0]), + isPaused: reader.readLongOrNull(offsets[1]) ?? 0, + mainTimerTime: reader.readString(offsets[2]), + ringtoneName: reader.readString(offsets[3]), + timeElapsed: reader.readStringOrNull(offsets[4]) ?? '00:00:00', + timerName: reader.readString(offsets[5]), + timerTime: reader.readString(offsets[6]), + ); + object.timerId = id; + return object; +} + +P _timerModelDeserializeProp

( + IsarReader reader, + int propertyId, + int offset, + Map> allOffsets, +) { + switch (propertyId) { + case 0: + return (reader.readLong(offset)) as P; + case 1: + return (reader.readLongOrNull(offset) ?? 0) as P; + case 2: + return (reader.readString(offset)) as P; + case 3: + return (reader.readString(offset)) as P; + case 4: + return (reader.readStringOrNull(offset) ?? '00:00:00') as P; + case 5: + return (reader.readString(offset)) as P; + case 6: + return (reader.readString(offset)) as P; + default: + throw IsarError('Unknown property with id $propertyId'); + } +} + +Id _timerModelGetId(TimerModel object) { + return object.timerId; +} + +List> _timerModelGetLinks(TimerModel object) { + return []; +} + +void _timerModelAttach(IsarCollection col, Id id, TimerModel object) { + object.timerId = id; +} + +extension TimerModelQueryWhereSort + on QueryBuilder { + QueryBuilder anyTimerId() { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(const IdWhereClause.any()); + }); + } +} + +extension TimerModelQueryWhere + on QueryBuilder { + QueryBuilder timerIdEqualTo( + Id timerId) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IdWhereClause.between( + lower: timerId, + upper: timerId, + )); + }); + } + + QueryBuilder timerIdNotEqualTo( + Id timerId) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause( + IdWhereClause.lessThan(upper: timerId, includeUpper: false), + ) + .addWhereClause( + IdWhereClause.greaterThan(lower: timerId, includeLower: false), + ); + } else { + return query + .addWhereClause( + IdWhereClause.greaterThan(lower: timerId, includeLower: false), + ) + .addWhereClause( + IdWhereClause.lessThan(upper: timerId, includeUpper: false), + ); + } + }); + } + + QueryBuilder timerIdGreaterThan( + Id timerId, + {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause( + IdWhereClause.greaterThan(lower: timerId, includeLower: include), + ); + }); + } + + QueryBuilder timerIdLessThan( + Id timerId, + {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause( + IdWhereClause.lessThan(upper: timerId, includeUpper: include), + ); + }); + } + + QueryBuilder timerIdBetween( + Id lowerTimerId, + Id upperTimerId, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IdWhereClause.between( + lower: lowerTimerId, + includeLower: includeLower, + upper: upperTimerId, + includeUpper: includeUpper, + )); + }); + } +} + +extension TimerModelQueryFilter + on QueryBuilder { + QueryBuilder + intervalToAlarmEqualTo(int value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'intervalToAlarm', + value: value, + )); + }); + } + + QueryBuilder + intervalToAlarmGreaterThan( + int value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'intervalToAlarm', + value: value, + )); + }); + } + + QueryBuilder + intervalToAlarmLessThan( + int value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'intervalToAlarm', + value: value, + )); + }); + } + + QueryBuilder + intervalToAlarmBetween( + int lower, + int upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'intervalToAlarm', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder isPausedEqualTo( + int value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'isPaused', + value: value, + )); + }); + } + + QueryBuilder + isPausedGreaterThan( + int value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'isPaused', + value: value, + )); + }); + } + + QueryBuilder isPausedLessThan( + int value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'isPaused', + value: value, + )); + }); + } + + QueryBuilder isPausedBetween( + int lower, + int upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'isPaused', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder + mainTimerTimeEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'mainTimerTime', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + mainTimerTimeGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'mainTimerTime', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + mainTimerTimeLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'mainTimerTime', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + mainTimerTimeBetween( + String lower, + String upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'mainTimerTime', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + mainTimerTimeStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'mainTimerTime', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + mainTimerTimeEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'mainTimerTime', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + mainTimerTimeContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'mainTimerTime', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + mainTimerTimeMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'mainTimerTime', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + mainTimerTimeIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'mainTimerTime', + value: '', + )); + }); + } + + QueryBuilder + mainTimerTimeIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'mainTimerTime', + value: '', + )); + }); + } + + QueryBuilder + ringtoneNameEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'ringtoneName', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + ringtoneNameGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'ringtoneName', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + ringtoneNameLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'ringtoneName', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + ringtoneNameBetween( + String lower, + String upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'ringtoneName', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + ringtoneNameStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'ringtoneName', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + ringtoneNameEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'ringtoneName', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + ringtoneNameContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'ringtoneName', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + ringtoneNameMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'ringtoneName', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + ringtoneNameIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'ringtoneName', + value: '', + )); + }); + } + + QueryBuilder + ringtoneNameIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'ringtoneName', + value: '', + )); + }); + } + + QueryBuilder + timeElapsedEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'timeElapsed', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + timeElapsedGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'timeElapsed', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + timeElapsedLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'timeElapsed', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + timeElapsedBetween( + String lower, + String upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'timeElapsed', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + timeElapsedStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'timeElapsed', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + timeElapsedEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'timeElapsed', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + timeElapsedContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'timeElapsed', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + timeElapsedMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'timeElapsed', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + timeElapsedIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'timeElapsed', + value: '', + )); + }); + } + + QueryBuilder + timeElapsedIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'timeElapsed', + value: '', + )); + }); + } + + QueryBuilder timerIdEqualTo( + Id value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'timerId', + value: value, + )); + }); + } + + QueryBuilder + timerIdGreaterThan( + Id value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'timerId', + value: value, + )); + }); + } + + QueryBuilder timerIdLessThan( + Id value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'timerId', + value: value, + )); + }); + } + + QueryBuilder timerIdBetween( + Id lower, + Id upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'timerId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder timerNameEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'timerName', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + timerNameGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'timerName', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder timerNameLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'timerName', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder timerNameBetween( + String lower, + String upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'timerName', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + timerNameStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'timerName', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder timerNameEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'timerName', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder timerNameContains( + String value, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'timerName', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder timerNameMatches( + String pattern, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'timerName', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + timerNameIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'timerName', + value: '', + )); + }); + } + + QueryBuilder + timerNameIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'timerName', + value: '', + )); + }); + } + + QueryBuilder timerTimeEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'timerTime', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + timerTimeGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'timerTime', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder timerTimeLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'timerTime', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder timerTimeBetween( + String lower, + String upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'timerTime', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + timerTimeStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'timerTime', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder timerTimeEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'timerTime', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder timerTimeContains( + String value, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'timerTime', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder timerTimeMatches( + String pattern, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'timerTime', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + timerTimeIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'timerTime', + value: '', + )); + }); + } + + QueryBuilder + timerTimeIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'timerTime', + value: '', + )); + }); + } +} + +extension TimerModelQueryObject + on QueryBuilder {} + +extension TimerModelQueryLinks + on QueryBuilder {} + +extension TimerModelQuerySortBy + on QueryBuilder { + QueryBuilder sortByIntervalToAlarm() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'intervalToAlarm', Sort.asc); + }); + } + + QueryBuilder + sortByIntervalToAlarmDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'intervalToAlarm', Sort.desc); + }); + } + + QueryBuilder sortByIsPaused() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isPaused', Sort.asc); + }); + } + + QueryBuilder sortByIsPausedDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isPaused', Sort.desc); + }); + } + + QueryBuilder sortByMainTimerTime() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'mainTimerTime', Sort.asc); + }); + } + + QueryBuilder sortByMainTimerTimeDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'mainTimerTime', Sort.desc); + }); + } + + QueryBuilder sortByRingtoneName() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'ringtoneName', Sort.asc); + }); + } + + QueryBuilder sortByRingtoneNameDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'ringtoneName', Sort.desc); + }); + } + + QueryBuilder sortByTimeElapsed() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'timeElapsed', Sort.asc); + }); + } + + QueryBuilder sortByTimeElapsedDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'timeElapsed', Sort.desc); + }); + } + + QueryBuilder sortByTimerName() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'timerName', Sort.asc); + }); + } + + QueryBuilder sortByTimerNameDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'timerName', Sort.desc); + }); + } + + QueryBuilder sortByTimerTime() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'timerTime', Sort.asc); + }); + } + + QueryBuilder sortByTimerTimeDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'timerTime', Sort.desc); + }); + } +} + +extension TimerModelQuerySortThenBy + on QueryBuilder { + QueryBuilder thenByIntervalToAlarm() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'intervalToAlarm', Sort.asc); + }); + } + + QueryBuilder + thenByIntervalToAlarmDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'intervalToAlarm', Sort.desc); + }); + } + + QueryBuilder thenByIsPaused() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isPaused', Sort.asc); + }); + } + + QueryBuilder thenByIsPausedDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isPaused', Sort.desc); + }); + } + + QueryBuilder thenByMainTimerTime() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'mainTimerTime', Sort.asc); + }); + } + + QueryBuilder thenByMainTimerTimeDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'mainTimerTime', Sort.desc); + }); + } + + QueryBuilder thenByRingtoneName() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'ringtoneName', Sort.asc); + }); + } + + QueryBuilder thenByRingtoneNameDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'ringtoneName', Sort.desc); + }); + } + + QueryBuilder thenByTimeElapsed() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'timeElapsed', Sort.asc); + }); + } + + QueryBuilder thenByTimeElapsedDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'timeElapsed', Sort.desc); + }); + } + + QueryBuilder thenByTimerId() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'timerId', Sort.asc); + }); + } + + QueryBuilder thenByTimerIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'timerId', Sort.desc); + }); + } + + QueryBuilder thenByTimerName() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'timerName', Sort.asc); + }); + } + + QueryBuilder thenByTimerNameDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'timerName', Sort.desc); + }); + } + + QueryBuilder thenByTimerTime() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'timerTime', Sort.asc); + }); + } + + QueryBuilder thenByTimerTimeDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'timerTime', Sort.desc); + }); + } +} + +extension TimerModelQueryWhereDistinct + on QueryBuilder { + QueryBuilder distinctByIntervalToAlarm() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'intervalToAlarm'); + }); + } + + QueryBuilder distinctByIsPaused() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'isPaused'); + }); + } + + QueryBuilder distinctByMainTimerTime( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'mainTimerTime', + caseSensitive: caseSensitive); + }); + } + + QueryBuilder distinctByRingtoneName( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'ringtoneName', caseSensitive: caseSensitive); + }); + } + + QueryBuilder distinctByTimeElapsed( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'timeElapsed', caseSensitive: caseSensitive); + }); + } + + QueryBuilder distinctByTimerName( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'timerName', caseSensitive: caseSensitive); + }); + } + + QueryBuilder distinctByTimerTime( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'timerTime', caseSensitive: caseSensitive); + }); + } +} + +extension TimerModelQueryProperty + on QueryBuilder { + QueryBuilder timerIdProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'timerId'); + }); + } + + QueryBuilder intervalToAlarmProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'intervalToAlarm'); + }); + } + + QueryBuilder isPausedProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'isPaused'); + }); + } + + QueryBuilder mainTimerTimeProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'mainTimerTime'); + }); + } + + QueryBuilder ringtoneNameProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'ringtoneName'); + }); + } + + QueryBuilder timeElapsedProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'timeElapsed'); + }); + } + + QueryBuilder timerNameProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'timerName'); + }); + } + + QueryBuilder timerTimeProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'timerTime'); + }); + } +} diff --git a/lib/app/data/providers/isar_provider.dart b/lib/app/data/providers/isar_provider.dart index f741cf38..e71a8fe9 100644 --- a/lib/app/data/providers/isar_provider.dart +++ b/lib/app/data/providers/isar_provider.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:isar/isar.dart'; import 'package:path_provider/path_provider.dart'; @@ -19,16 +21,37 @@ class IsarDb { db = openDB(); } - Future getSQLiteDatabase() async { + Future getAlarmSQLiteDatabase() async { Database? db; final dir = await getDatabasesPath(); - final dbPath = '${dir}/alarms.db'; - print(dir); + final dbPath = '$dir/alarms.db'; db = await openDatabase(dbPath, version: 1, onCreate: _onCreate); return db; } + Future getTimerSQLiteDatabase() async { + Database? db; + final dir = await getDatabasesPath(); + db = await openDatabase( + '$dir/timer.db', + version: 1, + onCreate: (Database db, int version) async { + await db.execute(''' + create table timers ( + id integer primary key autoincrement, + timerTime text not null, + mainTimerTime text not null, + intervalToAlarm integer not null, + ringtoneName text not null, + timerName text not null, + isPaused integer not null) + '''); + }, + ); + return db; + } + void _onCreate(Database db, int version) async { // Create tables for alarms and ringtones (modify column types as needed) await db.execute(''' @@ -89,7 +112,7 @@ class IsarDb { final dir = await getApplicationDocumentsDirectory(); if (Isar.instanceNames.isEmpty) { return await Isar.open( - [AlarmModelSchema, RingtoneModelSchema], + [AlarmModelSchema, RingtoneModelSchema, TimerModelSchema], directory: dir.path, inspector: true, ); @@ -99,15 +122,15 @@ class IsarDb { static Future addAlarm(AlarmModel alarmRecord) async { final isarProvider = IsarDb(); - final sql = await IsarDb().getSQLiteDatabase(); + final sql = await IsarDb().getAlarmSQLiteDatabase(); final db = await isarProvider.db; await db.writeTxn(() async { await db.alarmModels.put(alarmRecord); }); final sqlmap = alarmRecord.toSQFliteMap(); - await sql! - .insert('alarms', sqlmap) - .then((value) => print("insert success")); + await sql!.insert('alarms', sqlmap).then((value) { + sql.close(); + }); return alarmRecord; } @@ -217,7 +240,7 @@ class IsarDb { static Future updateAlarm(AlarmModel alarmRecord) async { final isarProvider = IsarDb(); - final sql = await IsarDb().getSQLiteDatabase(); + final sql = await IsarDb().getAlarmSQLiteDatabase(); final db = await isarProvider.db; await db.writeTxn(() async { await db.alarmModels.put(alarmRecord); @@ -227,7 +250,10 @@ class IsarDb { alarmRecord.toSQFliteMap(), where: 'alarmID = ?', whereArgs: [alarmRecord.alarmID], - ); + ).then((value) { + sql.close(); + return value; + }); } static Future getAlarm(int id) async { @@ -243,21 +269,152 @@ class IsarDb { yield* db.alarmModels.where().watch(fireImmediately: true); } catch (e) { debugPrint(e.toString()); - throw e; + rethrow; } } static Future deleteAlarm(int id) async { final isarProvider = IsarDb(); final db = await isarProvider.db; - final sql = await IsarDb().getSQLiteDatabase(); - final tobedeleted = await db.alarmModels.get(id); + final sql = await IsarDb().getAlarmSQLiteDatabase(); + final tobedeleted = await db.alarmModels.get(id); await db.writeTxn(() async { await db.alarmModels.delete(id); }); - sql!.delete('alarms', where: 'alarmID = ?', whereArgs: [tobedeleted!.alarmID]); + await sql!.delete( + 'alarms', + where: 'alarmID = ?', + whereArgs: [tobedeleted!.alarmID], + ).then((value) { + sql.close(); + return value; + }); + } + + // Timer Functions + + static Future insertTimer(TimerModel timer) async { + final isarProvider = IsarDb(); + final sql = await IsarDb().getTimerSQLiteDatabase(); + final db = await isarProvider.db; + await db.writeTxn(() async { + await db.timerModels.put(timer); + }); + try { + await sql!.insert('timers', timer.toMap()).then((value) { + sql.close(); + }); + } catch (e) { + print(e.toString()); + } + return timer; + } + + static Future updateTimer(TimerModel timer) async { + final sql = await IsarDb().getTimerSQLiteDatabase(); + return await sql!.update( + 'timers', + timer.toMap(), + where: 'id = ?', + whereArgs: [timer.timerId], + ).then((value) { + sql.close(); + return value; + }); + } + + static Future updateTimerName(int id, String newTimerName) async { + final sql = await IsarDb().getTimerSQLiteDatabase(); + return await sql! + .update( + 'timers', + {'timerName': newTimerName}, + where: 'id = ?', + whereArgs: [id], + ) + .then((value) { + sql.close(); + return value; + }); + } + + static Future updateIsPaused(int id, int newIsPaused) async { + final sql = await IsarDb().getTimerSQLiteDatabase(); + + return await sql! + .update( + 'timers', + {'isPaused': newIsPaused}, + where: 'id = ?', + whereArgs: [id], + ) + .then((value) { + sql.close(); + return value; + }); + } + + static Future deleteTimer(int id) async { + final isarProvider = IsarDb(); + final sql = await IsarDb().getTimerSQLiteDatabase(); + final db = await isarProvider.db; + await db.writeTxn(() async { + await db.timerModels.delete(id); + }); + return await sql! + .delete('timers', where: 'id = ?', whereArgs: [id]).then((value) { + print("$value ss"); + sql.close(); + return value; + }); + } + + static Future> getAllTimers() async { + final sql = await IsarDb().getTimerSQLiteDatabase(); + List> maps = await sql!.query('timers', columns: [ + 'id', + 'timerTime', + 'mainTimerTime', + 'intervalToAlarm', + 'ringtoneName', + 'timerName', + 'isPaused' + ]); + if (maps.length > 0) { + return maps.map((timer) => TimerModel.fromMap(timer)).toList(); + } + return []; + } + + static Stream> getTimers() { + final isarProvider = IsarDb(); + final controller = StreamController>.broadcast(); + + isarProvider.db.then((db) { + final stream = db.timerModels.where().watch(fireImmediately: true); + stream.listen( + (data) => controller.add(data), + onError: (error) => controller.addError(error), + onDone: () => controller.close(), + ); + }).catchError((error) { + debugPrint(error.toString()); + controller.addError(error); + }); + + return controller.stream; + } + + static Future getNumberOfTimers() async { + final sql = await IsarDb().getTimerSQLiteDatabase(); + List> x = + await sql!.rawQuery('SELECT COUNT (*) from timers'); + sql.close(); + int result = Sqflite.firstIntValue(x)!; + return result; } +// Ringtone functions static Future addCustomRingtone( RingtoneModel customRingtone, ) async { diff --git a/lib/app/modules/timer/controllers/timer_controller.dart b/lib/app/modules/timer/controllers/timer_controller.dart index c81392c0..b17d4f3c 100644 --- a/lib/app/modules/timer/controllers/timer_controller.dart +++ b/lib/app/modules/timer/controllers/timer_controller.dart @@ -3,6 +3,8 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; +import 'package:rxdart/rxdart.dart' as rx; +import 'package:isar/isar.dart'; import 'package:ultimate_alarm_clock/app/data/models/alarm_model.dart'; import 'package:ultimate_alarm_clock/app/data/models/timer_model.dart'; import 'package:ultimate_alarm_clock/app/data/providers/isar_provider.dart'; @@ -10,7 +12,8 @@ import 'package:ultimate_alarm_clock/app/data/providers/secure_storage_provider. import 'package:ultimate_alarm_clock/app/utils/utils.dart'; import 'package:uuid/uuid.dart'; -class TimerController extends GetxController with WidgetsBindingObserver { +class TimerController extends GetxController + with WidgetsBindingObserver, GetTickerProviderStateMixin { MethodChannel timerChannel = const MethodChannel('timer'); final initialTime = DateTime(0, 0, 0, 0, 1, 0).obs; final remainingTime = const Duration(hours: 0, minutes: 0, seconds: 0).obs; @@ -18,8 +21,41 @@ class TimerController extends GetxController with WidgetsBindingObserver { RxInt startTime = 0.obs; RxBool isTimerPaused = false.obs; RxBool isTimerRunning = false.obs; + RxBool isbottom = false.obs; Rx countdownTimer = Rx(null); - TimerModel timerRecord = Utils.genFakeTimerModel(); + RxInt timerCount = 0.obs; + Stream? isarTimers; + ScrollController scrollController = ScrollController(); + RxList timers = [].obs; + RxList pausedTimers = [].obs; + RxList timerAnimationControllerList = [].obs; + RxList timeLeft = [].obs; + late AnimationController _controller; + + getFakeTimerModel() async { + TimerModel fakeTimer = await Utils.genFakeTimerModel(); + return fakeTimer; + } + + updateTimerInfo() async { + timerList.value = await IsarDb.getAllTimers(); + timerCount.value = await IsarDb.getNumberOfTimers(); + } + + initializeTempTimerVariables() { + pausedTimers.value = timerList.map((pause) => pause.isPaused).toList(); + timeLeft.value = timerList.map((time) => time.intervalToAlarm).toList(); + + } + + void toggleAnimation(int index) { + pausedTimers[index] == 0 + ? pausedTimers[index] = 1 + : pausedTimers[index] = 0; + + print(pausedTimers); + } + late int currentTimerIsarId; var hours = 0.obs, minutes = 1.obs, seconds = 0.obs; @@ -27,16 +63,27 @@ class TimerController extends GetxController with WidgetsBindingObserver { String strDigits(int n) => n.toString().padLeft(2, '0'); + final RxList timerList = [].obs; + @override - void onInit() { + Future onInit() async { super.onInit(); WidgetsBinding.instance.addObserver(this); loadTimerStateFromStorage(); + isarTimers = IsarDb.getTimers(); + updateTimerInfo(); + scrollController.addListener(() { + if (scrollController.offset < scrollController.position.maxScrollExtent && + !scrollController.position.outOfRange) { + isbottom.value = true; + } else { + isbottom.value = false; + } + }); } @override void onClose() { - WidgetsBinding.instance.removeObserver(this); super.onClose(); } @@ -106,6 +153,9 @@ class TimerController extends GetxController with WidgetsBindingObserver { } void createTimer() async { + TimerModel timerRecord = await getFakeTimerModel(); + print("${DateTime.now().toString()}"); + print("${DateTime.now().add(remainingTime.value).toString()}"); timerRecord.timerTime = Utils.formatDateTimeToHHMMSS( DateTime.now().add(remainingTime.value), ); @@ -117,9 +167,20 @@ class TimerController extends GetxController with WidgetsBindingObserver { DateTime.now().add(remainingTime.value), ); timerRecord.ringtoneName = 'Default'; - scheduleTimer(timerRecord); - - await _secureStorageProvider.writeTimerId(timerId: timerRecord.isarId); + IsarDb.insertTimer(timerRecord).then((value) { + updateTimerInfo(); + Get.back(); + }); + timerRecord.timerName = "${timerRecord.timerTime} Timer"; + timerList.forEach((timer) { + print('Timer ID: ${timer.timerId}'); + print('Timer Time: ${timer.timerTime}'); + print('Main Timer Time: ${timer.mainTimerTime}'); + print('Interval to Alarm: ${timer.intervalToAlarm}'); + print('Ringtone Name: ${timer.ringtoneName}'); + print('Timer Name: ${timer.timerName}'); + print('Is Paused: ${timer.isPaused}'); + }); } void startTimer() async { @@ -155,6 +216,10 @@ class TimerController extends GetxController with WidgetsBindingObserver { } } + deleteTimer(int id) async { + await IsarDb.deleteTimer(id).then((value) => updateTimerInfo()); + } + cancelTimer() async { await timerChannel.invokeMethod('cancelTimer'); } diff --git a/lib/app/modules/timer/views/timer_animation.dart b/lib/app/modules/timer/views/timer_animation.dart new file mode 100644 index 00000000..00a4c532 --- /dev/null +++ b/lib/app/modules/timer/views/timer_animation.dart @@ -0,0 +1,263 @@ +import 'dart:async'; +import 'dart:ui'; +import 'dart:math' as math; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:ultimate_alarm_clock/app/data/models/timer_model.dart'; +import 'package:ultimate_alarm_clock/app/modules/timer/controllers/timer_controller.dart'; +import 'package:ultimate_alarm_clock/app/utils/constants.dart'; + +import '../../../utils/utils.dart'; +import '../../settings/controllers/theme_controller.dart'; + +class TimerAnimatedCard extends StatefulWidget { + TimerModel timer; + int index; + + TimerAnimatedCard(this.timer, this.index); + @override + _TimerAnimatedCardState createState() => _TimerAnimatedCardState(); +} + +class _TimerAnimatedCardState extends State + with SingleTickerProviderStateMixin, AutomaticKeepAliveClientMixin { + late AnimationController _controller; + TimerController controller = Get.find(); + ThemeController themeController = Get.find(); + + bool isPlaying = false; + Timer? timerCounter; + late int milliseconds; + late int progressCounter; + void startTimer() { + milliseconds = widget.timer.intervalToAlarm; + timerCounter = Timer.periodic(Duration(seconds: 1), (timer) { + if (milliseconds > 0) { + setState(() { + milliseconds -= 1000; + progressCounter += 1000; + }); + } + }); + } + + void stopTimer() { + timerCounter!.cancel(); + } + + @override + void initState() { + super.initState(); + progressCounter = 0; + _controller = AnimationController( + vsync: this, + duration: Duration( + milliseconds: widget.timer + .intervalToAlarm), // Replace x with your duration in milliseconds + ); + if (controller.pausedTimers.value[widget.index] == 0) { + startTimer(); + _controller.forward(); + } + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + void _toggleAnimation() { + setState(() { + isPlaying = !isPlaying; + isPlaying ? _controller.forward() : _controller.stop(); + }); + } + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10.0, + ), + child: Container( + height: context.height / 3.3, + width: context.width, + child: Card( + margin: const EdgeInsets.all(5), + color: themeController.isLightMode.value + ? kLightSecondaryBackgroundColor + : ksecondaryBackgroundColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + 18, + ), + ), + child: Stack( + children: [ + AnimatedContainer( + decoration: BoxDecoration( + color: kprimaryDisabledTextColor.withOpacity(0.1), + borderRadius: BorderRadius.circular(18)), + duration: Duration(milliseconds: 1000), + height: context.height / 3.3, + width: context.width * + (progressCounter / (widget.timer.intervalToAlarm)), + ), + Center( + child: Padding( + padding: const EdgeInsets.only( + left: 25.0, + right: 20.0, + top: 20.0, + bottom: 20.0, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Expanded( + flex: 3, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + widget.timer.timerName, + overflow: TextOverflow.ellipsis, + // Set overflow property here + style: Theme.of( + context, + ).textTheme.bodySmall!.copyWith( + fontWeight: FontWeight.w500, + color: kprimaryColor, + fontSize: 18), + ), + InkWell( + onTap: () { + controller + .deleteTimer(widget.timer.timerId); + }, + child: Container( + decoration: BoxDecoration( + color: kprimaryBackgroundColor, + borderRadius: + BorderRadius.circular(20)), + child: const Padding( + padding: EdgeInsets.all(4.0), + child: Icon( + Icons.close, + size: 18, + ), + ), + ), + ) + ], + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 40), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + Utils.formatMilliseconds(milliseconds), + style: Theme.of( + context, + ).textTheme.displayLarge!.copyWith( + color: + themeController.isLightMode.value + ? kLightPrimaryTextColor + : kprimaryTextColor, + fontSize: 50, + ), + ), + InkWell( + customBorder: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(80), + ), + onTap: () { + controller.toggleAnimation(widget.index); + }, + child: Padding( + padding: EdgeInsets.all(25.0), + child: GestureDetector( + onTap: _toggleAnimation, + child: AnimatedBuilder( + animation: _controller, + builder: (context, child) { + return CustomPaint( + painter: CirclePainter( + _controller.value), + child: Container( + width: 50, + height: 50, + child: Icon( + controller.pausedTimers[ + widget.index] == + 0 + ? Icons.pause + : Icons.play_arrow, + size: 30, + color: + kLightPrimaryDisabledTextColor, + ), + ), + ); + }, + ), + )), + ) + ], + ), + ), + ], + ), + ), + ], + ), + ), + ) + ], + ), + ), + ), + ); + } + + @override + // TODO: implement wantKeepAlive + bool get wantKeepAlive => true; +} + +class CirclePainter extends CustomPainter { + final double progress; + + CirclePainter(this.progress); + + @override + void paint(Canvas canvas, Size size) { + Paint paint = Paint() + ..color = kprimaryColor + ..strokeWidth = 50 + ..style = PaintingStyle.stroke; + + double angle = 2 * math.pi * progress; + + canvas.drawArc( + Rect.fromCenter( + center: Offset(size.width / 2, size.height / 2), + width: size.width / 2, + height: size.height / 2), + -math.pi / 2, + angle, + false, + paint, + ); + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) => true; +} diff --git a/lib/app/modules/timer/views/timer_view.dart b/lib/app/modules/timer/views/timer_view.dart index 049f95b7..e9c502bc 100644 --- a/lib/app/modules/timer/views/timer_view.dart +++ b/lib/app/modules/timer/views/timer_view.dart @@ -1,34 +1,48 @@ import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:flutter/material.dart'; +import 'package:get_storage/get_storage.dart'; import 'package:numberpicker/numberpicker.dart'; import 'package:ultimate_alarm_clock/app/data/providers/isar_provider.dart'; -import 'package:ultimate_alarm_clock/app/data/providers/secure_storage_provider.dart'; import 'package:ultimate_alarm_clock/app/modules/addOrUpdateAlarm/controllers/input_time_controller.dart'; import 'package:ultimate_alarm_clock/app/modules/settings/controllers/theme_controller.dart'; import 'package:ultimate_alarm_clock/app/modules/timer/controllers/timer_controller.dart'; -import 'package:ultimate_alarm_clock/app/routes/app_pages.dart'; +import 'package:ultimate_alarm_clock/app/modules/timer/views/timer_animation.dart'; import 'package:ultimate_alarm_clock/app/utils/constants.dart'; import 'package:ultimate_alarm_clock/app/utils/end_drawer.dart'; import 'package:ultimate_alarm_clock/app/utils/utils.dart'; +import 'dart:math' as math; + +import '../../../data/models/timer_model.dart'; class TimerView extends GetView { TimerView({Key? key}) : super(key: key); - ThemeController themeController = Get.find(); - InputTimeController inputTimeController = Get.put(InputTimeController()); - + final ThemeController themeController = Get.find(); + final InputTimeController inputTimeController = Get.put(InputTimeController()); + var width = Get.width; + var height = Get.height; @override Widget build(BuildContext context) { - var width = Get.width; - var height = Get.height; return Scaffold( appBar: PreferredSize( - preferredSize: Size.fromHeight(height / 7.9), + preferredSize: Size.fromHeight(height / 8.9), child: AppBar( toolbarHeight: height / 7.9, elevation: 0.0, - centerTitle: true, + title: Text( + 'Timer', + style: Theme.of(context).textTheme.displaySmall!.copyWith( + color: themeController.isLightMode.value + ? kLightPrimaryTextColor.withOpacity( + 0.75, + ) + : kprimaryTextColor.withOpacity( + 0.75, + ), + fontSize: 26, + ), + ), actions: [ LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { @@ -51,621 +65,653 @@ class TimerView extends GetView { ], ), ), - body: Obx( - () => controller.isTimerRunning.value - ? Column( - mainAxisAlignment: MainAxisAlignment.spaceAround, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Center( - child: Obx( - () { - final hours = controller.strDigits( - controller.remainingTime.value.inHours.remainder(24), - ); - final minutes = controller.strDigits( - controller.remainingTime.value.inMinutes - .remainder(60), - ); - final seconds = controller.strDigits( - controller.remainingTime.value.inSeconds - .remainder(60), - ); - return Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - child: Center( - child: Text( - hours, - style: const TextStyle( - fontSize: 50.0, - fontWeight: FontWeight.bold, + body: Obx(() => controller.timerCount.value == 0 + ? addATimerSpace(context) + : StreamBuilder( + stream: IsarDb.getTimers(), + builder: (context, snapshot) { + if (!snapshot.hasData) { + return const Center( + child: CircularProgressIndicator.adaptive( + backgroundColor: Colors.transparent, + valueColor: AlwaysStoppedAnimation( + kprimaryColor, + ), + ), + ); + } else { + // list of pause values of timers + List? timerList = snapshot.data; + controller.timerList.value = timerList!; + controller.initializeTempTimerVariables(); + + + return ListView.builder( + controller: controller.scrollController, + shrinkWrap: true, + scrollDirection: Axis.vertical, + itemCount: timerList.length, + itemBuilder: (BuildContext context, int index) { + return Column( + children: [ + TimerAnimatedCard(timerList[index], index), + if (index == timerList.length - 1) + Padding( + padding: const EdgeInsets.all(8.0), + child: addATimerSpace(context), + ) + ], + ); + }, + ); + } + }, + )), + floatingActionButton: Obx( + () => Visibility( + visible: controller.isbottom.value, + child: Container( + height: 85, + child: FittedBox( + child: FloatingActionButton( + onPressed: () { + Utils.hapticFeedback(); + }, + backgroundColor: ksecondaryBackgroundColor, + child: const Icon( + Icons.add_alarm, + color: kprimaryColor, + size: 26, + ), + ), + ), + ), + ), + ), + endDrawer: buildEndDrawer(context), + ); + } + + Widget addATimerSpace(BuildContext context) { + return InkWell( + onTap: () { + Utils.hapticFeedback(); + TimerSelector(context); + }, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Center( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Icon( + Icons.add_alarm_outlined, + color: themeController.isLightMode.value + ? kLightPrimaryTextColor.withOpacity( + 0.75, + ) + : kprimaryTextColor.withOpacity( + 0.75, + ), + size: 30, + ), + ), + ), + Text( + 'Tap here to add a timer', + style: Theme.of(context).textTheme.displaySmall!.copyWith( + fontWeight: FontWeight.bold, + color: themeController.isLightMode.value + ? kLightPrimaryDisabledTextColor + : kprimaryDisabledTextColor, + ), + ), + ], + ), + ); + } + + void TimerSelector(BuildContext context) { + Get.defaultDialog( + title: '', + titlePadding: const EdgeInsets.all(0), + backgroundColor: kprimaryBackgroundColor, + content: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(20.0, 0, 0, 20), + child: Row( + children: [ + const Icon( + Icons.timer, + size: 20, + ), + Text( + ' Add timer', + style: Theme.of(context).textTheme.displayMedium!.copyWith( + color: themeController.isLightMode.value + ? kLightPrimaryTextColor.withOpacity( + 0.5, + ) + : kprimaryTextColor.withOpacity( + 0.5, + ), + fontSize: 15, + ), + ), + ], + ), + ), + InkWell( + onTap: () { + Utils.hapticFeedback(); + inputTimeController.changeTimePickerTimer(); + }, + child: Obx( + () => Container( + color: themeController.isLightMode.value + ? kLightPrimaryBackgroundColor + : kprimaryBackgroundColor, + width: width, + child: inputTimeController.isTimePickerTimer.value + ? Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'Hours', + style: Theme.of(context) + .textTheme + .displaySmall! + .copyWith( + fontWeight: FontWeight.bold, + color: themeController.isLightMode.value + ? kLightPrimaryDisabledTextColor + : kprimaryDisabledTextColor, + ), + ), + SizedBox( + height: height * 0.008, + ), + NumberPicker( + minValue: 0, + maxValue: 99, + value: controller.hours.value, + onChanged: (value) { + Utils.hapticFeedback(); + controller.hours.value = value; + inputTimeController.setTextFieldTimerTime(); + }, + infiniteLoop: true, + itemWidth: width * 0.17, + zeroPad: true, + selectedTextStyle: Theme.of(context) + .textTheme + .displayLarge! + .copyWith( + fontSize: 30, + fontWeight: FontWeight.bold, + color: kprimaryColor, + ), + textStyle: Theme.of(context) + .textTheme + .displayMedium! + .copyWith( + fontSize: 18, + color: themeController.isLightMode.value + ? kLightPrimaryDisabledTextColor + : kprimaryDisabledTextColor, + ), + decoration: BoxDecoration( + border: Border( + top: BorderSide( + width: width * 0.005, + color: themeController.isLightMode.value + ? kLightPrimaryDisabledTextColor + : kprimaryDisabledTextColor, + ), + bottom: BorderSide( + width: width * 0.005, + color: themeController.isLightMode.value + ? kLightPrimaryDisabledTextColor + : kprimaryDisabledTextColor, + ), ), ), ), + ], + ), + Padding( + padding: EdgeInsets.only( + left: width * 0.02, + right: width * 0.02, + top: height * 0.035, ), - const Text( + child: Text( ':', - style: TextStyle( - fontSize: 50.0, - fontWeight: FontWeight.bold, - ), - ), - Expanded( - child: Center( - child: Text( - minutes, - style: const TextStyle( - fontSize: 50.0, + style: Theme.of(context) + .textTheme + .displayLarge! + .copyWith( fontWeight: FontWeight.bold, + color: themeController.isLightMode.value + ? kLightPrimaryDisabledTextColor + : kprimaryDisabledTextColor, + ), + ), + ), + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'Minutes', + style: Theme.of(context) + .textTheme + .displaySmall! + .copyWith( + fontWeight: FontWeight.bold, + color: themeController.isLightMode.value + ? kLightPrimaryDisabledTextColor + : kprimaryDisabledTextColor, + ), + ), + SizedBox( + height: height * 0.008, + ), + NumberPicker( + minValue: 0, + maxValue: 59, + value: controller.minutes.value, + onChanged: (value) { + controller.minutes.value = value; + inputTimeController.setTextFieldTimerTime(); + }, + infiniteLoop: true, + itemWidth: width * 0.17, + zeroPad: true, + selectedTextStyle: Theme.of(context) + .textTheme + .displayLarge! + .copyWith( + fontSize: 30, + fontWeight: FontWeight.bold, + color: kprimaryColor, + ), + textStyle: Theme.of(context) + .textTheme + .displayMedium! + .copyWith( + fontSize: 18, + color: themeController.isLightMode.value + ? kLightPrimaryDisabledTextColor + : kprimaryDisabledTextColor, + ), + decoration: BoxDecoration( + border: Border( + top: BorderSide( + width: width * 0.005, + color: themeController.isLightMode.value + ? kLightPrimaryDisabledTextColor + : kprimaryDisabledTextColor, + ), + bottom: BorderSide( + width: width * 0.005, + color: themeController.isLightMode.value + ? kLightPrimaryDisabledTextColor + : kprimaryDisabledTextColor, + ), ), ), ), + ], + ), + Padding( + padding: EdgeInsets.only( + left: width * 0.02, + right: width * 0.02, + top: height * 0.035, ), - const Text( + child: Text( ':', - style: TextStyle( - fontSize: 50.0, - fontWeight: FontWeight.bold, - ), - ), - Expanded( - child: Center( - child: Text( - seconds, - style: const TextStyle( - fontSize: 50.0, + style: Theme.of(context) + .textTheme + .displayLarge! + .copyWith( fontWeight: FontWeight.bold, + color: themeController.isLightMode.value + ? kLightPrimaryDisabledTextColor + : kprimaryDisabledTextColor, ), - ), - ), ), - ], - ); - }, - ), - ), - Obx( - () => Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - FloatingActionButton.small( - heroTag: 'stop', - onPressed: () async { - Utils.hapticFeedback(); - controller.stopTimer(); - int timerId = - await SecureStorageProvider().readTimerId(); - await IsarDb.deleteAlarm(timerId); - }, - child: const Icon(Icons.close_rounded), - ), - SizedBox( - width: width * 0.11, - ), - FloatingActionButton( - heroTag: 'pause', - onPressed: () { - Utils.hapticFeedback(); - controller.isTimerPaused.value - ? controller.resumeTimer() - : controller.pauseTimer(); - }, - child: Icon( - controller.isTimerPaused.value - ? Icons.play_arrow_rounded - : Icons.pause_rounded, - size: 33, ), - ), - ], - ), - ), - ], - ) - : InkWell( - onTap: () { - Utils.hapticFeedback(); - inputTimeController.changeTimePickerTimer(); - }, - child: Obx( - () => Container( - color: themeController.isLightMode.value - ? kLightPrimaryBackgroundColor - : kprimaryBackgroundColor, - height: height * 0.32, - width: width, - child: inputTimeController.isTimePickerTimer.value - ? Row( + Column( mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, children: [ - Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - 'Hours', - style: Theme.of(context) - .textTheme - .displaySmall! - .copyWith( - fontWeight: FontWeight.bold, - color: themeController - .isLightMode.value - ? kLightPrimaryDisabledTextColor - : kprimaryDisabledTextColor, - ), - ), - SizedBox( - height: height * 0.008, - ), - NumberPicker( - minValue: 0, - maxValue: 23, - value: controller.hours.value, - onChanged: (value) { - Utils.hapticFeedback(); - controller.hours.value = value; - inputTimeController. - setTextFieldTimerTime(); - }, - infiniteLoop: true, - itemWidth: width * 0.17, - zeroPad: true, - selectedTextStyle: Theme.of(context) - .textTheme - .displayLarge! - .copyWith( - fontWeight: FontWeight.bold, - color: kprimaryColor, - ), - textStyle: Theme.of(context) - .textTheme - .displayMedium! - .copyWith( - fontSize: 20, - color: themeController - .isLightMode.value - ? kLightPrimaryDisabledTextColor - : kprimaryDisabledTextColor, - ), - decoration: BoxDecoration( - border: Border( - top: BorderSide( - width: width * 0.005, - color: themeController - .isLightMode.value - ? kLightPrimaryDisabledTextColor - : kprimaryDisabledTextColor, - ), - bottom: BorderSide( - width: width * 0.005, - color: themeController - .isLightMode.value - ? kLightPrimaryDisabledTextColor - : kprimaryDisabledTextColor, - ), - ), + Text( + 'Seconds', + style: Theme.of(context) + .textTheme + .displaySmall! + .copyWith( + fontWeight: FontWeight.bold, + color: themeController.isLightMode.value + ? kLightPrimaryDisabledTextColor + : kprimaryDisabledTextColor, ), - ), - ], ), - Padding( - padding: EdgeInsets.only( - left: width * 0.02, - right: width * 0.02, - top: height * 0.035, - ), - child: Text( - ':', - style: Theme.of(context) - .textTheme - .displayLarge! - .copyWith( - fontWeight: FontWeight.bold, - color: themeController.isLightMode.value - ? kLightPrimaryDisabledTextColor - : kprimaryDisabledTextColor, - ), - ), + SizedBox( + height: height * 0.008, ), - Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - 'Minutes', - style: Theme.of(context) - .textTheme - .displaySmall! - .copyWith( - fontWeight: FontWeight.bold, - color: themeController - .isLightMode.value - ? kLightPrimaryDisabledTextColor - : kprimaryDisabledTextColor, - ), - ), - SizedBox( - height: height * 0.008, - ), - NumberPicker( - minValue: 0, - maxValue: 59, - value: controller.minutes.value, - onChanged: (value) { - controller.minutes.value = value; - inputTimeController. - setTextFieldTimerTime(); - }, - infiniteLoop: true, - itemWidth: width * 0.17, - zeroPad: true, - selectedTextStyle: Theme.of(context) - .textTheme - .displayLarge! - .copyWith( - fontWeight: FontWeight.bold, - color: kprimaryColor, - ), - textStyle: Theme.of(context) - .textTheme - .displayMedium! - .copyWith( - fontSize: 20, - color: themeController - .isLightMode.value - ? kLightPrimaryDisabledTextColor - : kprimaryDisabledTextColor, - ), - decoration: BoxDecoration( - border: Border( - top: BorderSide( - width: width * 0.005, - color: themeController - .isLightMode.value - ? kLightPrimaryDisabledTextColor - : kprimaryDisabledTextColor, - ), - bottom: BorderSide( - width: width * 0.005, - color: themeController - .isLightMode.value - ? kLightPrimaryDisabledTextColor - : kprimaryDisabledTextColor, - ), - ), + NumberPicker( + minValue: 0, + maxValue: 59, + value: controller.seconds.value, + onChanged: (value) { + controller.seconds.value = value; + inputTimeController.setTextFieldTimerTime(); + }, + infiniteLoop: true, + itemWidth: width * 0.17, + zeroPad: true, + selectedTextStyle: Theme.of(context) + .textTheme + .displayLarge! + .copyWith( + fontSize: 30, + fontWeight: FontWeight.bold, + color: kprimaryColor, + ), + textStyle: Theme.of(context) + .textTheme + .displayMedium! + .copyWith( + fontSize: 18, + color: themeController.isLightMode.value + ? kLightPrimaryDisabledTextColor + : kprimaryDisabledTextColor, + ), + decoration: BoxDecoration( + border: Border( + top: BorderSide( + width: width * 0.005, + color: themeController.isLightMode.value + ? kLightPrimaryDisabledTextColor + : kprimaryDisabledTextColor, + ), + bottom: BorderSide( + width: width * 0.005, + color: themeController.isLightMode.value + ? kLightPrimaryDisabledTextColor + : kprimaryDisabledTextColor, ), ), - ], - ), - Padding( - padding: EdgeInsets.only( - left: width * 0.02, - right: width * 0.02, - top: height * 0.035, - ), - child: Text( - ':', - style: Theme.of(context) - .textTheme - .displayLarge! - .copyWith( - fontWeight: FontWeight.bold, - color: themeController.isLightMode.value - ? kLightPrimaryDisabledTextColor - : kprimaryDisabledTextColor, - ), ), ), - Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - 'Seconds', - style: Theme.of(context) - .textTheme - .displaySmall! - .copyWith( - fontWeight: FontWeight.bold, - color: themeController - .isLightMode.value - ? kLightPrimaryDisabledTextColor - : kprimaryDisabledTextColor, - ), - ), - SizedBox( - height: height * 0.008, + ], + ), + ], + ) + : Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'Hours', + style: Theme.of(context) + .textTheme + .displaySmall! + .copyWith( + fontWeight: FontWeight.bold, + color: themeController.isLightMode.value + ? kLightPrimaryDisabledTextColor + : kprimaryDisabledTextColor, + ), + ), + SizedBox( + height: height * 0.008, + ), + SizedBox( + width: 80, + child: TextField( + onChanged: (_) { + inputTimeController.setTimerTime(); + }, + decoration: const InputDecoration( + hintText: 'HH', + border: InputBorder.none, ), - NumberPicker( - minValue: 0, - maxValue: 59, - value: controller.seconds.value, - onChanged: (value) { - controller.seconds.value = value; - inputTimeController. - setTextFieldTimerTime(); - }, - infiniteLoop: true, - itemWidth: width * 0.17, - zeroPad: true, - selectedTextStyle: Theme.of(context) - .textTheme - .displayLarge! - .copyWith( - fontWeight: FontWeight.bold, - color: kprimaryColor, - ), - textStyle: Theme.of(context) - .textTheme - .displayMedium! - .copyWith( - fontSize: 20, - color: themeController - .isLightMode.value - ? kLightPrimaryDisabledTextColor - : kprimaryDisabledTextColor, - ), - decoration: BoxDecoration( - border: Border( - top: BorderSide( - width: width * 0.005, - color: themeController - .isLightMode.value - ? kLightPrimaryDisabledTextColor - : kprimaryDisabledTextColor, - ), - bottom: BorderSide( - width: width * 0.005, - color: themeController - .isLightMode.value - ? kLightPrimaryDisabledTextColor - : kprimaryDisabledTextColor, - ), + textAlign: TextAlign.center, + controller: inputTimeController + .inputHoursControllerTimer, + keyboardType: TextInputType.number, + inputFormatters: [ + FilteringTextInputFormatter.allow( + RegExp( + '[1,2,3,4,5,6,7,8,9,0]', ), ), - ), - ], + LengthLimitingTextInputFormatter( + 2, + ), + LimitRange( + 0, + 99, + ), + ], + ), ), ], - ) - : Row( + ), + Padding( + padding: EdgeInsets.only( + left: width * 0.02, + right: width * 0.02, + top: height * 0.035, + ), + child: Text( + ':', + style: Theme.of(context) + .textTheme + .displayLarge! + .copyWith( + fontWeight: FontWeight.bold, + color: themeController.isLightMode.value + ? kLightPrimaryDisabledTextColor + : kprimaryDisabledTextColor, + ), + ), + ), + Column( mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, children: [ - Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - 'Hours', - style: Theme.of(context) - .textTheme - .displaySmall! - .copyWith( - fontWeight: FontWeight.bold, - color: themeController - .isLightMode.value - ? kLightPrimaryDisabledTextColor - : kprimaryDisabledTextColor, - ), - ), - SizedBox( - height: height * 0.008, - ), - SizedBox( - width: 80, - child: TextField( - onChanged: (_) { - inputTimeController.setTimerTime(); - }, - decoration: const InputDecoration( - hintText: 'HH', - border: InputBorder.none, - ), - textAlign: TextAlign.center, - controller: inputTimeController - .inputHoursControllerTimer, - keyboardType: TextInputType.number, - inputFormatters: [ - FilteringTextInputFormatter.allow( - RegExp( - '[1,2,3,4,5,6,7,8,9,0]', - ), - ), - LengthLimitingTextInputFormatter( - 2, - ), - LimitRange( - 0, - 23, - ), - ], + Text( + 'Minutes', + style: Theme.of(context) + .textTheme + .displaySmall! + .copyWith( + fontWeight: FontWeight.bold, + color: themeController.isLightMode.value + ? kLightPrimaryDisabledTextColor + : kprimaryDisabledTextColor, ), - ), - ], ), - Padding( - padding: EdgeInsets.only( - left: width * 0.02, - right: width * 0.02, - top: height * 0.035, - ), - child: Text( - ':', - style: Theme.of(context) - .textTheme - .displayLarge! - .copyWith( - fontWeight: FontWeight.bold, - color: themeController.isLightMode.value - ? kLightPrimaryDisabledTextColor - : kprimaryDisabledTextColor, - ), - ), + SizedBox( + height: height * 0.008, ), - Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - 'Minutes', - style: Theme.of(context) - .textTheme - .displaySmall! - .copyWith( - fontWeight: FontWeight.bold, - color: themeController - .isLightMode.value - ? kLightPrimaryDisabledTextColor - : kprimaryDisabledTextColor, - ), - ), - SizedBox( - height: height * 0.008, + SizedBox( + width: 80, + child: TextField( + onChanged: (_) { + inputTimeController.setTimerTime(); + }, + decoration: const InputDecoration( + hintText: 'MM', + border: InputBorder.none, ), - SizedBox( - width: 80, - child: TextField( - onChanged: (_) { - inputTimeController.setTimerTime(); - }, - decoration: const InputDecoration( - hintText: 'MM', - border: InputBorder.none, + textAlign: TextAlign.center, + controller: inputTimeController + .inputMinutesControllerTimer, + keyboardType: TextInputType.number, + inputFormatters: [ + FilteringTextInputFormatter.allow( + RegExp( + '[1,2,3,4,5,6,7,8,9,0]', ), - textAlign: TextAlign.center, - controller: inputTimeController - .inputMinutesControllerTimer, - keyboardType: TextInputType.number, - inputFormatters: [ - FilteringTextInputFormatter.allow( - RegExp( - '[1,2,3,4,5,6,7,8,9,0]', - ), - ), - LengthLimitingTextInputFormatter( - 2, - ), - LimitRange( - 0, - 59, - ), - ], ), - ), - ], - ), - Padding( - padding: EdgeInsets.only( - left: width * 0.02, - right: width * 0.02, - top: height * 0.035, - ), - child: Text( - ':', - style: Theme.of(context) - .textTheme - .displayLarge! - .copyWith( - fontWeight: FontWeight.bold, - color: themeController.isLightMode.value - ? kLightPrimaryDisabledTextColor - : kprimaryDisabledTextColor, - ), + LengthLimitingTextInputFormatter( + 2, + ), + LimitRange( + 0, + 59, + ), + ], ), ), - Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - 'Seconds', - style: Theme.of(context) - .textTheme - .displaySmall! - .copyWith( - fontWeight: FontWeight.bold, - color: themeController - .isLightMode.value - ? kLightPrimaryDisabledTextColor - : kprimaryDisabledTextColor, - ), + ], + ), + Padding( + padding: EdgeInsets.only( + left: width * 0.02, + right: width * 0.02, + top: height * 0.035, + ), + child: Text( + ':', + style: Theme.of(context) + .textTheme + .displayLarge! + .copyWith( + fontWeight: FontWeight.bold, + color: themeController.isLightMode.value + ? kLightPrimaryDisabledTextColor + : kprimaryDisabledTextColor, ), - SizedBox( - height: height * 0.008, + ), + ), + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'Seconds', + style: Theme.of(context) + .textTheme + .displaySmall! + .copyWith( + fontWeight: FontWeight.bold, + color: themeController.isLightMode.value + ? kLightPrimaryDisabledTextColor + : kprimaryDisabledTextColor, + ), + ), + SizedBox( + height: height * 0.008, + ), + SizedBox( + width: 80, + child: TextField( + onChanged: (_) { + inputTimeController.setTimerTime(); + }, + decoration: const InputDecoration( + hintText: 'SS', + border: InputBorder.none, ), - SizedBox( - width: 80, - child: TextField( - onChanged: (_) { - inputTimeController.setTimerTime(); - }, - decoration: const InputDecoration( - hintText: 'SS', - border: InputBorder.none, + textAlign: TextAlign.center, + controller: inputTimeController + .inputSecondsControllerTimer, + keyboardType: TextInputType.number, + inputFormatters: [ + FilteringTextInputFormatter.allow( + RegExp( + '[1,2,3,4,5,6,7,8,9,0]', ), - textAlign: TextAlign.center, - controller: inputTimeController - .inputSecondsControllerTimer, - keyboardType: TextInputType.number, - inputFormatters: [ - FilteringTextInputFormatter.allow( - RegExp( - '[1,2,3,4,5,6,7,8,9,0]', - ), - ), - LengthLimitingTextInputFormatter( - 2, - ), - LimitRange( - 0, - 59, - ), - ], ), - ), - ], + LengthLimitingTextInputFormatter( + 2, + ), + LimitRange( + 0, + 59, + ), + ], + ), ), ], ), + ], + ), + ), + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(0, 20.0, 20.00, 0), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(0, 0, 20, 0), + child: InkWell( + onTap: () { + Get.back(); + }, + child: Text( + 'Cancel', + style: + Theme.of(context).textTheme.displayMedium!.copyWith( + color: themeController.isLightMode.value + ? kLightPrimaryTextColor.withOpacity( + 0.5, + ) + : kprimaryTextColor.withOpacity( + 0.5, + ), + fontSize: 15, + ), + ), ), ), - ), - ), - floatingActionButton: Obx( - () => controller.isTimerRunning.value - ? const SizedBox() - : Obx( - () => AbsorbPointer( - absorbing: controller.hours.value == 0 && - controller.minutes.value == 0 && - controller.seconds.value == 0 - ? true - : false, - child: FloatingActionButton( - onPressed: () { - Utils.hapticFeedback(); + Padding( + padding: const EdgeInsets.fromLTRB(20, 0, 0, 0), + child: InkWell( + onTap: () { controller.remainingTime.value = Duration( hours: controller.hours.value, minutes: controller.minutes.value, seconds: controller.seconds.value, ); - controller.startTimer(); controller.createTimer(); }, - backgroundColor: controller.hours.value == 0 && - controller.minutes.value == 0 && - controller.seconds.value == 0 - ? kprimaryDisabledTextColor - : kprimaryColor, - child: const Icon( - Icons.play_arrow_rounded, - size: 33, + child: Text( + 'OK', + style: + Theme.of(context).textTheme.displayMedium!.copyWith( + color: themeController.isLightMode.value + ? kLightPrimaryTextColor.withOpacity( + 0.5, + ) + : kprimaryTextColor.withOpacity( + 0.5, + ), + fontSize: 15, + ), ), ), ), - ), + ], + ), + ), + ], ), - endDrawer: buildEndDrawer(context), ); } } diff --git a/lib/app/modules/timerRing/controllers/timer_ring_controller.dart b/lib/app/modules/timerRing/controllers/timer_ring_controller.dart index 332f6a0a..c9ff6c70 100644 --- a/lib/app/modules/timerRing/controllers/timer_ring_controller.dart +++ b/lib/app/modules/timerRing/controllers/timer_ring_controller.dart @@ -11,9 +11,12 @@ import 'package:vibration/vibration.dart'; class TimerRingController extends GetxController { MethodChannel timerChannel = const MethodChannel('timer'); - final Rx currentlyRingingAlarm = Utils.genFakeTimerModel().obs; Timer? vibrationTimer; late StreamSubscription _subscription; + getFakeTimerModel()async { + TimerModel fakeTimer = await Utils.genFakeTimerModel(); + return fakeTimer; + } @override void onInit() async { super.onInit(); @@ -28,17 +31,17 @@ class TimerRingController extends GetxController { Timer.periodic(const Duration(milliseconds: 3500), (Timer timer) { Vibration.vibrate(pattern: [500, 3000]); }); - AudioUtils.playTimer(alarmRecord: currentlyRingingAlarm.value); + AudioUtils.playTimer(alarmRecord: await getFakeTimerModel().value); await timerChannel.invokeMethod('cancelTimer'); } @override - void onClose() { + onClose() async { Vibration.cancel(); vibrationTimer!.cancel(); AudioUtils.stopTimer( - ringtoneName: currentlyRingingAlarm.value.ringtoneName, + ringtoneName: await getFakeTimerModel().ringtoneName, ); _subscription.cancel(); super.onClose(); diff --git a/lib/app/utils/utils.dart b/lib/app/utils/utils.dart index ff081514..57cc0d21 100644 --- a/lib/app/utils/utils.dart +++ b/lib/app/utils/utils.dart @@ -10,6 +10,7 @@ import 'dart:math'; import 'package:ultimate_alarm_clock/app/data/models/alarm_model.dart'; import 'package:ultimate_alarm_clock/app/data/models/quote_model.dart'; import 'package:ultimate_alarm_clock/app/data/models/timer_model.dart'; +import 'package:ultimate_alarm_clock/app/data/providers/isar_provider.dart'; import 'package:ultimate_alarm_clock/app/data/providers/secure_storage_provider.dart'; import 'package:ultimate_alarm_clock/app/utils/quote_list.dart'; @@ -101,6 +102,27 @@ class Utils { return milliseconds; } + static String formatMilliseconds(int milliseconds) { + final Duration duration = Duration(milliseconds: milliseconds); + final int seconds = duration.inSeconds; + final int minutes = duration.inMinutes; + final int hours = duration.inHours; + + if (seconds < 10) { + return "${seconds}"; + } else if (seconds < 60) { + return "${seconds}"; + } else if (minutes < 10) { + return "${minutes}:${seconds % 60 < 10 ? '0' : ''}${seconds % 60}"; + } else if (minutes < 60) { + return "${minutes}:${seconds % 60 < 10 ? '0' : ''}${seconds % 60}"; + } else if (hours < 10) { + return "${hours}:${minutes % 60 < 10 ? '0' : ''}${minutes % 60}:${seconds % 60 < 10 ? '0' : ''}${seconds % 60}"; + } else { + return "${hours}:${minutes % 60 < 10 ? '0' : ''}${minutes % 60}:${seconds % 60 < 10 ? '0' : ''}${seconds % 60}"; + } + } + static List convertTo12HourFormat(String time) { int hour = int.parse(time.substring(0, 2)); String minute = time.substring(3); @@ -355,12 +377,13 @@ class Utils { } // Utility function to create a dummy model to pass to functions - static TimerModel genFakeTimerModel() { + static Future genFakeTimerModel() async { return TimerModel( intervalToAlarm: 0, mainTimerTime: '', ringtoneName: '', timerTime: Utils.timeOfDayToString(TimeOfDay.now()), + timerName: '', ); } diff --git a/pubspec.lock b/pubspec.lock index 7d2a3339..cd000dfb 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -752,30 +752,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.8.2" - leak_tracker: - dependency: transitive - description: - name: leak_tracker - sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" - url: "https://pub.dev" - source: hosted - version: "10.0.0" - leak_tracker_flutter_testing: - dependency: transitive - description: - name: leak_tracker_flutter_testing - sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 - url: "https://pub.dev" - source: hosted - version: "2.0.1" - leak_tracker_testing: - dependency: transitive - description: - name: leak_tracker_testing - sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 - url: "https://pub.dev" - source: hosted - version: "2.0.1" lints: dependency: transitive description: @@ -804,26 +780,26 @@ packages: dependency: transitive description: name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" url: "https://pub.dev" source: hosted - version: "0.12.16+1" + version: "0.12.16" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.5.0" meta: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.10.0" mgrs_dart: dependency: transitive description: @@ -868,10 +844,10 @@ packages: dependency: transitive description: name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.8.3" path_parsing: dependency: transitive description: @@ -1149,6 +1125,22 @@ packages: url: "https://pub.dev" source: hosted version: "7.0.0" + sqflite: + dependency: "direct main" + description: + name: sqflite + sha256: a9016f495c927cb90557c909ff26a6d92d9bd54fc42ba92e19d4e79d61e798c6 + url: "https://pub.dev" + source: hosted + version: "2.3.2" + sqflite_common: + dependency: transitive + description: + name: sqflite_common + sha256: "28d8c66baee4968519fb8bd6cdbedad982d6e53359091f0b74544a9f32ec72d5" + url: "https://pub.dev" + source: hosted + version: "2.5.3" stack_trace: dependency: transitive description: @@ -1365,14 +1357,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.8.4" - vm_service: - dependency: transitive - description: - name: vm_service - sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 - url: "https://pub.dev" - source: hosted - version: "13.0.0" watcher: dependency: transitive description: @@ -1462,4 +1446,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.2.0 <4.0.0" \ No newline at end of file + dart: ">=3.2.0 <4.0.0" + flutter: ">=3.16.0" From bae8a1408b04a521aa110983286dc5ee31fa4aef Mon Sep 17 00:00:00 2001 From: AryanSarafDev Date: Wed, 22 May 2024 00:44:15 +0530 Subject: [PATCH 2/5] timer logic optimization --- lib/app/data/models/timer_model.dart | 31 +- lib/app/data/models/timer_model.g.dart | 537 +++++------------- lib/app/data/providers/isar_provider.dart | 34 +- .../timer/controllers/timer_controller.dart | 79 +-- .../modules/timer/views/timer_animation.dart | 149 ++--- lib/app/modules/timer/views/timer_view.dart | 17 +- lib/app/utils/utils.dart | 6 +- 7 files changed, 263 insertions(+), 590 deletions(-) diff --git a/lib/app/data/models/timer_model.dart b/lib/app/data/models/timer_model.dart index 63b765b5..ba72b675 100644 --- a/lib/app/data/models/timer_model.dart +++ b/lib/app/data/models/timer_model.dart @@ -4,30 +4,27 @@ part 'timer_model.g.dart'; @collection class TimerModel { Id timerId = Isar.autoIncrement; - late String timerTime; - late String mainTimerTime; - late String timeElapsed ; - late int intervalToAlarm; - late String ringtoneName; late String timerName; - late int isPaused ; + late int timerValue; + late String startedOn; + late int timeElapsed; + late String ringtoneName; + late int isPaused; TimerModel( - { - required this.intervalToAlarm, - required this.mainTimerTime, + {required this.timerValue, + required this.startedOn, required this.ringtoneName, - required this.timerTime, required this.timerName, this.isPaused = 0, - this.timeElapsed = '00:00:00'}); + this.timeElapsed = 0}); Map toMap() { return { 'id': timerId, - 'timerTime': timerTime, - 'mainTimerTime': mainTimerTime, - 'intervalToAlarm': intervalToAlarm, + 'startedOn': startedOn, + 'timerValue': timerValue, + 'timeElapsed': timeElapsed, 'ringtoneName': ringtoneName, 'timerName': timerName, 'isPaused': isPaused, @@ -37,9 +34,9 @@ class TimerModel { // Extract a TimerModel object from a Map object. TimerModel.fromMap(Map map) { timerId = map['id']; - timerTime = map['timerTime']; - mainTimerTime = map['mainTimerTime']; - intervalToAlarm = map['intervalToAlarm']; + startedOn = map['startedOn']; + timerValue = map['timerValue']; + timeElapsed= map['timeElapsed']; ringtoneName = map['ringtoneName']; timerName = map['timerName']; isPaused = map['isPaused']; diff --git a/lib/app/data/models/timer_model.g.dart b/lib/app/data/models/timer_model.g.dart index 9bb98bcd..6b68a216 100644 --- a/lib/app/data/models/timer_model.g.dart +++ b/lib/app/data/models/timer_model.g.dart @@ -17,40 +17,35 @@ const TimerModelSchema = CollectionSchema( name: r'TimerModel', id: 1326376837060457485, properties: { - r'intervalToAlarm': PropertySchema( - id: 0, - name: r'intervalToAlarm', - type: IsarType.long, - ), r'isPaused': PropertySchema( - id: 1, + id: 0, name: r'isPaused', type: IsarType.long, ), - r'mainTimerTime': PropertySchema( - id: 2, - name: r'mainTimerTime', - type: IsarType.string, - ), r'ringtoneName': PropertySchema( - id: 3, + id: 1, name: r'ringtoneName', type: IsarType.string, ), + r'startedOn': PropertySchema( + id: 2, + name: r'startedOn', + type: IsarType.string, + ), r'timeElapsed': PropertySchema( - id: 4, + id: 3, name: r'timeElapsed', - type: IsarType.string, + type: IsarType.long, ), r'timerName': PropertySchema( - id: 5, + id: 4, name: r'timerName', type: IsarType.string, ), - r'timerTime': PropertySchema( - id: 6, - name: r'timerTime', - type: IsarType.string, + r'timerValue': PropertySchema( + id: 5, + name: r'timerValue', + type: IsarType.long, ) }, estimateSize: _timerModelEstimateSize, @@ -73,11 +68,9 @@ int _timerModelEstimateSize( Map> allOffsets, ) { var bytesCount = offsets.last; - bytesCount += 3 + object.mainTimerTime.length * 3; bytesCount += 3 + object.ringtoneName.length * 3; - bytesCount += 3 + object.timeElapsed.length * 3; + bytesCount += 3 + object.startedOn.length * 3; bytesCount += 3 + object.timerName.length * 3; - bytesCount += 3 + object.timerTime.length * 3; return bytesCount; } @@ -87,13 +80,12 @@ void _timerModelSerialize( List offsets, Map> allOffsets, ) { - writer.writeLong(offsets[0], object.intervalToAlarm); - writer.writeLong(offsets[1], object.isPaused); - writer.writeString(offsets[2], object.mainTimerTime); - writer.writeString(offsets[3], object.ringtoneName); - writer.writeString(offsets[4], object.timeElapsed); - writer.writeString(offsets[5], object.timerName); - writer.writeString(offsets[6], object.timerTime); + writer.writeLong(offsets[0], object.isPaused); + writer.writeString(offsets[1], object.ringtoneName); + writer.writeString(offsets[2], object.startedOn); + writer.writeLong(offsets[3], object.timeElapsed); + writer.writeString(offsets[4], object.timerName); + writer.writeLong(offsets[5], object.timerValue); } TimerModel _timerModelDeserialize( @@ -103,13 +95,12 @@ TimerModel _timerModelDeserialize( Map> allOffsets, ) { final object = TimerModel( - intervalToAlarm: reader.readLong(offsets[0]), - isPaused: reader.readLongOrNull(offsets[1]) ?? 0, - mainTimerTime: reader.readString(offsets[2]), - ringtoneName: reader.readString(offsets[3]), - timeElapsed: reader.readStringOrNull(offsets[4]) ?? '00:00:00', - timerName: reader.readString(offsets[5]), - timerTime: reader.readString(offsets[6]), + isPaused: reader.readLongOrNull(offsets[0]) ?? 0, + ringtoneName: reader.readString(offsets[1]), + startedOn: reader.readString(offsets[2]), + timeElapsed: reader.readLongOrNull(offsets[3]) ?? 0, + timerName: reader.readString(offsets[4]), + timerValue: reader.readLong(offsets[5]), ); object.timerId = id; return object; @@ -123,19 +114,17 @@ P _timerModelDeserializeProp

( ) { switch (propertyId) { case 0: - return (reader.readLong(offset)) as P; - case 1: return (reader.readLongOrNull(offset) ?? 0) as P; + case 1: + return (reader.readString(offset)) as P; case 2: return (reader.readString(offset)) as P; case 3: - return (reader.readString(offset)) as P; + return (reader.readLongOrNull(offset) ?? 0) as P; case 4: - return (reader.readStringOrNull(offset) ?? '00:00:00') as P; - case 5: - return (reader.readString(offset)) as P; - case 6: return (reader.readString(offset)) as P; + case 5: + return (reader.readLong(offset)) as P; default: throw IsarError('Unknown property with id $propertyId'); } @@ -236,62 +225,6 @@ extension TimerModelQueryWhere extension TimerModelQueryFilter on QueryBuilder { - QueryBuilder - intervalToAlarmEqualTo(int value) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'intervalToAlarm', - value: value, - )); - }); - } - - QueryBuilder - intervalToAlarmGreaterThan( - int value, { - bool include = false, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'intervalToAlarm', - value: value, - )); - }); - } - - QueryBuilder - intervalToAlarmLessThan( - int value, { - bool include = false, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'intervalToAlarm', - value: value, - )); - }); - } - - QueryBuilder - intervalToAlarmBetween( - int lower, - int upper, { - bool includeLower = true, - bool includeUpper = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'intervalToAlarm', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); - }); - } - QueryBuilder isPausedEqualTo( int value) { return QueryBuilder.apply(this, (query) { @@ -347,13 +280,13 @@ extension TimerModelQueryFilter } QueryBuilder - mainTimerTimeEqualTo( + ringtoneNameEqualTo( String value, { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.equalTo( - property: r'mainTimerTime', + property: r'ringtoneName', value: value, caseSensitive: caseSensitive, )); @@ -361,7 +294,7 @@ extension TimerModelQueryFilter } QueryBuilder - mainTimerTimeGreaterThan( + ringtoneNameGreaterThan( String value, { bool include = false, bool caseSensitive = true, @@ -369,7 +302,7 @@ extension TimerModelQueryFilter return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.greaterThan( include: include, - property: r'mainTimerTime', + property: r'ringtoneName', value: value, caseSensitive: caseSensitive, )); @@ -377,7 +310,7 @@ extension TimerModelQueryFilter } QueryBuilder - mainTimerTimeLessThan( + ringtoneNameLessThan( String value, { bool include = false, bool caseSensitive = true, @@ -385,7 +318,7 @@ extension TimerModelQueryFilter return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.lessThan( include: include, - property: r'mainTimerTime', + property: r'ringtoneName', value: value, caseSensitive: caseSensitive, )); @@ -393,7 +326,7 @@ extension TimerModelQueryFilter } QueryBuilder - mainTimerTimeBetween( + ringtoneNameBetween( String lower, String upper, { bool includeLower = true, @@ -402,7 +335,7 @@ extension TimerModelQueryFilter }) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.between( - property: r'mainTimerTime', + property: r'ringtoneName', lower: lower, includeLower: includeLower, upper: upper, @@ -413,13 +346,13 @@ extension TimerModelQueryFilter } QueryBuilder - mainTimerTimeStartsWith( + ringtoneNameStartsWith( String value, { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.startsWith( - property: r'mainTimerTime', + property: r'ringtoneName', value: value, caseSensitive: caseSensitive, )); @@ -427,13 +360,13 @@ extension TimerModelQueryFilter } QueryBuilder - mainTimerTimeEndsWith( + ringtoneNameEndsWith( String value, { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.endsWith( - property: r'mainTimerTime', + property: r'ringtoneName', value: value, caseSensitive: caseSensitive, )); @@ -441,10 +374,10 @@ extension TimerModelQueryFilter } QueryBuilder - mainTimerTimeContains(String value, {bool caseSensitive = true}) { + ringtoneNameContains(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.contains( - property: r'mainTimerTime', + property: r'ringtoneName', value: value, caseSensitive: caseSensitive, )); @@ -452,10 +385,10 @@ extension TimerModelQueryFilter } QueryBuilder - mainTimerTimeMatches(String pattern, {bool caseSensitive = true}) { + ringtoneNameMatches(String pattern, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.matches( - property: r'mainTimerTime', + property: r'ringtoneName', wildcard: pattern, caseSensitive: caseSensitive, )); @@ -463,33 +396,32 @@ extension TimerModelQueryFilter } QueryBuilder - mainTimerTimeIsEmpty() { + ringtoneNameIsEmpty() { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.equalTo( - property: r'mainTimerTime', + property: r'ringtoneName', value: '', )); }); } QueryBuilder - mainTimerTimeIsNotEmpty() { + ringtoneNameIsNotEmpty() { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.greaterThan( - property: r'mainTimerTime', + property: r'ringtoneName', value: '', )); }); } - QueryBuilder - ringtoneNameEqualTo( + QueryBuilder startedOnEqualTo( String value, { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.equalTo( - property: r'ringtoneName', + property: r'startedOn', value: value, caseSensitive: caseSensitive, )); @@ -497,7 +429,7 @@ extension TimerModelQueryFilter } QueryBuilder - ringtoneNameGreaterThan( + startedOnGreaterThan( String value, { bool include = false, bool caseSensitive = true, @@ -505,15 +437,14 @@ extension TimerModelQueryFilter return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.greaterThan( include: include, - property: r'ringtoneName', + property: r'startedOn', value: value, caseSensitive: caseSensitive, )); }); } - QueryBuilder - ringtoneNameLessThan( + QueryBuilder startedOnLessThan( String value, { bool include = false, bool caseSensitive = true, @@ -521,15 +452,14 @@ extension TimerModelQueryFilter return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.lessThan( include: include, - property: r'ringtoneName', + property: r'startedOn', value: value, caseSensitive: caseSensitive, )); }); } - QueryBuilder - ringtoneNameBetween( + QueryBuilder startedOnBetween( String lower, String upper, { bool includeLower = true, @@ -538,7 +468,7 @@ extension TimerModelQueryFilter }) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.between( - property: r'ringtoneName', + property: r'startedOn', lower: lower, includeLower: includeLower, upper: upper, @@ -549,49 +479,50 @@ extension TimerModelQueryFilter } QueryBuilder - ringtoneNameStartsWith( + startedOnStartsWith( String value, { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.startsWith( - property: r'ringtoneName', + property: r'startedOn', value: value, caseSensitive: caseSensitive, )); }); } - QueryBuilder - ringtoneNameEndsWith( + QueryBuilder startedOnEndsWith( String value, { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.endsWith( - property: r'ringtoneName', + property: r'startedOn', value: value, caseSensitive: caseSensitive, )); }); } - QueryBuilder - ringtoneNameContains(String value, {bool caseSensitive = true}) { + QueryBuilder startedOnContains( + String value, + {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.contains( - property: r'ringtoneName', + property: r'startedOn', value: value, caseSensitive: caseSensitive, )); }); } - QueryBuilder - ringtoneNameMatches(String pattern, {bool caseSensitive = true}) { + QueryBuilder startedOnMatches( + String pattern, + {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.matches( - property: r'ringtoneName', + property: r'startedOn', wildcard: pattern, caseSensitive: caseSensitive, )); @@ -599,78 +530,69 @@ extension TimerModelQueryFilter } QueryBuilder - ringtoneNameIsEmpty() { + startedOnIsEmpty() { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.equalTo( - property: r'ringtoneName', + property: r'startedOn', value: '', )); }); } QueryBuilder - ringtoneNameIsNotEmpty() { + startedOnIsNotEmpty() { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.greaterThan( - property: r'ringtoneName', + property: r'startedOn', value: '', )); }); } QueryBuilder - timeElapsedEqualTo( - String value, { - bool caseSensitive = true, - }) { + timeElapsedEqualTo(int value) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.equalTo( property: r'timeElapsed', value: value, - caseSensitive: caseSensitive, )); }); } QueryBuilder timeElapsedGreaterThan( - String value, { + int value, { bool include = false, - bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.greaterThan( include: include, property: r'timeElapsed', value: value, - caseSensitive: caseSensitive, )); }); } QueryBuilder timeElapsedLessThan( - String value, { + int value, { bool include = false, - bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.lessThan( include: include, property: r'timeElapsed', value: value, - caseSensitive: caseSensitive, )); }); } QueryBuilder timeElapsedBetween( - String lower, - String upper, { + int lower, + int upper, { bool includeLower = true, bool includeUpper = true, - bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.between( @@ -679,77 +601,6 @@ extension TimerModelQueryFilter includeLower: includeLower, upper: upper, includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - timeElapsedStartsWith( - String value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'timeElapsed', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - timeElapsedEndsWith( - String value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'timeElapsed', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - timeElapsedContains(String value, {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'timeElapsed', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - timeElapsedMatches(String pattern, {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'timeElapsed', - wildcard: pattern, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - timeElapsedIsEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'timeElapsed', - value: '', - )); - }); - } - - QueryBuilder - timeElapsedIsNotEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'timeElapsed', - value: '', )); }); } @@ -942,136 +793,57 @@ extension TimerModelQueryFilter }); } - QueryBuilder timerTimeEqualTo( - String value, { - bool caseSensitive = true, - }) { + QueryBuilder timerValueEqualTo( + int value) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.equalTo( - property: r'timerTime', + property: r'timerValue', value: value, - caseSensitive: caseSensitive, )); }); } QueryBuilder - timerTimeGreaterThan( - String value, { + timerValueGreaterThan( + int value, { bool include = false, - bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.greaterThan( include: include, - property: r'timerTime', + property: r'timerValue', value: value, - caseSensitive: caseSensitive, )); }); } - QueryBuilder timerTimeLessThan( - String value, { + QueryBuilder + timerValueLessThan( + int value, { bool include = false, - bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.lessThan( include: include, - property: r'timerTime', + property: r'timerValue', value: value, - caseSensitive: caseSensitive, )); }); } - QueryBuilder timerTimeBetween( - String lower, - String upper, { + QueryBuilder timerValueBetween( + int lower, + int upper, { bool includeLower = true, bool includeUpper = true, - bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.between( - property: r'timerTime', + property: r'timerValue', lower: lower, includeLower: includeLower, upper: upper, includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - timerTimeStartsWith( - String value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'timerTime', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder timerTimeEndsWith( - String value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'timerTime', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder timerTimeContains( - String value, - {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'timerTime', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder timerTimeMatches( - String pattern, - {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'timerTime', - wildcard: pattern, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - timerTimeIsEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'timerTime', - value: '', - )); - }); - } - - QueryBuilder - timerTimeIsNotEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'timerTime', - value: '', )); }); } @@ -1085,19 +857,6 @@ extension TimerModelQueryLinks extension TimerModelQuerySortBy on QueryBuilder { - QueryBuilder sortByIntervalToAlarm() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'intervalToAlarm', Sort.asc); - }); - } - - QueryBuilder - sortByIntervalToAlarmDesc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'intervalToAlarm', Sort.desc); - }); - } - QueryBuilder sortByIsPaused() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'isPaused', Sort.asc); @@ -1110,27 +869,27 @@ extension TimerModelQuerySortBy }); } - QueryBuilder sortByMainTimerTime() { + QueryBuilder sortByRingtoneName() { return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'mainTimerTime', Sort.asc); + return query.addSortBy(r'ringtoneName', Sort.asc); }); } - QueryBuilder sortByMainTimerTimeDesc() { + QueryBuilder sortByRingtoneNameDesc() { return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'mainTimerTime', Sort.desc); + return query.addSortBy(r'ringtoneName', Sort.desc); }); } - QueryBuilder sortByRingtoneName() { + QueryBuilder sortByStartedOn() { return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'ringtoneName', Sort.asc); + return query.addSortBy(r'startedOn', Sort.asc); }); } - QueryBuilder sortByRingtoneNameDesc() { + QueryBuilder sortByStartedOnDesc() { return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'ringtoneName', Sort.desc); + return query.addSortBy(r'startedOn', Sort.desc); }); } @@ -1158,34 +917,21 @@ extension TimerModelQuerySortBy }); } - QueryBuilder sortByTimerTime() { + QueryBuilder sortByTimerValue() { return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'timerTime', Sort.asc); + return query.addSortBy(r'timerValue', Sort.asc); }); } - QueryBuilder sortByTimerTimeDesc() { + QueryBuilder sortByTimerValueDesc() { return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'timerTime', Sort.desc); + return query.addSortBy(r'timerValue', Sort.desc); }); } } extension TimerModelQuerySortThenBy on QueryBuilder { - QueryBuilder thenByIntervalToAlarm() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'intervalToAlarm', Sort.asc); - }); - } - - QueryBuilder - thenByIntervalToAlarmDesc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'intervalToAlarm', Sort.desc); - }); - } - QueryBuilder thenByIsPaused() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'isPaused', Sort.asc); @@ -1198,27 +944,27 @@ extension TimerModelQuerySortThenBy }); } - QueryBuilder thenByMainTimerTime() { + QueryBuilder thenByRingtoneName() { return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'mainTimerTime', Sort.asc); + return query.addSortBy(r'ringtoneName', Sort.asc); }); } - QueryBuilder thenByMainTimerTimeDesc() { + QueryBuilder thenByRingtoneNameDesc() { return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'mainTimerTime', Sort.desc); + return query.addSortBy(r'ringtoneName', Sort.desc); }); } - QueryBuilder thenByRingtoneName() { + QueryBuilder thenByStartedOn() { return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'ringtoneName', Sort.asc); + return query.addSortBy(r'startedOn', Sort.asc); }); } - QueryBuilder thenByRingtoneNameDesc() { + QueryBuilder thenByStartedOnDesc() { return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'ringtoneName', Sort.desc); + return query.addSortBy(r'startedOn', Sort.desc); }); } @@ -1258,52 +1004,44 @@ extension TimerModelQuerySortThenBy }); } - QueryBuilder thenByTimerTime() { + QueryBuilder thenByTimerValue() { return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'timerTime', Sort.asc); + return query.addSortBy(r'timerValue', Sort.asc); }); } - QueryBuilder thenByTimerTimeDesc() { + QueryBuilder thenByTimerValueDesc() { return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'timerTime', Sort.desc); + return query.addSortBy(r'timerValue', Sort.desc); }); } } extension TimerModelQueryWhereDistinct on QueryBuilder { - QueryBuilder distinctByIntervalToAlarm() { - return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(r'intervalToAlarm'); - }); - } - QueryBuilder distinctByIsPaused() { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'isPaused'); }); } - QueryBuilder distinctByMainTimerTime( + QueryBuilder distinctByRingtoneName( {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(r'mainTimerTime', - caseSensitive: caseSensitive); + return query.addDistinctBy(r'ringtoneName', caseSensitive: caseSensitive); }); } - QueryBuilder distinctByRingtoneName( + QueryBuilder distinctByStartedOn( {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(r'ringtoneName', caseSensitive: caseSensitive); + return query.addDistinctBy(r'startedOn', caseSensitive: caseSensitive); }); } - QueryBuilder distinctByTimeElapsed( - {bool caseSensitive = true}) { + QueryBuilder distinctByTimeElapsed() { return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(r'timeElapsed', caseSensitive: caseSensitive); + return query.addDistinctBy(r'timeElapsed'); }); } @@ -1314,10 +1052,9 @@ extension TimerModelQueryWhereDistinct }); } - QueryBuilder distinctByTimerTime( - {bool caseSensitive = true}) { + QueryBuilder distinctByTimerValue() { return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(r'timerTime', caseSensitive: caseSensitive); + return query.addDistinctBy(r'timerValue'); }); } } @@ -1330,31 +1067,25 @@ extension TimerModelQueryProperty }); } - QueryBuilder intervalToAlarmProperty() { - return QueryBuilder.apply(this, (query) { - return query.addPropertyName(r'intervalToAlarm'); - }); - } - QueryBuilder isPausedProperty() { return QueryBuilder.apply(this, (query) { return query.addPropertyName(r'isPaused'); }); } - QueryBuilder mainTimerTimeProperty() { + QueryBuilder ringtoneNameProperty() { return QueryBuilder.apply(this, (query) { - return query.addPropertyName(r'mainTimerTime'); + return query.addPropertyName(r'ringtoneName'); }); } - QueryBuilder ringtoneNameProperty() { + QueryBuilder startedOnProperty() { return QueryBuilder.apply(this, (query) { - return query.addPropertyName(r'ringtoneName'); + return query.addPropertyName(r'startedOn'); }); } - QueryBuilder timeElapsedProperty() { + QueryBuilder timeElapsedProperty() { return QueryBuilder.apply(this, (query) { return query.addPropertyName(r'timeElapsed'); }); @@ -1366,9 +1097,9 @@ extension TimerModelQueryProperty }); } - QueryBuilder timerTimeProperty() { + QueryBuilder timerValueProperty() { return QueryBuilder.apply(this, (query) { - return query.addPropertyName(r'timerTime'); + return query.addPropertyName(r'timerValue'); }); } } diff --git a/lib/app/data/providers/isar_provider.dart b/lib/app/data/providers/isar_provider.dart index e71a8fe9..d023a8c4 100644 --- a/lib/app/data/providers/isar_provider.dart +++ b/lib/app/data/providers/isar_provider.dart @@ -40,9 +40,9 @@ class IsarDb { await db.execute(''' create table timers ( id integer primary key autoincrement, - timerTime text not null, - mainTimerTime text not null, - intervalToAlarm integer not null, + startedOn text not null, + timerValue integer not null, + timeElapsed integer not null, ringtoneName text not null, timerName text not null, isPaused integer not null) @@ -373,19 +373,37 @@ class IsarDb { final sql = await IsarDb().getTimerSQLiteDatabase(); List> maps = await sql!.query('timers', columns: [ 'id', - 'timerTime', - 'mainTimerTime', - 'intervalToAlarm', + 'startedOn', + 'timerValue', + 'timeElapsed', 'ringtoneName', 'timerName', - 'isPaused' + 'isPaused', ]); if (maps.length > 0) { return maps.map((timer) => TimerModel.fromMap(timer)).toList(); } return []; } - + static Future updateTimerTick(int id, TimerModel timer) async { + final isarProvider = IsarDb(); + final db = await isarProvider.db; + await db.writeTxn(() async { + await db.timerModels.put(timer); + }); + final sql = await IsarDb().getTimerSQLiteDatabase(); + await sql! + .update( + 'timers', + {'timeElapsed': timer.timeElapsed}, + where: 'id = ?', + whereArgs: [id], + ) + .then((value) { + sql.close(); + return value; + }); + } static Stream> getTimers() { final isarProvider = IsarDb(); final controller = StreamController>.broadcast(); diff --git a/lib/app/modules/timer/controllers/timer_controller.dart b/lib/app/modules/timer/controllers/timer_controller.dart index b17d4f3c..fdfa785e 100644 --- a/lib/app/modules/timer/controllers/timer_controller.dart +++ b/lib/app/modules/timer/controllers/timer_controller.dart @@ -12,8 +12,7 @@ import 'package:ultimate_alarm_clock/app/data/providers/secure_storage_provider. import 'package:ultimate_alarm_clock/app/utils/utils.dart'; import 'package:uuid/uuid.dart'; -class TimerController extends GetxController - with WidgetsBindingObserver, GetTickerProviderStateMixin { +class TimerController extends GetxController with WidgetsBindingObserver { MethodChannel timerChannel = const MethodChannel('timer'); final initialTime = DateTime(0, 0, 0, 0, 1, 0).obs; final remainingTime = const Duration(hours: 0, minutes: 0, seconds: 0).obs; @@ -22,14 +21,13 @@ class TimerController extends GetxController RxBool isTimerPaused = false.obs; RxBool isTimerRunning = false.obs; RxBool isbottom = false.obs; - Rx countdownTimer = Rx(null); - RxInt timerCount = 0.obs; Stream? isarTimers; ScrollController scrollController = ScrollController(); RxList timers = [].obs; RxList pausedTimers = [].obs; + RxList timeElapsedList = [].obs; RxList timerAnimationControllerList = [].obs; - RxList timeLeft = [].obs; + late AnimationController _controller; getFakeTimerModel() async { @@ -39,13 +37,11 @@ class TimerController extends GetxController updateTimerInfo() async { timerList.value = await IsarDb.getAllTimers(); - timerCount.value = await IsarDb.getNumberOfTimers(); } initializeTempTimerVariables() { pausedTimers.value = timerList.map((pause) => pause.isPaused).toList(); - timeLeft.value = timerList.map((time) => time.intervalToAlarm).toList(); - + timeElapsedList.value = timerList.map((time) => time.timeElapsed).toList(); } void toggleAnimation(int index) { @@ -132,7 +128,6 @@ class TimerController extends GetxController minutes: minutes, seconds: seconds, ); - startTimer(); } else { stopTimer(); } @@ -154,53 +149,36 @@ class TimerController extends GetxController void createTimer() async { TimerModel timerRecord = await getFakeTimerModel(); - print("${DateTime.now().toString()}"); - print("${DateTime.now().add(remainingTime.value).toString()}"); - timerRecord.timerTime = Utils.formatDateTimeToHHMMSS( - DateTime.now().add(remainingTime.value), - ); - timerRecord.mainTimerTime = Utils.formatDateTimeToHHMMSS( - DateTime.now().add(remainingTime.value), + + timerRecord.startedOn = Utils.formatDateTimeToHHMMSS( + DateTime.now(), ); - timerRecord.intervalToAlarm = Utils.getMillisecondsToAlarm( + timerRecord.timerValue = Utils.getMillisecondsToAlarm( DateTime.now(), DateTime.now().add(remainingTime.value), ); timerRecord.ringtoneName = 'Default'; + timerRecord.timerName = + "${Utils.formatMilliseconds(timerRecord.timerValue)} Timer"; + IsarDb.insertTimer(timerRecord).then((value) { updateTimerInfo(); + initializeTempTimerVariables(); + timerList.forEach((timer) { + print('Timer ID: ${timer.timerId}'); + print('Main Timer Time: ${timer.startedOn}'); + print('Interval to Alarm: ${timer.timerValue}'); + print('Ringtone Name: ${timer.ringtoneName}'); + print('Timer Name: ${timer.timerName}'); + print('Is Paused: ${timer.isPaused}'); + }); Get.back(); }); - timerRecord.timerName = "${timerRecord.timerTime} Timer"; - timerList.forEach((timer) { - print('Timer ID: ${timer.timerId}'); - print('Timer Time: ${timer.timerTime}'); - print('Main Timer Time: ${timer.mainTimerTime}'); - print('Interval to Alarm: ${timer.intervalToAlarm}'); - print('Ringtone Name: ${timer.ringtoneName}'); - print('Timer Name: ${timer.timerName}'); - print('Is Paused: ${timer.isPaused}'); - }); - } - void startTimer() async { - if (remainingTime.value.inSeconds > 0) { - final now = DateTime.now(); - startTime.value = now.millisecondsSinceEpoch; - isTimerRunning.value = true; - isTimerPaused.value = false; - - saveTimerStateToStorage(); - - countdownTimer.value = Timer.periodic( - const Duration(seconds: 1), - (_) => setCountDown(), - ); - } } scheduleTimer(TimerModel timerRecord) async { - DateTime? timerDateTime = Utils.stringToDateTime(timerRecord.timerTime); + DateTime? timerDateTime = Utils.stringToDateTime(timerRecord.startedOn); if (timerDateTime != null) { await timerChannel.invokeMethod('cancelTimer'); int intervaltoTimer = Utils.getMillisecondsToTimer( @@ -218,6 +196,7 @@ class TimerController extends GetxController deleteTimer(int id) async { await IsarDb.deleteTimer(id).then((value) => updateTimerInfo()); + updateTimerInfo(); } cancelTimer() async { @@ -225,7 +204,6 @@ class TimerController extends GetxController } void stopTimer() async { - countdownTimer.value?.cancel(); isTimerPaused.value = false; isTimerRunning.value = false; initialTime.value = DateTime(0, 0, 0, 0, 1, 0); @@ -243,7 +221,6 @@ class TimerController extends GetxController } void pauseTimer() async { - countdownTimer.value?.cancel(); isTimerPaused.value = true; saveTimerStateToStorage(); @@ -252,18 +229,6 @@ class TimerController extends GetxController await IsarDb.deleteAlarm(timerId); } - void resumeTimer() async { - if (isTimerPaused.value) { - countdownTimer.value = Timer.periodic( - const Duration(seconds: 1), - (_) => setCountDown(), - ); - isTimerPaused.value = false; - - createTimer(); - } - } - void setCountDown() { const reduceSecondsBy = 1; final seconds = remainingTime.value.inSeconds - reduceSecondsBy; diff --git a/lib/app/modules/timer/views/timer_animation.dart b/lib/app/modules/timer/views/timer_animation.dart index 00a4c532..621e4eaa 100644 --- a/lib/app/modules/timer/views/timer_animation.dart +++ b/lib/app/modules/timer/views/timer_animation.dart @@ -4,6 +4,7 @@ import 'dart:math' as math; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:ultimate_alarm_clock/app/data/models/timer_model.dart'; +import 'package:ultimate_alarm_clock/app/data/providers/isar_provider.dart'; import 'package:ultimate_alarm_clock/app/modules/timer/controllers/timer_controller.dart'; import 'package:ultimate_alarm_clock/app/utils/constants.dart'; @@ -11,66 +12,59 @@ import '../../../utils/utils.dart'; import '../../settings/controllers/theme_controller.dart'; class TimerAnimatedCard extends StatefulWidget { - TimerModel timer; - int index; + final TimerModel timer; - TimerAnimatedCard(this.timer, this.index); + const TimerAnimatedCard(this.timer, {super.key}); @override _TimerAnimatedCardState createState() => _TimerAnimatedCardState(); } class _TimerAnimatedCardState extends State with SingleTickerProviderStateMixin, AutomaticKeepAliveClientMixin { - late AnimationController _controller; TimerController controller = Get.find(); ThemeController themeController = Get.find(); bool isPlaying = false; - Timer? timerCounter; - late int milliseconds; - late int progressCounter; + Timer? _timerCounter; + late int _progressCounter; void startTimer() { - milliseconds = widget.timer.intervalToAlarm; - timerCounter = Timer.periodic(Duration(seconds: 1), (timer) { - if (milliseconds > 0) { + _timerCounter = Timer.periodic(Duration(seconds: 1), (timer) { + print("${widget.timer.timerName}"); + if (widget.timer.timeElapsed < widget.timer.timerValue) { setState(() { - milliseconds -= 1000; - progressCounter += 1000; + widget.timer.timeElapsed += 1000; + _progressCounter += 1000; + IsarDb.updateTimerTick(widget.timer.timerId, widget.timer); }); } }); } void stopTimer() { - timerCounter!.cancel(); + _timerCounter!.cancel(); } @override void initState() { super.initState(); - progressCounter = 0; - _controller = AnimationController( - vsync: this, - duration: Duration( - milliseconds: widget.timer - .intervalToAlarm), // Replace x with your duration in milliseconds - ); - if (controller.pausedTimers.value[widget.index] == 0) { + setState(() { + _progressCounter = 0; + }); + + if (widget.timer.isPaused == 0) { startTimer(); - _controller.forward(); } } @override void dispose() { - _controller.dispose(); + _timerCounter!.cancel(); super.dispose(); } void _toggleAnimation() { setState(() { isPlaying = !isPlaying; - isPlaying ? _controller.forward() : _controller.stop(); }); } @@ -102,7 +96,7 @@ class _TimerAnimatedCardState extends State duration: Duration(milliseconds: 1000), height: context.height / 3.3, width: context.width * - (progressCounter / (widget.timer.intervalToAlarm)), + (_progressCounter / (widget.timer.timerValue)), ), Center( child: Padding( @@ -145,7 +139,7 @@ class _TimerAnimatedCardState extends State color: kprimaryBackgroundColor, borderRadius: BorderRadius.circular(20)), - child: const Padding( + child: const Padding( padding: EdgeInsets.all(4.0), child: Icon( Icons.close, @@ -157,56 +151,57 @@ class _TimerAnimatedCardState extends State ], ), Padding( - padding: const EdgeInsets.symmetric(vertical: 40), + padding: const EdgeInsets.symmetric(vertical: 20), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - Utils.formatMilliseconds(milliseconds), - style: Theme.of( - context, - ).textTheme.displayLarge!.copyWith( - color: - themeController.isLightMode.value - ? kLightPrimaryTextColor - : kprimaryTextColor, - fontSize: 50, - ), + AnimatedContainer( + duration: Duration(seconds: 1), + child: Text( + "${Utils.formatMilliseconds(widget.timer.timerValue - widget.timer.timeElapsed)}", + style: Theme.of( + context, + ).textTheme.displayLarge!.copyWith( + color: themeController + .isLightMode.value + ? kLightPrimaryTextColor + : kprimaryTextColor, + fontSize: 50, + ), + ), ), InkWell( customBorder: RoundedRectangleBorder( borderRadius: BorderRadius.circular(80), ), onTap: () { - controller.toggleAnimation(widget.index); + setState(() { + widget.timer.isPaused == 0 + ? stopTimer() + : startTimer(); + widget.timer.isPaused = + widget.timer.isPaused == 0 ? 1 : 0; + }); }, child: Padding( - padding: EdgeInsets.all(25.0), + padding: EdgeInsets.all(20), child: GestureDetector( onTap: _toggleAnimation, - child: AnimatedBuilder( - animation: _controller, - builder: (context, child) { - return CustomPaint( - painter: CirclePainter( - _controller.value), - child: Container( - width: 50, - height: 50, - child: Icon( - controller.pausedTimers[ - widget.index] == - 0 - ? Icons.pause - : Icons.play_arrow, - size: 30, - color: - kLightPrimaryDisabledTextColor, - ), - ), - ); - }, + child: Container( + decoration: BoxDecoration( + color: kprimaryColor, + borderRadius: + BorderRadius.circular(80)), + width: 80, + height: 80, + child: Icon( + widget.timer.isPaused == 0 + ? Icons.pause + : Icons.play_arrow, + size: 30, + color: ksecondaryBackgroundColor, + ), ), )), ) @@ -231,33 +226,3 @@ class _TimerAnimatedCardState extends State // TODO: implement wantKeepAlive bool get wantKeepAlive => true; } - -class CirclePainter extends CustomPainter { - final double progress; - - CirclePainter(this.progress); - - @override - void paint(Canvas canvas, Size size) { - Paint paint = Paint() - ..color = kprimaryColor - ..strokeWidth = 50 - ..style = PaintingStyle.stroke; - - double angle = 2 * math.pi * progress; - - canvas.drawArc( - Rect.fromCenter( - center: Offset(size.width / 2, size.height / 2), - width: size.width / 2, - height: size.height / 2), - -math.pi / 2, - angle, - false, - paint, - ); - } - - @override - bool shouldRepaint(CustomPainter oldDelegate) => true; -} diff --git a/lib/app/modules/timer/views/timer_view.dart b/lib/app/modules/timer/views/timer_view.dart index e9c502bc..219240e1 100644 --- a/lib/app/modules/timer/views/timer_view.dart +++ b/lib/app/modules/timer/views/timer_view.dart @@ -65,12 +65,12 @@ class TimerView extends GetView { ], ), ), - body: Obx(() => controller.timerCount.value == 0 + body: Obx(() => controller.timerList.value.length == 0 ? addATimerSpace(context) : StreamBuilder( stream: IsarDb.getTimers(), builder: (context, snapshot) { - if (!snapshot.hasData) { + if (!snapshot.hasData && snapshot.data!=[]) { return const Center( child: CircularProgressIndicator.adaptive( backgroundColor: Colors.transparent, @@ -81,21 +81,18 @@ class TimerView extends GetView { ); } else { // list of pause values of timers - List? timerList = snapshot.data; - controller.timerList.value = timerList!; controller.initializeTempTimerVariables(); - - - return ListView.builder( + List? listOfTimers = snapshot.data; + return ListView.builder( controller: controller.scrollController, shrinkWrap: true, scrollDirection: Axis.vertical, - itemCount: timerList.length, + itemCount: snapshot.data!.length, itemBuilder: (BuildContext context, int index) { return Column( children: [ - TimerAnimatedCard(timerList[index], index), - if (index == timerList.length - 1) + TimerAnimatedCard(listOfTimers![index]), + if (index == snapshot.data!.length - 1) Padding( padding: const EdgeInsets.all(8.0), child: addATimerSpace(context), diff --git a/lib/app/utils/utils.dart b/lib/app/utils/utils.dart index 57cc0d21..987d9c97 100644 --- a/lib/app/utils/utils.dart +++ b/lib/app/utils/utils.dart @@ -379,10 +379,10 @@ class Utils { // Utility function to create a dummy model to pass to functions static Future genFakeTimerModel() async { return TimerModel( - intervalToAlarm: 0, - mainTimerTime: '', + timerValue: 0, + timeElapsed :0, + startedOn: '', ringtoneName: '', - timerTime: Utils.timeOfDayToString(TimeOfDay.now()), timerName: '', ); } From 330017843ac63ceda6e21ef4a48be7c7fe2a628b Mon Sep 17 00:00:00 2001 From: AryanSarafDev Date: Sun, 26 May 2024 05:44:05 +0530 Subject: [PATCH 3/5] timer revamp complete --- android/app/src/main/AndroidManifest.xml | 13 + .../ultimate_alarm_clock/BootReceiver.kt | 28 +- .../CommonTimerManager.kt | 39 +++ .../ultimate_alarm_clock/GetLatestAlarm.kt | 13 +- .../ultimate_alarm_clock/MainActivity.kt | 59 +++- .../TimerDatabaseHelper.kt | 17 + .../ultimate_alarm_clock/TimerFunctions.kt | 75 +++++ .../ultimate_alarm_clock/TimerNotification.kt | 98 ++++++ .../ultimate_alarm_clock/TimerService.kt | 7 + lib/app/data/providers/isar_provider.dart | 59 ++-- .../bottom_navigation_bar_controller.dart | 8 - .../home/controllers/home_controller.dart | 2 +- .../timer/controllers/timer_controller.dart | 203 ++++-------- .../modules/timer/views/timer_animation.dart | 307 +++++++++++------- lib/app/modules/timer/views/timer_view.dart | 99 ++++-- lib/app/utils/utils.dart | 11 + lib/main.dart | 7 +- 17 files changed, 657 insertions(+), 388 deletions(-) create mode 100644 android/app/src/main/kotlin/com/example/ultimate_alarm_clock/CommonTimerManager.kt create mode 100644 android/app/src/main/kotlin/com/example/ultimate_alarm_clock/TimerDatabaseHelper.kt create mode 100644 android/app/src/main/kotlin/com/example/ultimate_alarm_clock/TimerFunctions.kt create mode 100644 android/app/src/main/kotlin/com/example/ultimate_alarm_clock/TimerNotification.kt create mode 100644 android/app/src/main/kotlin/com/example/ultimate_alarm_clock/TimerService.kt diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 5879725c..3c7f8a1b 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -4,6 +4,7 @@ + @@ -37,6 +38,16 @@ + + + + + + + + + ? { + + val cursor = db.rawQuery( + """ + SELECT id, timerValue, timeElapsed, timerName, startedOn + FROM timers + WHERE isPaused = 0 + AND DATETIME(startedOn, '+' || CAST(timerValue / 1000 AS TEXT) || ' seconds') > DATETIME('now') + ORDER BY (timerValue - timeElapsed) ASC + LIMIT 1; + """, null + ) + + + return if (cursor.moveToFirst()) { + val timer = TimerModel.fromCursor(cursor) + cursor.close() + val intervalToTimer = isFutureDatetimeWithMillis(timer.startedOn,timer.timerValue.toLong()) + Triple(timer.id, intervalToTimer.toLong(),timer.timerName) + + } else + { + cursor.close() + null + } +} +fun pauseTimer(db: SQLiteDatabase, timerId: Int): Boolean { + val contentValues = ContentValues() + contentValues.put("isPaused", 1) + + val rowsAffected = db.update("timers", contentValues, "id = ?", arrayOf(timerId.toString())) + + return rowsAffected > 0 +} +fun isFutureDatetimeWithMillis(datetimeString: String, milliseconds: Long): Long { + try { + val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSS") + val providedDatetime = dateFormat.parse(datetimeString) + val updatedDatetime = Date(providedDatetime.time + milliseconds) + val currentDatetime = Date() + + return if (updatedDatetime.after(currentDatetime)) { + updatedDatetime.time - currentDatetime.time + } else { + 0 + } + } catch (e: Exception) { + // Handle invalid datetime format (e.g., parsing error) + return 0 + } +} + + + +private data class TimerModel(val id: Int, val timerValue: Int, val timeElapsed : Int, val timerName: String, val startedOn: String) { + companion object { + fun fromCursor(cursor: Cursor): TimerModel { + val id = cursor.getInt(cursor.getColumnIndex("id")) + val timerValue = cursor.getInt(cursor.getColumnIndex("timerValue")) + val timeElapsed = cursor.getInt(cursor.getColumnIndex("timeElapsed")) + val timerName = cursor.getString(cursor.getColumnIndex("timerName")) + val startedOn = cursor.getString(cursor.getColumnIndex("startedOn")) + return TimerModel(id, timerValue, timeElapsed,timerName,startedOn) + } + } +} diff --git a/android/app/src/main/kotlin/com/example/ultimate_alarm_clock/TimerNotification.kt b/android/app/src/main/kotlin/com/example/ultimate_alarm_clock/TimerNotification.kt new file mode 100644 index 00000000..c9dc6d51 --- /dev/null +++ b/android/app/src/main/kotlin/com/example/ultimate_alarm_clock/TimerNotification.kt @@ -0,0 +1,98 @@ +package com.example.ultimate_alarm_clock +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.PendingIntent +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.os.Build +import androidx.core.app.NotificationCompat + +class TimerNotification: BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + val timerdbhelper = TimerDatabaseHelper(context) + val timerdb = timerdbhelper.readableDatabase + val time = getLatestTimer(timerdb) + var notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + val commonTimer = CommonTimerManager.getCommonTimer(object : TimerListener { + override fun onTick(millisUntilFinished: Long) { + println(millisUntilFinished) + showTimerNotification(millisUntilFinished,"Timer",context) + + } + + override fun onFinish() { + notificationManager.cancel(1) + } + }) + + if(intent.action =="com.example.ultimate_alarm_clock.START_TIMERNOTIF" || intent.action == Intent.ACTION_BOOT_COMPLETED ) + { + createNotificationChannel(context) + + + + if (time!=null){ + + // Start or stop the timer based on your requirements + commonTimer.startTimer(time.second) + + } + + } + if(intent.action=="com.example.ultimate_alarm_clock.STOP_TIMERNOTIF"){ + + commonTimer.stopTimer() + + } + + + } +private fun createNotificationChannel(context: Context) { + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val channel = NotificationChannel( + TimerService.TIMER_CHANNEL_ID, + "Timer Channel", + NotificationManager.IMPORTANCE_DEFAULT + ) + val notificationManager = + context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + notificationManager.createNotificationChannel(channel) + } + } + + + private fun showTimerNotification(milliseconds: Long,timerName:String,context: Context){ + var notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + val deleteIntent = Intent(context,TimerNotification::class.java) + deleteIntent.action = "com.example.ultimate_alarm_clock.STOP_TIMERNOTIF" + val deletePendingIntent = PendingIntent.getBroadcast(context, 5, deleteIntent, + PendingIntent.FLAG_IMMUTABLE) + val notification = NotificationCompat.Builder(context, TimerService.TIMER_CHANNEL_ID) + .setSmallIcon(R.mipmap.launcher_icon) + .setContentText("$timerName") + .setContentText(formatDuration(milliseconds)) + .setOnlyAlertOnce(true) + .setDeleteIntent(deletePendingIntent) + . + build() + notificationManager.notify(1,notification) + } + + private fun formatDuration(milliseconds: Long): String { + val seconds = (milliseconds / 1000) % 60 + val minutes = (milliseconds / (1000 * 60)) % 60 + val hours = (milliseconds / (1000 * 60 * 60)) % 24 + + return if (hours > 0) { + String.format("%02d:%02d:%02d", hours, minutes, seconds) + } else { + String.format("%02d:%02d", minutes, seconds) + } + } + + + + +} \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/example/ultimate_alarm_clock/TimerService.kt b/android/app/src/main/kotlin/com/example/ultimate_alarm_clock/TimerService.kt new file mode 100644 index 00000000..e5371589 --- /dev/null +++ b/android/app/src/main/kotlin/com/example/ultimate_alarm_clock/TimerService.kt @@ -0,0 +1,7 @@ +package com.example.ultimate_alarm_clock + +class TimerService { + companion object{ + val TIMER_CHANNEL_ID = "Timer Channel" + } +} \ No newline at end of file diff --git a/lib/app/data/providers/isar_provider.dart b/lib/app/data/providers/isar_provider.dart index d023a8c4..d4d70f89 100644 --- a/lib/app/data/providers/isar_provider.dart +++ b/lib/app/data/providers/isar_provider.dart @@ -250,10 +250,7 @@ class IsarDb { alarmRecord.toSQFliteMap(), where: 'alarmID = ?', whereArgs: [alarmRecord.alarmID], - ).then((value) { - sql.close(); - return value; - }); + ); } static Future getAlarm(int id) async { @@ -285,10 +282,7 @@ class IsarDb { 'alarms', where: 'alarmID = ?', whereArgs: [tobedeleted!.alarmID], - ).then((value) { - sql.close(); - return value; - }); + ); } // Timer Functions @@ -332,26 +326,7 @@ class IsarDb { where: 'id = ?', whereArgs: [id], ) - .then((value) { - sql.close(); - return value; - }); - } - - static Future updateIsPaused(int id, int newIsPaused) async { - final sql = await IsarDb().getTimerSQLiteDatabase(); - - return await sql! - .update( - 'timers', - {'isPaused': newIsPaused}, - where: 'id = ?', - whereArgs: [id], - ) - .then((value) { - sql.close(); - return value; - }); + ; } static Future deleteTimer(int id) async { @@ -385,7 +360,8 @@ class IsarDb { } return []; } - static Future updateTimerTick(int id, TimerModel timer) async { + + static Future updateTimerTick(TimerModel timer) async { final isarProvider = IsarDb(); final db = await isarProvider.db; await db.writeTxn(() async { @@ -397,12 +373,9 @@ class IsarDb { 'timers', {'timeElapsed': timer.timeElapsed}, where: 'id = ?', - whereArgs: [id], + whereArgs: [timer.timerId], ) - .then((value) { - sql.close(); - return value; - }); + ; } static Stream> getTimers() { final isarProvider = IsarDb(); @@ -423,6 +396,24 @@ class IsarDb { return controller.stream; } + static Future updateTimerPauseStatus(TimerModel timer) async { + final isarProvider = IsarDb(); + final db = await isarProvider.db; + await db.writeTxn(() async { + await db.timerModels.put(timer); + }); + final sql = await IsarDb().getTimerSQLiteDatabase(); + await sql! + .update( + 'timers', + {'isPaused': timer.isPaused}, + where: 'id = ?', + whereArgs: [timer.timerId], + ); + + } + + static Future getNumberOfTimers() async { final sql = await IsarDb().getTimerSQLiteDatabase(); List> x = diff --git a/lib/app/modules/bottomNavigationBar/controllers/bottom_navigation_bar_controller.dart b/lib/app/modules/bottomNavigationBar/controllers/bottom_navigation_bar_controller.dart index 4a8815db..ce4b548e 100644 --- a/lib/app/modules/bottomNavigationBar/controllers/bottom_navigation_bar_controller.dart +++ b/lib/app/modules/bottomNavigationBar/controllers/bottom_navigation_bar_controller.dart @@ -3,19 +3,15 @@ import 'package:get/get.dart'; import 'package:ultimate_alarm_clock/app/data/providers/secure_storage_provider.dart'; import 'package:ultimate_alarm_clock/app/modules/home/views/home_view.dart'; import 'package:ultimate_alarm_clock/app/modules/stopwatch/views/stopwatch_view.dart'; -import 'package:ultimate_alarm_clock/app/modules/timer/controllers/timer_controller.dart'; import 'package:ultimate_alarm_clock/app/modules/timer/views/timer_view.dart'; class BottomNavigationBarController extends GetxController with WidgetsBindingObserver { RxInt activeTabIndex = 0.obs; - RxBool isTimerRunning = false.obs; RxBool hasloaded = false.obs; final _secureStorageProvider = SecureStorageProvider(); - TimerController timerController = Get.find(); - List pages = [ HomeView(), StopwatchView(), @@ -51,9 +47,5 @@ class BottomNavigationBarController extends GetxController activeTabIndex.value = index; _saveState(); - if (index == 0 && - (timerController.isTimerRunning.value || isTimerRunning.value)) { - timerController.saveTimerStateToStorage(); - } } } diff --git a/lib/app/modules/home/controllers/home_controller.dart b/lib/app/modules/home/controllers/home_controller.dart index 937850ec..27a27848 100644 --- a/lib/app/modules/home/controllers/home_controller.dart +++ b/lib/app/modules/home/controllers/home_controller.dart @@ -25,7 +25,7 @@ class Pair { Pair(this.first, this.second); } -class HomeController extends GetxController { +class HomeController extends GetxController { MethodChannel alarmChannel = const MethodChannel('ulticlock'); Stream? firestoreStreamAlarms; diff --git a/lib/app/modules/timer/controllers/timer_controller.dart b/lib/app/modules/timer/controllers/timer_controller.dart index fdfa785e..002f241b 100644 --- a/lib/app/modules/timer/controllers/timer_controller.dart +++ b/lib/app/modules/timer/controllers/timer_controller.dart @@ -1,18 +1,12 @@ import 'dart:async'; - import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; -import 'package:rxdart/rxdart.dart' as rx; -import 'package:isar/isar.dart'; -import 'package:ultimate_alarm_clock/app/data/models/alarm_model.dart'; import 'package:ultimate_alarm_clock/app/data/models/timer_model.dart'; import 'package:ultimate_alarm_clock/app/data/providers/isar_provider.dart'; -import 'package:ultimate_alarm_clock/app/data/providers/secure_storage_provider.dart'; import 'package:ultimate_alarm_clock/app/utils/utils.dart'; -import 'package:uuid/uuid.dart'; -class TimerController extends GetxController with WidgetsBindingObserver { +class TimerController extends FullLifeCycleController with FullLifeCycleMixin { MethodChannel timerChannel = const MethodChannel('timer'); final initialTime = DateTime(0, 0, 0, 0, 1, 0).obs; final remainingTime = const Duration(hours: 0, minutes: 0, seconds: 0).obs; @@ -24,9 +18,7 @@ class TimerController extends GetxController with WidgetsBindingObserver { Stream? isarTimers; ScrollController scrollController = ScrollController(); RxList timers = [].obs; - RxList pausedTimers = [].obs; - RxList timeElapsedList = [].obs; - RxList timerAnimationControllerList = [].obs; + RxList isRinging = [].obs; late AnimationController _controller; @@ -37,26 +29,19 @@ class TimerController extends GetxController with WidgetsBindingObserver { updateTimerInfo() async { timerList.value = await IsarDb.getAllTimers(); - } - - initializeTempTimerVariables() { - pausedTimers.value = timerList.map((pause) => pause.isPaused).toList(); - timeElapsedList.value = timerList.map((time) => time.timeElapsed).toList(); - } - - void toggleAnimation(int index) { - pausedTimers[index] == 0 - ? pausedTimers[index] = 1 - : pausedTimers[index] = 0; - - print(pausedTimers); + for (var timer in timerList) { + print('Timer ID: ${timer.timerId}'); + print('Started on: ${timer.startedOn}'); + print('timer value: ${timer.timerValue}'); + print('Ringtone Name: ${timer.ringtoneName}'); + print('Timer Name: ${timer.timerName}'); + print('Is Paused: ${timer.isPaused}'); + } } late int currentTimerIsarId; var hours = 0.obs, minutes = 1.obs, seconds = 0.obs; - final _secureStorageProvider = SecureStorageProvider(); - String strDigits(int n) => n.toString().padLeft(2, '0'); final RxList timerList = [].obs; @@ -65,7 +50,6 @@ class TimerController extends GetxController with WidgetsBindingObserver { Future onInit() async { super.onInit(); WidgetsBinding.instance.addObserver(this); - loadTimerStateFromStorage(); isarTimers = IsarDb.getTimers(); updateTimerInfo(); scrollController.addListener(() { @@ -83,115 +67,47 @@ class TimerController extends GetxController with WidgetsBindingObserver { super.onClose(); } - void saveTimerStateToStorage() async { - await _secureStorageProvider.writeRemainingTimeInSeconds( - remainingTimeInSeconds: remainingTime.value.inSeconds, - ); - await _secureStorageProvider.writeStartTime( - startTime: startTime.value, - ); - await _secureStorageProvider.writeIsTimerRunning( - isTimerRunning: isTimerRunning.value, - ); - await _secureStorageProvider.writeIsTimerPaused( - isTimerPaused: isTimerPaused.value, - ); + void startRinger(int id) async { + try { + isRinging.value.add(id); + print(isRinging.value); + if (isRinging.value.length == 1) { + await timerChannel.invokeMethod('playDefaultAlarm'); + } + } on PlatformException catch (e) { + print('Failed to schedule alarm: ${e.message}'); + } } - void loadTimerStateFromStorage() async { - final storedRemainingTimeInSeconds = - await _secureStorageProvider.readRemainingTimeInSeconds(); - final storedStartTime = await _secureStorageProvider.readStartTime(); - isTimerRunning.value = await _secureStorageProvider.readIsTimerRunning(); - isTimerPaused.value = await _secureStorageProvider.readIsTimerPaused(); - - if (storedRemainingTimeInSeconds != -1 && storedStartTime != -1) { - if (!isTimerPaused.value) { - final elapsedMilliseconds = - DateTime.now().millisecondsSinceEpoch - storedStartTime; - final elapsedSeconds = (elapsedMilliseconds / 1000).round(); - final updatedRemainingTimeInSeconds = - storedRemainingTimeInSeconds - elapsedSeconds; - - if (updatedRemainingTimeInSeconds > 0) { - // Update remaining time and start timer from the correct point - int hours = updatedRemainingTimeInSeconds ~/ - 3600; // Calculate the number of hours - int remainingSeconds = updatedRemainingTimeInSeconds % - 3600; // Calculate the remaining seconds - int minutes = - remainingSeconds ~/ 60; // Calculate the number of minutes - int seconds = - remainingSeconds % 60; // Calculate the number of seconds - remainingTime.value = Duration( - hours: hours, - minutes: minutes, - seconds: seconds, - ); - } else { - stopTimer(); - } - } else { - int hours = storedRemainingTimeInSeconds ~/ - 3600; // Calculate the number of hours - int remainingSeconds = storedRemainingTimeInSeconds % - 3600; // Calculate the remaining seconds - int minutes = remainingSeconds ~/ 60; // Calculate the number of minutes - int seconds = remainingSeconds % 60; // Calculate the number of seconds - remainingTime.value = Duration( - hours: hours, - minutes: minutes, - seconds: seconds, - ); + void stopRinger(int id) async { + try { + isRinging.value.remove(id); + print(isRinging.value); + if (isRinging.value.length == 0) { + await timerChannel.invokeMethod('stopDefaultAlarm'); } + } on PlatformException catch (e) { + print('Failed to schedule alarm: ${e.message}'); } } void createTimer() async { TimerModel timerRecord = await getFakeTimerModel(); - timerRecord.startedOn = Utils.formatDateTimeToHHMMSS( - DateTime.now(), - ); + timerRecord.startedOn = DateTime.now().toString(); + ; timerRecord.timerValue = Utils.getMillisecondsToAlarm( DateTime.now(), DateTime.now().add(remainingTime.value), ); timerRecord.ringtoneName = 'Default'; timerRecord.timerName = - "${Utils.formatMilliseconds(timerRecord.timerValue)} Timer"; + '${Utils.formatMilliseconds(timerRecord.timerValue)} Timer'; - IsarDb.insertTimer(timerRecord).then((value) { + IsarDb.insertTimer(timerRecord).then((value) async { updateTimerInfo(); - initializeTempTimerVariables(); - timerList.forEach((timer) { - print('Timer ID: ${timer.timerId}'); - print('Main Timer Time: ${timer.startedOn}'); - print('Interval to Alarm: ${timer.timerValue}'); - print('Ringtone Name: ${timer.ringtoneName}'); - print('Timer Name: ${timer.timerName}'); - print('Is Paused: ${timer.isPaused}'); - }); - Get.back(); }); - - } - - scheduleTimer(TimerModel timerRecord) async { - DateTime? timerDateTime = Utils.stringToDateTime(timerRecord.startedOn); - if (timerDateTime != null) { - await timerChannel.invokeMethod('cancelTimer'); - int intervaltoTimer = Utils.getMillisecondsToTimer( - DateTime.now(), - timerDateTime, - ); - try { - await timerChannel - .invokeMethod('scheduleTimer', {'milliSeconds': intervaltoTimer}); - } on PlatformException catch (e) { - print("Failed to schedule alarm: ${e.message}"); - } - } + Get.back(); } deleteTimer(int id) async { @@ -203,39 +119,34 @@ class TimerController extends GetxController with WidgetsBindingObserver { await timerChannel.invokeMethod('cancelTimer'); } - void stopTimer() async { - isTimerPaused.value = false; - isTimerRunning.value = false; - initialTime.value = DateTime(0, 0, 0, 0, 1, 0); - remainingTime.value = Duration( - hours: initialTime.value.hour, - minutes: initialTime.value.minute, - seconds: initialTime.value.second, - ); - currentTime.value = const Duration(hours: 0, minutes: 0, seconds: 0); - startTime.value = 0; - await _secureStorageProvider.removeRemainingTimeInSeconds(); - await _secureStorageProvider.removeStartTime(); - await _secureStorageProvider.writeIsTimerRunning(isTimerRunning: false); - await _secureStorageProvider.writeIsTimerPaused(isTimerPaused: false); + @override + void onDetached() {} + + @override + Future onHidden() async { + try { + await timerChannel.invokeMethod('runtimerNotif'); + Get.back(); + } on PlatformException catch (e) { + print('Failed to schedule alarm: ${e.message}'); + Get.back(); + } } - void pauseTimer() async { - isTimerPaused.value = true; + @override + onInactive() {} - saveTimerStateToStorage(); - await cancelTimer(); - int timerId = await SecureStorageProvider().readTimerId(); - await IsarDb.deleteAlarm(timerId); - } + @override + void onPaused() {} - void setCountDown() { - const reduceSecondsBy = 1; - final seconds = remainingTime.value.inSeconds - reduceSecondsBy; - if (seconds < 0) { - stopTimer(); - } else { - remainingTime.value = Duration(seconds: seconds); + @override + onResumed() async { + try { + await timerChannel.invokeMethod('clearTimerNotif'); + Get.back(); + } on PlatformException catch (e) { + print('Failed to schedule alarm: ${e.message}'); + Get.back(); } } } diff --git a/lib/app/modules/timer/views/timer_animation.dart b/lib/app/modules/timer/views/timer_animation.dart index 621e4eaa..0edd43ac 100644 --- a/lib/app/modules/timer/views/timer_animation.dart +++ b/lib/app/modules/timer/views/timer_animation.dart @@ -13,8 +13,13 @@ import '../../settings/controllers/theme_controller.dart'; class TimerAnimatedCard extends StatefulWidget { final TimerModel timer; + final int index; - const TimerAnimatedCard(this.timer, {super.key}); + const TimerAnimatedCard({ + super.key, + required this.index, + required this.timer, + }); @override _TimerAnimatedCardState createState() => _TimerAnimatedCardState(); } @@ -24,18 +29,18 @@ class _TimerAnimatedCardState extends State TimerController controller = Get.find(); ThemeController themeController = Get.find(); - bool isPlaying = false; Timer? _timerCounter; - late int _progressCounter; void startTimer() { _timerCounter = Timer.periodic(Duration(seconds: 1), (timer) { print("${widget.timer.timerName}"); if (widget.timer.timeElapsed < widget.timer.timerValue) { setState(() { widget.timer.timeElapsed += 1000; - _progressCounter += 1000; - IsarDb.updateTimerTick(widget.timer.timerId, widget.timer); + IsarDb.updateTimerTick(widget.timer); }); + } else { + stopTimer(); + controller.startRinger(widget.timer.timerId); } }); } @@ -47,10 +52,21 @@ class _TimerAnimatedCardState extends State @override void initState() { super.initState(); - setState(() { - _progressCounter = 0; - }); - + if (Utils.getDifferenceMillisFromNow( + widget.timer.startedOn, widget.timer.timerValue) <= + 0 && + widget.timer.isPaused == 0) { + widget.timer.isPaused = 1; + widget.timer.timeElapsed = 0; + IsarDb.updateTimerPauseStatus(widget.timer); + } else if (Utils.getDifferenceMillisFromNow( + widget.timer.startedOn, widget.timer.timerValue) < + widget.timer.timerValue && + widget.timer.isPaused == 0) { + widget.timer.timeElapsed =widget.timer.timerValue-Utils.getDifferenceMillisFromNow( + widget.timer.startedOn, widget.timer.timerValue); + IsarDb.updateTimerPauseStatus(widget.timer); + } if (widget.timer.isPaused == 0) { startTimer(); } @@ -62,12 +78,6 @@ class _TimerAnimatedCardState extends State super.dispose(); } - void _toggleAnimation() { - setState(() { - isPlaying = !isPlaying; - }); - } - @override Widget build(BuildContext context) { return Padding( @@ -79,115 +89,175 @@ class _TimerAnimatedCardState extends State width: context.width, child: Card( margin: const EdgeInsets.all(5), - color: themeController.isLightMode.value - ? kLightSecondaryBackgroundColor - : ksecondaryBackgroundColor, + color: widget.timer.timeElapsed < widget.timer.timerValue + ? themeController.isLightMode.value + ? kLightSecondaryBackgroundColor + : ksecondaryBackgroundColor + : themeController.isLightMode.value + ? kLightSecondaryColor + : ksecondaryColor, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular( 18, ), ), - child: Stack( - children: [ - AnimatedContainer( - decoration: BoxDecoration( - color: kprimaryDisabledTextColor.withOpacity(0.1), - borderRadius: BorderRadius.circular(18)), - duration: Duration(milliseconds: 1000), - height: context.height / 3.3, - width: context.width * - (_progressCounter / (widget.timer.timerValue)), - ), - Center( - child: Padding( - padding: const EdgeInsets.only( - left: 25.0, - right: 20.0, - top: 20.0, - bottom: 20.0, - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Expanded( - flex: 3, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - widget.timer.timerName, - overflow: TextOverflow.ellipsis, - // Set overflow property here - style: Theme.of( - context, - ).textTheme.bodySmall!.copyWith( - fontWeight: FontWeight.w500, - color: kprimaryColor, - fontSize: 18), - ), - InkWell( - onTap: () { - controller - .deleteTimer(widget.timer.timerId); - }, - child: Container( - decoration: BoxDecoration( - color: kprimaryBackgroundColor, - borderRadius: - BorderRadius.circular(20)), - child: const Padding( - padding: EdgeInsets.all(4.0), - child: Icon( - Icons.close, - size: 18, - ), - ), - ), - ) - ], - ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 20), - child: Row( + child: ClipRRect( + borderRadius: BorderRadius.circular(18), + child: Stack( + children: [ + AnimatedContainer( + decoration: BoxDecoration( + color: kprimaryDisabledTextColor.withOpacity(0.1), + borderRadius: BorderRadius.circular(18)), + duration: Duration(milliseconds: 1000), + height: context.height / 3.3, + width: context.width * + ((widget.timer.timeElapsed + 500) / + (widget.timer.timerValue)), + ), + Center( + child: Padding( + padding: const EdgeInsets.only( + left: 25.0, + right: 20.0, + top: 20.0, + bottom: 20.0, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Expanded( + flex: 3, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - AnimatedContainer( - duration: Duration(seconds: 1), - child: Text( - "${Utils.formatMilliseconds(widget.timer.timerValue - widget.timer.timeElapsed)}", - style: Theme.of( - context, - ).textTheme.displayLarge!.copyWith( - color: themeController - .isLightMode.value - ? kLightPrimaryTextColor - : kprimaryTextColor, - fontSize: 50, + Text( + widget.timer.timerName, + overflow: TextOverflow.ellipsis, + // Set overflow property here + style: Theme.of( + context, + ).textTheme.bodySmall!.copyWith( + fontWeight: FontWeight.w500, + color: kprimaryColor, + fontSize: 18), + ), + Spacer(), + Padding( + padding: const EdgeInsets.only(right: 16.0), + child: InkWell( + onTap: () { + setState(() { + if (_timerCounter != null && + widget.timer.isPaused == 0) { + stopTimer(); + } + widget.timer.timeElapsed = 0; + IsarDb.updateTimerTick(widget.timer); + if (_timerCounter != null && + widget.timer.isPaused == 0) { + widget.timer.startedOn = + DateTime.now().toString(); + IsarDb.updateTimerTick(widget.timer) + .then((value) => startTimer()); + } + }); + }, + child: Container( + decoration: BoxDecoration( + color: kprimaryBackgroundColor, + borderRadius: + BorderRadius.circular(20)), + child: const Padding( + padding: EdgeInsets.all(4.0), + child: Icon( + Icons.refresh, + size: 18, ), + ), + ), ), ), InkWell( - customBorder: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(80), - ), onTap: () { - setState(() { - widget.timer.isPaused == 0 - ? stopTimer() - : startTimer(); - widget.timer.isPaused = - widget.timer.isPaused == 0 ? 1 : 0; - }); + controller + .stopRinger(widget.timer.timerId); + controller + .deleteTimer(widget.timer.timerId); }, - child: Padding( + child: Container( + decoration: BoxDecoration( + color: kprimaryBackgroundColor, + borderRadius: + BorderRadius.circular(20)), + child: const Padding( + padding: EdgeInsets.all(4.0), + child: Icon( + Icons.close, + size: 18, + ), + ), + ), + ) + ], + ), + Padding( + padding: + const EdgeInsets.symmetric(vertical: 20), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + AnimatedContainer( + duration: Duration(seconds: 1), + child: Text( + "${Utils.formatMilliseconds(widget.timer.timerValue - widget.timer.timeElapsed)}", + style: Theme.of( + context, + ).textTheme.displayLarge!.copyWith( + color: themeController + .isLightMode.value + ? kLightPrimaryTextColor + : kprimaryTextColor, + fontSize: 44, + ), + ), + ), + Padding( padding: EdgeInsets.all(20), child: GestureDetector( - onTap: _toggleAnimation, + onTap: () { + setState(() { + widget.timer.isPaused == 0 + ? stopTimer() + : startTimer(); + widget.timer.isPaused = + widget.timer.isPaused == 0 + ? 1 + : 0; + IsarDb.updateTimerPauseStatus( + widget.timer); + }); + if (widget.timer.timeElapsed >= + widget.timer.timerValue) { + controller.stopRinger( + widget.timer.timerId); + setState(() { + widget.timer.timeElapsed = 0; + IsarDb.updateTimerTick( + widget.timer) + .then((value) => IsarDb + .updateTimerPauseStatus( + widget.timer)); + widget.timer.isPaused = 1; + }); + } + }, child: Container( decoration: BoxDecoration( color: kprimaryColor, @@ -203,19 +273,19 @@ class _TimerAnimatedCardState extends State color: ksecondaryBackgroundColor, ), ), - )), - ) - ], + )) + ], + ), ), - ), - ], + ], + ), ), - ), - ], + ], + ), ), - ), - ) - ], + ) + ], + ), ), ), ), @@ -223,6 +293,5 @@ class _TimerAnimatedCardState extends State } @override - // TODO: implement wantKeepAlive bool get wantKeepAlive => true; } diff --git a/lib/app/modules/timer/views/timer_view.dart b/lib/app/modules/timer/views/timer_view.dart index 219240e1..c4265737 100644 --- a/lib/app/modules/timer/views/timer_view.dart +++ b/lib/app/modules/timer/views/timer_view.dart @@ -19,7 +19,8 @@ class TimerView extends GetView { TimerView({Key? key}) : super(key: key); final ThemeController themeController = Get.find(); - final InputTimeController inputTimeController = Get.put(InputTimeController()); + final InputTimeController inputTimeController = + Get.put(InputTimeController()); var width = Get.width; var height = Get.height; @override @@ -70,7 +71,7 @@ class TimerView extends GetView { : StreamBuilder( stream: IsarDb.getTimers(), builder: (context, snapshot) { - if (!snapshot.hasData && snapshot.data!=[]) { + if (!snapshot.hasData && snapshot.data != []) { return const Center( child: CircularProgressIndicator.adaptive( backgroundColor: Colors.transparent, @@ -81,9 +82,8 @@ class TimerView extends GetView { ); } else { // list of pause values of timers - controller.initializeTempTimerVariables(); List? listOfTimers = snapshot.data; - return ListView.builder( + return ListView.builder( controller: controller.scrollController, shrinkWrap: true, scrollDirection: Axis.vertical, @@ -91,7 +91,11 @@ class TimerView extends GetView { itemBuilder: (BuildContext context, int index) { return Column( children: [ - TimerAnimatedCard(listOfTimers![index]), + TimerAnimatedCard( + key: ValueKey(listOfTimers![index].timerId), + index: index, + timer: listOfTimers![index], + ), if (index == snapshot.data!.length - 1) Padding( padding: const EdgeInsets.all(8.0), @@ -197,6 +201,24 @@ class TimerView extends GetView { ), fontSize: 15, ), + ),Spacer(), + Padding( + padding: const EdgeInsets.only(right: 20.0), + child: GestureDetector( + onTap: () { + inputTimeController.changeTimePickerTimer(); + }, + child: Icon( + Icons.keyboard,color: themeController.isLightMode.value + ? kLightPrimaryTextColor.withOpacity( + 0.5, + ) + : kprimaryTextColor.withOpacity( + 0.5, + ), + size: 20, + ), + ), ), ], ), @@ -204,7 +226,6 @@ class TimerView extends GetView { InkWell( onTap: () { Utils.hapticFeedback(); - inputTimeController.changeTimePickerTimer(); }, child: Obx( () => Container( @@ -657,29 +678,34 @@ class TimerView extends GetView { children: [ Padding( padding: const EdgeInsets.fromLTRB(0, 0, 20, 0), - child: InkWell( + child: InkWell(borderRadius: BorderRadius.circular(18), onTap: () { Get.back(); }, - child: Text( - 'Cancel', - style: - Theme.of(context).textTheme.displayMedium!.copyWith( - color: themeController.isLightMode.value - ? kLightPrimaryTextColor.withOpacity( - 0.5, - ) - : kprimaryTextColor.withOpacity( - 0.5, - ), - fontSize: 15, - ), + child: Container( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + 'Cancel', + style: + Theme.of(context).textTheme.displayMedium!.copyWith( + color: themeController.isLightMode.value + ? kLightPrimaryTextColor.withOpacity( + 0.5, + ) + : kprimaryTextColor.withOpacity( + 0.5, + ), + fontSize: 15, + ), + ), + ), ), ), ), Padding( padding: const EdgeInsets.fromLTRB(20, 0, 0, 0), - child: InkWell( + child: InkWell(borderRadius: BorderRadius.circular(18), onTap: () { controller.remainingTime.value = Duration( hours: controller.hours.value, @@ -688,19 +714,24 @@ class TimerView extends GetView { ); controller.createTimer(); }, - child: Text( - 'OK', - style: - Theme.of(context).textTheme.displayMedium!.copyWith( - color: themeController.isLightMode.value - ? kLightPrimaryTextColor.withOpacity( - 0.5, - ) - : kprimaryTextColor.withOpacity( - 0.5, - ), - fontSize: 15, - ), + child: Container( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + 'OK', + style: + Theme.of(context).textTheme.displayMedium!.copyWith( + color: themeController.isLightMode.value + ? kLightPrimaryTextColor.withOpacity( + 0.5, + ) + : kprimaryTextColor.withOpacity( + 0.5, + ), + fontSize: 15, + ), + ), + ), ), ), ), diff --git a/lib/app/utils/utils.dart b/lib/app/utils/utils.dart index 987d9c97..58c4630f 100644 --- a/lib/app/utils/utils.dart +++ b/lib/app/utils/utils.dart @@ -666,4 +666,15 @@ class Utils { ); } } + static int getDifferenceMillisFromNow(String datetimeString, int milliseconds) { + try { + final providedDatetime = DateTime.parse(datetimeString); + final updatedDatetime = providedDatetime.add(Duration(milliseconds: milliseconds)); + final currentDatetime = DateTime.now(); + final difference = updatedDatetime.difference(currentDatetime); + return difference.inMilliseconds; + } catch (e) { + return 0; + } + } } diff --git a/lib/main.dart b/lib/main.dart index e57fbba8..96ad2c54 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; +import 'package:permission_handler/permission_handler.dart'; import 'package:ultimate_alarm_clock/app/data/providers/get_storage_provider.dart'; import 'package:ultimate_alarm_clock/app/utils/language.dart'; @@ -14,7 +15,11 @@ Locale? loc; void main() async { WidgetsFlutterBinding.ensureInitialized(); - + await Permission.notification.isDenied.then((value) { + if (value) { + Permission.notification.request(); + } + }); await Firebase.initializeApp(); await Get.putAsync(() => GetStorageProvider().init()); From bd76b873b74f7ca2e69521eac6ac07118a13875f Mon Sep 17 00:00:00 2001 From: AryanSarafDev Date: Sun, 26 May 2024 15:13:32 +0530 Subject: [PATCH 4/5] timer bg optimization --- lib/app/data/providers/isar_provider.dart | 45 +++------- .../timer/controllers/timer_controller.dart | 13 +-- .../modules/timer/views/timer_animation.dart | 2 +- lib/app/modules/timer/views/timer_view.dart | 86 +++++++++++-------- 4 files changed, 65 insertions(+), 81 deletions(-) diff --git a/lib/app/data/providers/isar_provider.dart b/lib/app/data/providers/isar_provider.dart index d4d70f89..9125e158 100644 --- a/lib/app/data/providers/isar_provider.dart +++ b/lib/app/data/providers/isar_provider.dart @@ -128,9 +128,7 @@ class IsarDb { await db.alarmModels.put(alarmRecord); }); final sqlmap = alarmRecord.toSQFliteMap(); - await sql!.insert('alarms', sqlmap).then((value) { - sql.close(); - }); + await sql!.insert('alarms', sqlmap); return alarmRecord; } @@ -294,13 +292,8 @@ class IsarDb { await db.writeTxn(() async { await db.timerModels.put(timer); }); - try { - await sql!.insert('timers', timer.toMap()).then((value) { - sql.close(); - }); - } catch (e) { - print(e.toString()); - } + + await sql!.insert('timers', timer.toMap()); return timer; } @@ -311,22 +304,17 @@ class IsarDb { timer.toMap(), where: 'id = ?', whereArgs: [timer.timerId], - ).then((value) { - sql.close(); - return value; - }); + ); } static Future updateTimerName(int id, String newTimerName) async { final sql = await IsarDb().getTimerSQLiteDatabase(); - return await sql! - .update( + return await sql!.update( 'timers', {'timerName': newTimerName}, where: 'id = ?', whereArgs: [id], - ) - ; + ); } static Future deleteTimer(int id) async { @@ -336,12 +324,7 @@ class IsarDb { await db.writeTxn(() async { await db.timerModels.delete(id); }); - return await sql! - .delete('timers', where: 'id = ?', whereArgs: [id]).then((value) { - print("$value ss"); - sql.close(); - return value; - }); + return await sql!.delete('timers', where: 'id = ?', whereArgs: [id]); } static Future> getAllTimers() async { @@ -368,15 +351,14 @@ class IsarDb { await db.timerModels.put(timer); }); final sql = await IsarDb().getTimerSQLiteDatabase(); - await sql! - .update( + await sql!.update( 'timers', {'timeElapsed': timer.timeElapsed}, where: 'id = ?', whereArgs: [timer.timerId], - ) - ; + ); } + static Stream> getTimers() { final isarProvider = IsarDb(); final controller = StreamController>.broadcast(); @@ -384,7 +366,7 @@ class IsarDb { isarProvider.db.then((db) { final stream = db.timerModels.where().watch(fireImmediately: true); stream.listen( - (data) => controller.add(data), + (data) => controller.add(data), onError: (error) => controller.addError(error), onDone: () => controller.close(), ); @@ -403,17 +385,14 @@ class IsarDb { await db.timerModels.put(timer); }); final sql = await IsarDb().getTimerSQLiteDatabase(); - await sql! - .update( + await sql!.update( 'timers', {'isPaused': timer.isPaused}, where: 'id = ?', whereArgs: [timer.timerId], ); - } - static Future getNumberOfTimers() async { final sql = await IsarDb().getTimerSQLiteDatabase(); List> x = diff --git a/lib/app/modules/timer/controllers/timer_controller.dart b/lib/app/modules/timer/controllers/timer_controller.dart index 002f241b..17445a40 100644 --- a/lib/app/modules/timer/controllers/timer_controller.dart +++ b/lib/app/modules/timer/controllers/timer_controller.dart @@ -8,9 +8,7 @@ import 'package:ultimate_alarm_clock/app/utils/utils.dart'; class TimerController extends FullLifeCycleController with FullLifeCycleMixin { MethodChannel timerChannel = const MethodChannel('timer'); - final initialTime = DateTime(0, 0, 0, 0, 1, 0).obs; final remainingTime = const Duration(hours: 0, minutes: 0, seconds: 0).obs; - final currentTime = const Duration(hours: 0, minutes: 0, seconds: 0).obs; RxInt startTime = 0.obs; RxBool isTimerPaused = false.obs; RxBool isTimerRunning = false.obs; @@ -20,7 +18,6 @@ class TimerController extends FullLifeCycleController with FullLifeCycleMixin { RxList timers = [].obs; RxList isRinging = [].obs; - late AnimationController _controller; getFakeTimerModel() async { TimerModel fakeTimer = await Utils.genFakeTimerModel(); @@ -29,14 +26,6 @@ class TimerController extends FullLifeCycleController with FullLifeCycleMixin { updateTimerInfo() async { timerList.value = await IsarDb.getAllTimers(); - for (var timer in timerList) { - print('Timer ID: ${timer.timerId}'); - print('Started on: ${timer.startedOn}'); - print('timer value: ${timer.timerValue}'); - print('Ringtone Name: ${timer.ringtoneName}'); - print('Timer Name: ${timer.timerName}'); - print('Is Paused: ${timer.isPaused}'); - } } late int currentTimerIsarId; @@ -63,7 +52,7 @@ class TimerController extends FullLifeCycleController with FullLifeCycleMixin { } @override - void onClose() { + Future onClose() async { super.onClose(); } diff --git a/lib/app/modules/timer/views/timer_animation.dart b/lib/app/modules/timer/views/timer_animation.dart index 0edd43ac..ca1001c3 100644 --- a/lib/app/modules/timer/views/timer_animation.dart +++ b/lib/app/modules/timer/views/timer_animation.dart @@ -112,7 +112,7 @@ class _TimerAnimatedCardState extends State duration: Duration(milliseconds: 1000), height: context.height / 3.3, width: context.width * - ((widget.timer.timeElapsed + 500) / + ((widget.timer.timeElapsed) / (widget.timer.timerValue)), ), Center( diff --git a/lib/app/modules/timer/views/timer_view.dart b/lib/app/modules/timer/views/timer_view.dart index c4265737..3132dd95 100644 --- a/lib/app/modules/timer/views/timer_view.dart +++ b/lib/app/modules/timer/views/timer_view.dart @@ -26,6 +26,7 @@ class TimerView extends GetView { @override Widget build(BuildContext context) { return Scaffold( + floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, appBar: PreferredSize( preferredSize: Size.fromHeight(height / 8.9), child: AppBar( @@ -117,11 +118,12 @@ class TimerView extends GetView { child: FloatingActionButton( onPressed: () { Utils.hapticFeedback(); + TimerSelector(context); }, - backgroundColor: ksecondaryBackgroundColor, + backgroundColor: kprimaryColor, child: const Icon( Icons.add_alarm, - color: kprimaryColor, + color: ksecondaryBackgroundColor, size: 26, ), ), @@ -201,21 +203,23 @@ class TimerView extends GetView { ), fontSize: 15, ), - ),Spacer(), + ), + Spacer(), Padding( padding: const EdgeInsets.only(right: 20.0), child: GestureDetector( onTap: () { inputTimeController.changeTimePickerTimer(); }, - child: Icon( - Icons.keyboard,color: themeController.isLightMode.value - ? kLightPrimaryTextColor.withOpacity( - 0.5, - ) - : kprimaryTextColor.withOpacity( - 0.5, - ), + child: Icon( + Icons.keyboard, + color: themeController.isLightMode.value + ? kLightPrimaryTextColor.withOpacity( + 0.5, + ) + : kprimaryTextColor.withOpacity( + 0.5, + ), size: 20, ), ), @@ -678,7 +682,8 @@ class TimerView extends GetView { children: [ Padding( padding: const EdgeInsets.fromLTRB(0, 0, 20, 0), - child: InkWell(borderRadius: BorderRadius.circular(18), + child: InkWell( + borderRadius: BorderRadius.circular(18), onTap: () { Get.back(); }, @@ -687,17 +692,19 @@ class TimerView extends GetView { padding: const EdgeInsets.all(8.0), child: Text( 'Cancel', - style: - Theme.of(context).textTheme.displayMedium!.copyWith( - color: themeController.isLightMode.value - ? kLightPrimaryTextColor.withOpacity( - 0.5, - ) - : kprimaryTextColor.withOpacity( - 0.5, - ), - fontSize: 15, - ), + style: Theme.of(context) + .textTheme + .displayMedium! + .copyWith( + color: themeController.isLightMode.value + ? kLightPrimaryTextColor.withOpacity( + 0.5, + ) + : kprimaryTextColor.withOpacity( + 0.5, + ), + fontSize: 15, + ), ), ), ), @@ -705,31 +712,40 @@ class TimerView extends GetView { ), Padding( padding: const EdgeInsets.fromLTRB(20, 0, 0, 0), - child: InkWell(borderRadius: BorderRadius.circular(18), + child: InkWell( + borderRadius: BorderRadius.circular(18), onTap: () { controller.remainingTime.value = Duration( hours: controller.hours.value, minutes: controller.minutes.value, seconds: controller.seconds.value, ); + if(controller.hours.value != 0 || + controller.minutes.value != 0|| + controller.seconds.value != 0) controller.createTimer(); + controller.hours.value = 0; + controller.minutes.value = 1; + controller.seconds.value = 0; }, child: Container( child: Padding( padding: const EdgeInsets.all(8.0), child: Text( 'OK', - style: - Theme.of(context).textTheme.displayMedium!.copyWith( - color: themeController.isLightMode.value - ? kLightPrimaryTextColor.withOpacity( - 0.5, - ) - : kprimaryTextColor.withOpacity( - 0.5, - ), - fontSize: 15, - ), + style: Theme.of(context) + .textTheme + .displayMedium! + .copyWith( + color: themeController.isLightMode.value + ? kLightPrimaryTextColor.withOpacity( + 0.5, + ) + : kprimaryTextColor.withOpacity( + 0.5, + ), + fontSize: 15, + ), ), ), ), From 76a6349588bd586b28bbeb81d55474a7ca96b116 Mon Sep 17 00:00:00 2001 From: AryanSarafDev Date: Mon, 27 May 2024 15:34:26 +0530 Subject: [PATCH 5/5] timer complete --- android/app/src/main/AndroidManifest.xml | 4 - .../ultimate_alarm_clock/BootReceiver.kt | 76 ++++++++++++++++++- .../ultimate_alarm_clock/MainActivity.kt | 1 - .../TimerBroadcasts/CancelTimer.kt | 22 ++++++ .../TimerDatabaseHelper.kt | 11 ++- .../ultimate_alarm_clock/TimerFunctions.kt | 2 +- .../ultimate_alarm_clock/TimerNotification.kt | 2 - 7 files changed, 107 insertions(+), 11 deletions(-) create mode 100644 android/app/src/main/kotlin/com/example/ultimate_alarm_clock/TimerBroadcasts/CancelTimer.kt diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 3c7f8a1b..26cc1170 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -42,10 +42,6 @@ android:name=".TimerNotification" android:enabled="true" android:exported="false"> - - - - diff --git a/android/app/src/main/kotlin/com/example/ultimate_alarm_clock/BootReceiver.kt b/android/app/src/main/kotlin/com/example/ultimate_alarm_clock/BootReceiver.kt index 41c931bd..a935253f 100644 --- a/android/app/src/main/kotlin/com/example/ultimate_alarm_clock/BootReceiver.kt +++ b/android/app/src/main/kotlin/com/example/ultimate_alarm_clock/BootReceiver.kt @@ -26,8 +26,37 @@ class BootReceiver : BroadcastReceiver() { scheduleAlarm(ringTime, context) } - } - } + val timerdbhelper = TimerDatabaseHelper(context) + val timerdb = timerdbhelper.readableDatabase + val time = getLatestTimer(timerdb) + timerdb.close() + var notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + val commonTimer = CommonTimerManager.getCommonTimer(object : TimerListener { + override fun onTick(millisUntilFinished: Long) { + println(millisUntilFinished) + showTimerNotification(millisUntilFinished,"Timer",context) + + } + + override fun onFinish() { + notificationManager.cancel(1) + } + }) + + + createNotificationChannel(context) + + + + if (time!=null){ + + // Start or stop the timer based on your requirements + commonTimer.startTimer(time.second) + + } + + + }} fun scheduleAlarm(milliSeconds: Long, context: Context) { val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager val intent = Intent(context, AlarmReceiver::class.java) @@ -42,6 +71,49 @@ class BootReceiver : BroadcastReceiver() { val triggerTime = SystemClock.elapsedRealtime() + milliSeconds alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerTime, pendingIntent) } + private fun createNotificationChannel(context: Context) { + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val channel = NotificationChannel( + TimerService.TIMER_CHANNEL_ID, + "Timer Channel", + NotificationManager.IMPORTANCE_DEFAULT + ) + val notificationManager = + context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + notificationManager.createNotificationChannel(channel) + } + } + + + private fun showTimerNotification(milliseconds: Long,timerName:String,context: Context){ + var notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + val deleteIntent = Intent(context,TimerNotification::class.java) + deleteIntent.action = "com.example.ultimate_alarm_clock.STOP_TIMERNOTIF" + val deletePendingIntent = PendingIntent.getBroadcast(context, 5, deleteIntent, + PendingIntent.FLAG_IMMUTABLE) + val notification = NotificationCompat.Builder(context, TimerService.TIMER_CHANNEL_ID) + .setSmallIcon(R.mipmap.launcher_icon) + .setContentText("$timerName") + .setContentText(formatDuration(milliseconds)) + .setOnlyAlertOnce(true) + .setDeleteIntent(deletePendingIntent) + . + build() + notificationManager.notify(1,notification) + } + + private fun formatDuration(milliseconds: Long): String { + val seconds = (milliseconds / 1000) % 60 + val minutes = (milliseconds / (1000 * 60)) % 60 + val hours = (milliseconds / (1000 * 60 * 60)) % 24 + + return if (hours > 0) { + String.format("%02d:%02d:%02d", hours, minutes, seconds) + } else { + String.format("%02d:%02d", minutes, seconds) + } + } diff --git a/android/app/src/main/kotlin/com/example/ultimate_alarm_clock/MainActivity.kt b/android/app/src/main/kotlin/com/example/ultimate_alarm_clock/MainActivity.kt index c3c93b14..92448da9 100644 --- a/android/app/src/main/kotlin/com/example/ultimate_alarm_clock/MainActivity.kt +++ b/android/app/src/main/kotlin/com/example/ultimate_alarm_clock/MainActivity.kt @@ -37,7 +37,6 @@ class MainActivity : FlutterActivity() { var intentFilter = IntentFilter() intentFilter.addAction("com.example.ultimate_alarm_clock.START_TIMERNOTIF") intentFilter.addAction("com.example.ultimate_alarm_clock.STOP_TIMERNOTIF") - intentFilter.addAction(Intent.ACTION_BOOT_COMPLETED) context.registerReceiver(TimerNotification(),intentFilter) } diff --git a/android/app/src/main/kotlin/com/example/ultimate_alarm_clock/TimerBroadcasts/CancelTimer.kt b/android/app/src/main/kotlin/com/example/ultimate_alarm_clock/TimerBroadcasts/CancelTimer.kt new file mode 100644 index 00000000..7bd9ced1 --- /dev/null +++ b/android/app/src/main/kotlin/com/example/ultimate_alarm_clock/TimerBroadcasts/CancelTimer.kt @@ -0,0 +1,22 @@ +package com.example.ultimate_alarm_clock.TimerBroadcasts + +import android.app.NotificationManager +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import com.example.ultimate_alarm_clock.CommonTimerManager +import com.example.ultimate_alarm_clock.TimerListener + +class CancelTimer : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent?) { + var notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + val commonTimer = CommonTimerManager.getCommonTimer(object : TimerListener { + override fun onTick(millisUntilFinished: Long) { + } + override fun onFinish() { + notificationManager.cancel(1) + } + }) + commonTimer.stopTimer() + } +} \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/example/ultimate_alarm_clock/TimerDatabaseHelper.kt b/android/app/src/main/kotlin/com/example/ultimate_alarm_clock/TimerDatabaseHelper.kt index a363ac13..09a2a795 100644 --- a/android/app/src/main/kotlin/com/example/ultimate_alarm_clock/TimerDatabaseHelper.kt +++ b/android/app/src/main/kotlin/com/example/ultimate_alarm_clock/TimerDatabaseHelper.kt @@ -10,7 +10,16 @@ class TimerDatabaseHelper(context: Context) : SQLiteOpenHelper(context, DATABASE private const val DATABASE_NAME = "timer.db" } - override fun onCreate(db: SQLiteDatabase) {} + override fun onCreate(db: SQLiteDatabase) { + db.rawQuery(""" create table timers ( + id integer primary key autoincrement, + startedOn text not null, + timerValue integer not null, + timeElapsed integer not null, + ringtoneName text not null, + timerName text not null, + isPaused integer not null)""",null) + } override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { } diff --git a/android/app/src/main/kotlin/com/example/ultimate_alarm_clock/TimerFunctions.kt b/android/app/src/main/kotlin/com/example/ultimate_alarm_clock/TimerFunctions.kt index 372cc259..d6e9fbdb 100644 --- a/android/app/src/main/kotlin/com/example/ultimate_alarm_clock/TimerFunctions.kt +++ b/android/app/src/main/kotlin/com/example/ultimate_alarm_clock/TimerFunctions.kt @@ -20,7 +20,6 @@ fun getLatestTimer(db: SQLiteDatabase): Triple? { """, null ) - return if (cursor.moveToFirst()) { val timer = TimerModel.fromCursor(cursor) cursor.close() @@ -41,6 +40,7 @@ fun pauseTimer(db: SQLiteDatabase, timerId: Int): Boolean { return rowsAffected > 0 } + fun isFutureDatetimeWithMillis(datetimeString: String, milliseconds: Long): Long { try { val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSS") diff --git a/android/app/src/main/kotlin/com/example/ultimate_alarm_clock/TimerNotification.kt b/android/app/src/main/kotlin/com/example/ultimate_alarm_clock/TimerNotification.kt index c9dc6d51..28a0338e 100644 --- a/android/app/src/main/kotlin/com/example/ultimate_alarm_clock/TimerNotification.kt +++ b/android/app/src/main/kotlin/com/example/ultimate_alarm_clock/TimerNotification.kt @@ -16,9 +16,7 @@ class TimerNotification: BroadcastReceiver() { var notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager val commonTimer = CommonTimerManager.getCommonTimer(object : TimerListener { override fun onTick(millisUntilFinished: Long) { - println(millisUntilFinished) showTimerNotification(millisUntilFinished,"Timer",context) - } override fun onFinish() {