Skip to content

Commit

Permalink
Add nextAlarm property to Hive
Browse files Browse the repository at this point in the history
- The nextAlarm property is used with the enabled property
  to track service state over restarts
  • Loading branch information
kmac committed Nov 14, 2021
1 parent 86a3442 commit 8189ada
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 72 deletions.
4 changes: 2 additions & 2 deletions lib/components/alarmservice.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ Future<bool> initializeAlarmService({bool bootstrap: false}) async {
return alarmServiceAlreadyRunning;
}

// TODO the underlying issue is that we can't query android alarm manager
// The underlying issue is that we can't query android alarm manager
// to see if we have outstanding alarms.
// We could store next alarm time in hive db and compare
// We store the next alarm time in hive db and compare
// with current time to see if we should have an alarm scheduled

try {
Expand Down
68 changes: 36 additions & 32 deletions lib/components/datastore.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ var logger = createLogger('datastore');

final Random random = Random();

bool testMigrateApp = false;
bool testMigrateSched = false;

// A list for the initial json string. Each entry has keys: text, enabled, tag, weight
//
// Idea: add optional weight to support weighing reminders differently
Expand Down Expand Up @@ -94,18 +97,6 @@ const List<Map<String, dynamic>> defaultJsonReminderMap = [
"enabled": true,
"tag": "${Reminder.defaultTagName}"
},
// {
// "text":
// "Two is very two, two is very too. This is very long. This is very long. This is very long. This is very long. This is very long. This is very long. This is very long. This is very long. This is very long. This is very long. This is very long. This is very long. This is very long. This is very long. This is very long. This is very long. This is very long. This is very long. This is very long. This is very long. This is very long. This is very long. This is very long.",
// "enabled": true,
// "tag": "${Reminder.defaultTagName}"
// },
// {
// "text":
// "This is very long. This is very long. This is very long. This is very long. This is very long. This is very long. This is very long. This is very long. This is very long. This is very long. This is very long. This is very long. This is very long. This is very long. This is very long. This is very long. This is very long. This is very long. This is very long. This is very long. This is very long. This is very long. This is very long.",
// "enabled": true,
// "tag": "${Reminder.defaultTagName}"
// },
];

