Skip to content

Commit

Permalink
Apply very_good_analysis lint rules
Browse files Browse the repository at this point in the history
  • Loading branch information
gdelataillade committed Mar 7, 2024
1 parent da1d8c6 commit 9232aa1
Show file tree
Hide file tree
Showing 13 changed files with 153 additions and 98 deletions.
5 changes: 1 addition & 4 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1 @@
include: package:flutter_lints/flutter.yaml

# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
include: package:very_good_analysis/analysis_options.yaml
1 change: 1 addition & 0 deletions example/lib/screens/edit_alarm.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:alarm/alarm.dart';
import 'package:alarm/model/alarm_settings.dart';
import 'package:flutter/material.dart';

class ExampleAlarmEditScreen extends StatefulWidget {
Expand Down
1 change: 1 addition & 0 deletions example/lib/screens/home.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'dart:async';

import 'package:alarm/alarm.dart';
import 'package:alarm/model/alarm_settings.dart';
import 'package:alarm_example/screens/edit_alarm.dart';
import 'package:alarm_example/screens/ring.dart';
import 'package:alarm_example/screens/shortcut_button.dart';
Expand Down
1 change: 1 addition & 0 deletions example/lib/screens/ring.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:alarm/alarm.dart';
import 'package:alarm/model/alarm_settings.dart';
import 'package:flutter/material.dart';

class ExampleAlarmRingScreen extends StatelessWidget {
Expand Down
1 change: 1 addition & 0 deletions example/lib/screens/shortcut_button.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:alarm/alarm.dart';
import 'package:alarm/model/alarm_settings.dart';
import 'package:flutter/material.dart';

class ExampleAlarmHomeShortcutButton extends StatefulWidget {
Expand Down
45 changes: 15 additions & 30 deletions lib/alarm.dart
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
// ignore_for_file: avoid_print

export 'package:alarm/model/alarm_settings.dart';
import 'dart:async';

import 'package:alarm/model/alarm_settings.dart';
import 'package:alarm/src/ios_alarm.dart';
import 'package:alarm/service/alarm_storage.dart';
import 'package:alarm/src/android_alarm.dart';
import 'package:alarm/service/storage.dart';
import 'package:alarm/src/ios_alarm.dart';
import 'package:alarm/utils/alarm_exception.dart';
import 'package:alarm/utils/extensions.dart';
import 'package:flutter/foundation.dart';

/// Custom print function designed for Alarm plugin.
DebugPrintCallback alarmPrint = debugPrintThrottled;

/// Class that handles the alarm.
class Alarm {
/// Whether it's iOS device.
static bool get iOS => defaultTargetPlatform == TargetPlatform.iOS;
Expand All @@ -31,7 +33,7 @@ class Alarm {
static Future<void> init({bool showDebugLogs = true}) async {
alarmPrint = (String? message, {int? wrapWidth}) {
if (kDebugMode && showDebugLogs) {
print("[Alarm] $message");
print('[Alarm] $message');
}
};

Expand Down Expand Up @@ -62,7 +64,7 @@ class Alarm {

/// Schedules an alarm with given [alarmSettings] with its notification.
///
/// If you set an alarm for the same [dateTime] as an existing one,
/// If you set an alarm for the same dateTime as an existing one,
/// the new alarm will replace the existing one.
static Future<bool> set({required AlarmSettings alarmSettings}) async {
alarmSettingsValidation(alarmSettings);
Expand All @@ -82,7 +84,7 @@ class Alarm {
() => ringStream.add(alarmSettings),
);
} else if (android) {
return await AndroidAlarm.set(
return AndroidAlarm.set(
alarmSettings,
() => ringStream.add(alarmSettings),
);
Expand All @@ -91,6 +93,7 @@ class Alarm {
return false;
}

/// Validates [alarmSettings] fields.
static void alarmSettingsValidation(AlarmSettings alarmSettings) {
if (alarmSettings.id == 0 || alarmSettings.id == -1) {
throw AlarmException(
Expand All @@ -99,12 +102,12 @@ class Alarm {
}
if (alarmSettings.id > 2147483647) {
throw AlarmException(
'Alarm id cannot be set larger than Int max value (2147483647). Provided: ${alarmSettings.id}',
'''Alarm id cannot be set larger than Int max value (2147483647). Provided: ${alarmSettings.id}''',
);
}
if (alarmSettings.id < -2147483648) {
throw AlarmException(
'Alarm id cannot be set smaller than Int min value (-2147483648). Provided: ${alarmSettings.id}',
'''Alarm id cannot be set smaller than Int min value (-2147483648). Provided: ${alarmSettings.id}''',
);
}
if (alarmSettings.volume != null &&
Expand All @@ -115,7 +118,7 @@ class Alarm {
}
if (alarmSettings.fadeDuration < 0) {
throw AlarmException(
'Fade duration must be positive. Provided: ${alarmSettings.fadeDuration}',
'''Fade duration must be positive. Provided: ${alarmSettings.fadeDuration}''',
);
}
}
Expand All @@ -127,7 +130,8 @@ class Alarm {
///
/// [title] default value is `Your alarm may not ring`
///
/// [body] default value is `You killed the app. Please reopen so your alarm can ring.`
/// [body] default value is `You killed the app.
/// Please reopen so your alarm can ring.`
static Future<void> setNotificationOnAppKillContent(
String title,
String body,
Expand Down Expand Up @@ -160,7 +164,7 @@ class Alarm {

/// Returns alarm by given id. Returns null if not found.
static AlarmSettings? getAlarm(int id) {
List<AlarmSettings> alarms = AlarmStorage.getSavedAlarms();
final alarms = AlarmStorage.getSavedAlarms();

for (final alarm in alarms) {
if (alarm.id == id) return alarm;
Expand All @@ -173,22 +177,3 @@ class Alarm {
/// Returns all the alarms.
static List<AlarmSettings> getAlarms() => AlarmStorage.getSavedAlarms();
}

class AlarmException implements Exception {
final String message;

const AlarmException(this.message);

@override
String toString() => message;
}

extension DateTimeExtension on DateTime {
bool isSameSecond(DateTime other) =>
year == other.year &&
month == other.month &&
day == other.day &&
hour == other.hour &&
minute == other.minute &&
second == other.second;
}
114 changes: 66 additions & 48 deletions lib/model/alarm_settings.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,45 @@
import 'package:flutter/widgets.dart';

/// [AlarmSettings] is a model that contains all the settings to customize
/// and set an alarm.
@immutable
class AlarmSettings {
/// Model that contains all the settings to customize and set an alarm.
///
///
/// Note that if you want to show a notification when alarm is triggered, both
/// [notificationTitle] and [notificationBody] must not be null nor empty.
const AlarmSettings({
required this.id,
required this.dateTime,
required this.assetAudioPath,
required this.notificationTitle,
required this.notificationBody,
this.loopAudio = true,
this.vibrate = true,
this.volume,
this.fadeDuration = 0.0,
this.enableNotificationOnKill = true,
this.androidFullScreenIntent = true,
});

/// Constructs an `AlarmSettings` instance from the given JSON data.
factory AlarmSettings.fromJson(Map<String, dynamic> json) => AlarmSettings(
id: json['id'] as int,
dateTime: DateTime.fromMicrosecondsSinceEpoch(json['dateTime'] as int),
assetAudioPath: json['assetAudioPath'] as String,
loopAudio: json['loopAudio'] as bool,
vibrate: json['vibrate'] as bool? ?? true,
volume: json['volume'] as double?,
fadeDuration: json['fadeDuration'] as double,
notificationTitle: json['notificationTitle'] as String? ?? '',
notificationBody: json['notificationBody'] as String? ?? '',
enableNotificationOnKill:
json['enableNotificationOnKill'] as bool? ?? true,
androidFullScreenIntent:
json['androidFullScreenIntent'] as bool? ?? true,
);

/// Unique identifier assiocated with the alarm. Cannot be 0 or -1;
final int id;

Expand All @@ -7,15 +48,24 @@ class AlarmSettings {

/// Path to audio asset to be used as the alarm ringtone. Accepted formats:
///
/// * **Project asset**: Specifies an asset bundled with your Flutter project. Use this format for assets that are included in your project's `pubspec.yaml` file.
/// * **Project asset**: Specifies an asset bundled with your Flutter project.
/// Use this format for assets that are included in your project's
/// `pubspec.yaml` file.
/// Example: `assets/audio.mp3`.
/// * **Absolute file path**: Specifies a direct file system path to the audio file. This format is used for audio files stored outside the Flutter project, such as files saved in the device's internal or external storage.
/// * **Absolute file path**: Specifies a direct file system path to the
/// audio file. This format is used for audio files stored outside the
/// Flutter project, such as files saved in the device's internal
/// or external storage.
/// Example: `/path/to/your/audio.mp3`.
/// * **Relative file path**: Specifies a file path relative to a predefined base directory in the app's internal storage. This format is convenient for referring to files that are stored within a specific directory of your app's internal storage without needing to specify the full path.
/// * **Relative file path**: Specifies a file path relative to a predefined
/// base directory in the app's internal storage. This format is convenient
/// for referring to files that are stored within a specific directory of
/// your app's internal storage without needing to specify the full path.
/// Example: `Audios/audio.mp3`.
///
/// If you want to use aboslute or relative file path, you must request android storage
/// permission and add the following permission to your `AndroidManifest.xml`:
/// If you want to use aboslute or relative file path, you must request
/// android storage permission and add the following permission to your
/// `AndroidManifest.xml`:
/// `android.permission.READ_EXTERNAL_STORAGE`
final String assetAudioPath;

Expand All @@ -30,11 +80,13 @@ class AlarmSettings {

/// Specifies the system volume level to be set at the designated [dateTime].
///
/// Accepts a value between 0 (mute) and 1 (maximum volume). When the alarm is triggered at [dateTime],
/// the system volume adjusts to this specified level. Upon stopping the alarm, the system volume reverts
/// Accepts a value between 0 (mute) and 1 (maximum volume).
/// When the alarm is triggered at [dateTime], the system volume adjusts to
/// this specified level. Upon stopping the alarm, the system volume reverts
/// to its prior setting.
///
/// If left unspecified or set to `null`, the current system volume at the time of the alarm will be used.
/// If left unspecified or set to `null`, the current system volume
/// at the time of the alarm will be used.
/// Defaults to `null`.
final double? volume;

Expand All @@ -57,7 +109,8 @@ class AlarmSettings {
/// when android alarm notification is triggered. Enabled by default.
final bool androidFullScreenIntent;

/// Returns a hash code for this `AlarmSettings` instance using Jenkins hash function.
/// Returns a hash code for this `AlarmSettings` instance using
/// Jenkins hash function.
@override
int get hashCode {
var hash = 0;
Expand All @@ -77,42 +130,6 @@ class AlarmSettings {
return hash;
}

/// Model that contains all the settings to customize and set an alarm.
///
///
/// Note that if you want to show a notification when alarm is triggered,
/// both [notificationTitle] and [notificationBody] must not be null nor empty.
const AlarmSettings({
required this.id,
required this.dateTime,
required this.assetAudioPath,
this.loopAudio = true,
this.vibrate = true,
this.volume,
this.fadeDuration = 0.0,
required this.notificationTitle,
required this.notificationBody,
this.enableNotificationOnKill = true,
this.androidFullScreenIntent = true,
});

/// Constructs an `AlarmSettings` instance from the given JSON data.
factory AlarmSettings.fromJson(Map<String, dynamic> json) => AlarmSettings(
id: json['id'] as int,
dateTime: DateTime.fromMicrosecondsSinceEpoch(json['dateTime'] as int),
assetAudioPath: json['assetAudioPath'] as String,
loopAudio: json['loopAudio'] as bool,
vibrate: json['vibrate'] as bool? ?? true,
volume: json['volume'] as double?,
fadeDuration: json['fadeDuration'] as double,
notificationTitle: json['notificationTitle'] as String? ?? '',
notificationBody: json['notificationBody'] as String? ?? '',
enableNotificationOnKill:
json['enableNotificationOnKill'] as bool? ?? true,
androidFullScreenIntent:
json['androidFullScreenIntent'] as bool? ?? true,
);

/// Creates a copy of `AlarmSettings` but with the given fields replaced with
/// the new values.
AlarmSettings copyWith({
Expand Down Expand Up @@ -163,10 +180,11 @@ class AlarmSettings {
/// Returns all the properties of `AlarmSettings` for debug purposes.
@override
String toString() {
Map<String, dynamic> json = toJson();
json['dateTime'] = DateTime.fromMicrosecondsSinceEpoch(json['dateTime']);
final json = toJson();
json['dateTime'] =
DateTime.fromMicrosecondsSinceEpoch(json['dateTime'] as int);

return "AlarmSettings: ${json.toString()}";
return 'AlarmSettings: $json';
}

/// Compares two AlarmSettings.
Expand Down
24 changes: 19 additions & 5 deletions lib/service/storage.dart → lib/service/alarm_storage.dart
Original file line number Diff line number Diff line change
@@ -1,17 +1,29 @@
import 'dart:convert';

import 'package:alarm/alarm.dart';
import 'package:alarm/model/alarm_settings.dart';
import 'package:shared_preferences/shared_preferences.dart';

/// Class that handles the local storage of the alarm info.
class AlarmStorage {
/// Prefix to be used in local storage to identify alarm info.
static const prefix = '__alarm_id__';

/// Key to be used in local storage to identify
/// notification on app kill title.
static const notificationOnAppKill = 'notificationOnAppKill';

/// Key to be used in local storage to identify
/// notification on app kill body.
static const notificationOnAppKillTitle = 'notificationOnAppKillTitle';

/// Key to be used in local storage to identify
/// notification on app kill body.
static const notificationOnAppKillBody = 'notificationOnAppKillBody';

/// Shared preferences instance.
static late SharedPreferences prefs;

/// Initializes shared preferences instance.
static Future<void> init() async {
prefs = await SharedPreferences.getInstance();
}
Expand All @@ -24,7 +36,7 @@ class AlarmStorage {
);

/// Removes alarm from local storage.
static Future<void> unsaveAlarm(int id) => prefs.remove("$prefix$id");
static Future<void> unsaveAlarm(int id) => prefs.remove('$prefix$id');

/// Whether at least one alarm is set.
static bool hasAlarm() {
Expand All @@ -46,7 +58,9 @@ class AlarmStorage {
for (final key in keys) {
if (key.startsWith(prefix)) {
final res = prefs.getString(key);
alarms.add(AlarmSettings.fromJson(json.decode(res!)));
alarms.add(
AlarmSettings.fromJson(json.decode(res!) as Map<String, dynamic>),
);
}
}

Expand All @@ -63,11 +77,11 @@ class AlarmStorage {
prefs.setString(notificationOnAppKillBody, body),
]);

/// Returns notification on app kill [title].
/// Returns notification on app kill [notificationOnAppKillTitle].
static String getNotificationOnAppKillTitle() =>
prefs.getString(notificationOnAppKillTitle) ?? 'Your alarms may not ring';

/// Returns notification on app kill [body].
/// Returns notification on app kill [notificationOnAppKillBody].
static String getNotificationOnAppKillBody() =>
prefs.getString(notificationOnAppKillBody) ??
'You killed the app. Please reopen so your alarms can be rescheduled.';
Expand Down
Loading

0 comments on commit 9232aa1

Please sign in to comment.