Future<void> checkMigrateSharedPreferences(var box,
Expand All @@ -116,7 +107,7 @@ Future<void> checkMigrateSharedPreferences(var box,
// Check if we need to convert from SharedPreferences
SharedPreferences prefs = await SharedPreferences.getInstance();
if (prefs.getKeys().length > 0) {
logger.i("Migrating SharedPreferences to Hive");
logger.i("Migrating SharedPreferences to Hive box ${box.name}");

for (String key in prefs.getKeys()) {
bool ignoreKey = false;
Expand All @@ -126,12 +117,11 @@ Future<void> checkMigrateSharedPreferences(var box,
ignoreKey = true;
}
if (ignoreKey) {
logger.i("Ignoring $key");
logger.i("${box.name}: Ignoring $key");
} else {
var value = prefs.get(key);
logger.i("Converting $key = $value");
logger.i("${box.name}: Converting $key = $value");
box.put(key, value);
// TODO uncomment this when ready:
// logger.i("Removing key: $key from SharedPreferences");
// prefs.remove(key);
}
Expand All @@ -154,7 +144,7 @@ class AppDataStore {

static AppDataStore _instance;

var _box;
Box _box;

/// Public factory
static Future<AppDataStore> getInstance() async {
Expand All @@ -175,10 +165,9 @@ class AppDataStore {
await Hive.initFlutter();

_box = await Hive.openBox('appdata');
bool testMigrate = false;
if (testMigrate) {
if (testMigrateApp) {
await _box.clear();
testMigrate = false;
testMigrateApp = false;
}
await checkMigrateSharedPreferences(_box, includeKeys: [
ScheduleDataStoreBase.themeKey,
Expand Down Expand Up @@ -233,6 +222,7 @@ abstract class ScheduleDataStoreBase {
static const String quietHoursEndMinuteKey = 'quietHoursEndMinute';
static const String notifyQuietHoursKey = 'notifyQuietHours';
static const String reminderMessageKey = 'reminderMessage';
static const String nextAlarmKey = 'nextAlarm';

// replaced by jsonReminders :
static const String remindersKeyDeprecated = 'reminders';
Expand All @@ -258,7 +248,7 @@ abstract class ScheduleDataStoreBase {
static const int defaultQuietHoursEndMinute = 0;
static const bool defaultNotifyQuietHours = false;
static const String defaultReminderMessage = 'Not Enabled';
static const String defaultInfoMessage = 'Uninitialized';
static const String defaultInfoMessage = 'Disabled';
static const String defaultControlMessage = '';
static const String defaultTheme = 'Default';
static const String defaultBellId = 'bell1';
Expand Down Expand Up @@ -287,6 +277,7 @@ abstract class ScheduleDataStoreBase {
String get controlMessage;
String get bellId;
String get customBellPath;
String get nextAlarm;

// this is only here to support old reminders format
bool reminderExists(String reminderText, {List jsonReminderList}) {
Expand All @@ -298,6 +289,11 @@ abstract class ScheduleDataStoreBase {
}
return false;
}

String randomReminder({String tag}) {
Reminders reminders = Reminders.fromJson(jsonReminders);
return reminders.randomReminder(tag: tag);
}
}

/// In-memory data store. Created from the scheduler/alarm service and passed
Expand Down Expand Up @@ -327,6 +323,7 @@ class InMemoryScheduleDataStore extends ScheduleDataStoreBase {
String controlMessage;
String bellId;
String customBellPath;
String nextAlarm;

InMemoryScheduleDataStore.fromDS(ScheduleDataStore ds)
: this.enabled = ds.enabled,
Expand All @@ -351,7 +348,8 @@ class InMemoryScheduleDataStore extends ScheduleDataStoreBase {
this.infoMessage = ds.infoMessage,
this.controlMessage = ds.controlMessage,
this.bellId = ds.bellId,
this.customBellPath = ds.customBellPath;
this.customBellPath = ds.customBellPath,
this.nextAlarm = ds.nextAlarm;
}

/// Data store for the scheduler/alarm service. This data store is accessed
Expand All @@ -378,17 +376,16 @@ class ScheduleDataStore extends ScheduleDataStoreBase {
return InMemoryScheduleDataStore.fromDS(this);
}

var _box;
Box _box;

Future<void> _init() async {
logger.i("Initializing ScheduleDataStore (hive");
await Hive.initFlutter();

_box = await Hive.openBox('mindfulnotifier');
bool testMigrate = false;
if (testMigrate) {
_box = await Hive.openBox('scheduledata');
if (testMigrateSched) {
await _box.clear();
testMigrate = false;
testMigrateSched = false;
}
await checkMigrateSharedPreferences(_box, excludeKeys: [
ScheduleDataStoreBase.themeKey,
Expand Down Expand Up @@ -449,6 +446,7 @@ class ScheduleDataStore extends ScheduleDataStoreBase {
_mergeVal(ScheduleDataStoreBase.controlMessageKey, mds.controlMessage);
_mergeVal(ScheduleDataStoreBase.bellIdKey, mds.bellId);
_mergeVal(ScheduleDataStoreBase.customBellPathKey, mds.customBellPath);
_mergeVal(ScheduleDataStoreBase.nextAlarmKey, mds.nextAlarm);
}

bool get enabled {
Expand Down Expand Up @@ -687,6 +685,17 @@ class ScheduleDataStore extends ScheduleDataStoreBase {
setSync(ScheduleDataStoreBase.customBellPathKey, value);
}

String get nextAlarm {
if (_box.get(ScheduleDataStoreBase.nextAlarmKey) == null) {
nextAlarm = '';
}
return _box.get(ScheduleDataStoreBase.nextAlarmKey);
}

set nextAlarm(String value) {
setSync(ScheduleDataStoreBase.nextAlarmKey, value);
}

String get jsonReminders {
// Check for migration to new format:
if (_box.get(ScheduleDataStoreBase.remindersKeyDeprecated) != null) {
Expand All @@ -711,11 +720,6 @@ class ScheduleDataStore extends ScheduleDataStoreBase {

setSync(ScheduleDataStoreBase.jsonRemindersKey, jsonString);
}

String randomReminder({String tag}) {
Reminders reminders = Reminders.fromJson(jsonReminders);
return reminders.randomReminder(tag: tag);
}
}

class Reminder extends Equatable {
Expand Down
56 changes: 35 additions & 21 deletions lib/components/scheduler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:shared_preferences/shared_preferences.dart';

import 'package:mindfulnotifier/components/alarmservice.dart';
import 'package:mindfulnotifier/components/constants.dart' as constants;
Expand All @@ -26,14 +25,13 @@ const bool rescheduleAfterQuietHours = true;
const int scheduleAlarmID = 10;
bool initialNotificationTriggered = false;

/// Newest changes:
/// scheduler owns the data
/// is reinitialized upon every alarm
/// reschedules next alarm upon every alarm
/// always refreshes from the datastore
/// - no need to reinitialize when UI changes config
// Design notes:
/// - scheduler owns the data
/// - is reinitialized upon every alarm
/// - reschedules next alarm upon every alarm
/// - always refreshes from the datastore
/// - no need to reinitialize when UI changes config
// - the UI only makes config changes and enable/disable
// - everything else is controlled by the scheduler

Expand Down Expand Up @@ -88,10 +86,6 @@ class Scheduler {
// throws during testing
}

SharedPreferences prefs = await SharedPreferences.getInstance();
Get.delete<SharedPreferences>();
Get.put(prefs, permanent: true);

ds = await ScheduleDataStore.getInstance();
Get.delete<ScheduleDataStore>();
Get.put(ds, permanent: true);
Expand All @@ -118,7 +112,7 @@ class Scheduler {
}
}

void enable() {
void enable({bool restart = false}) {
logger.i("enable");
ds.enabled = true;

Expand All @@ -129,7 +123,7 @@ class Scheduler {
// 1) reboot
// 2) first enabled by user
// 3) re-enable after config changes by user
delegate.scheduleNext();
delegate.scheduleNext(restart: restart);
// sendInfoMessage(
// 'Next reminder at ${formatHHMM(delegate.queryNext())}');
String enabledReminderText = '${constants.appName} is enabled';
Expand All @@ -155,23 +149,26 @@ class Scheduler {
// return false;
// }

void disable({bool sendUpdate = true}) async {
void disable({bool sendUpdate = true, bool restart = false}) async {
logger.i("disable");
delegate?.cancel();
Notifier().shutdown();
running = false;
ds.enabled = false;
ds.reminderMessage = "Disabled";
ds.reminderMessage = ScheduleDataStoreBase.defaultReminderMessage;
if (!restart) {
ds.nextAlarm = '';
}
if (sendUpdate) {
sendDataStoreUpdate();
}
}

void restart() {
logger.i("restart");
disable(sendUpdate: false);
sleep(Duration(seconds: 1));
enable();
disable(sendUpdate: false, restart: true);
sleep(Duration(milliseconds: 500));
enable(restart: true);
}

void playSound(var fileOrPath) {
Expand Down Expand Up @@ -288,11 +285,25 @@ abstract class DelegatedScheduler {

DateTime getNextFireTime({DateTime fromTime, bool adjustFromQuiet});

void scheduleNext() async {
void scheduleNext({bool restart = false}) async {
logger.d(
"Scheduling next notification, type=$scheduleType ${getCurrentIsolate()}");

_nextDate = getNextFireTime();
_nextDate = null;
if (restart) {
// use nextAlarm if possible; otherwise it gets left null
String nextAlarmStr = scheduler.ds.nextAlarm;
if (nextAlarmStr != null && nextAlarmStr != '') {
DateTime nextAlarm = DateTime.parse(scheduler.ds.nextAlarm);
if (nextAlarm.isAfter(DateTime.now().add(Duration(seconds: 10)))) {
logger.i("Re-scheduling based on nextAlarm $nextAlarmStr");
_nextDate = nextAlarm;
}
}
}
if (_nextDate == null) {
_nextDate = getNextFireTime();
}

if (rescheduleAfterQuietHours && quietHours.isInQuietHours(_nextDate)) {
_nextDate = getNextFireTime(
Expand All @@ -307,6 +318,9 @@ abstract class DelegatedScheduler {

TimerService timerService = await getAlarmManagerTimerService();
timerService.oneShotAt(_nextDate, scheduleAlarmID, scheduleCallback);
scheduler.updateDS(
ScheduleDataStoreBase.nextAlarmKey, _nextDate.toIso8601String(),
sendUpdate: true);

if (!scheduled) {
initialScheduleComplete();
Expand Down
2 changes: 1 addition & 1 deletion lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ void main() async {
}

Get.put(await initializeAlarmService(bootstrap: true),
permanent: true, tag: constants.tagAlarmServiceAlreadyRunning);
tag: constants.tagAlarmServiceAlreadyRunning);

runApp(
GetMaterialApp(
Expand Down
Loading

0 comments on commit 8189ada

Please sign in to comment.