Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Implemented daily notification with custom time #33

Merged
merged 4 commits into from
May 15, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import android.text.Spanned;

import com.dexterous.flutterlocalnotifications.models.NotificationDetails;
import com.dexterous.flutterlocalnotifications.models.Time;
import com.dexterous.flutterlocalnotifications.models.styles.BigTextStyleInformation;
import com.dexterous.flutterlocalnotifications.models.styles.DefaultStyleInformation;
import com.dexterous.flutterlocalnotifications.models.styles.InboxStyleInformation;
Expand All @@ -32,6 +33,7 @@

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Iterator;
import java.util.Map;

Expand All @@ -54,6 +56,7 @@ public class FlutterLocalNotificationsPlugin implements MethodCallHandler, Plugi
private static final String CANCEL_ALL_METHOD = "cancelAll";
private static final String SCHEDULE_METHOD = "schedule";
private static final String PERIODICALLY_SHOW_METHOD = "periodicallyShow";
private static final String SHOW_DAILY_AT_TIME = "showDailyAtTime";
private static final String METHOD_CHANNEL = "dexterous.com/flutter/local_notifications";
private static final String PAYLOAD = "payload";
public static String NOTIFICATION_ID = "notification_id";
Expand Down Expand Up @@ -170,32 +173,39 @@ private static void repeatNotification(Context context, NotificationDetails noti
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, notificationDetails.id, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);

AlarmManager alarmManager = getAlarmManager(context);
long startTimeMilliseconds = notificationDetails.calledAt;
long repeatInterval = 0;
switch(notificationDetails.repeatInterval) {
case EveryMinute:
repeatInterval = 60000;
break;
case Hourly:
repeatInterval = 60000 * 60;
break;
case Daily:
repeatInterval = 60000 * 60 * 24;
break;
case Weekly:
repeatInterval = 60000 * 60 * 24 * 7;
break;
default:
break;
}
if (notificationDetails.repeatTime != null) {
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, notificationDetails.repeatTime.hour);
calendar.set(Calendar.MINUTE, notificationDetails.repeatTime.minute);
calendar.set(Calendar.SECOND, notificationDetails.repeatTime.second);
alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pendingIntent);
} else {
long startTimeMilliseconds = notificationDetails.calledAt;
long repeatInterval = 0;
switch(notificationDetails.repeatInterval) {
case EveryMinute:
repeatInterval = 60000;
break;
case Hourly:
repeatInterval = 60000 * 60;
break;
case Daily:
repeatInterval = 60000 * 60 * 24;
break;
case Weekly:
repeatInterval = 60000 * 60 * 24 * 7;
break;
default:
break;
}

long currentTime = System.currentTimeMillis();
while(startTimeMilliseconds < currentTime) {
startTimeMilliseconds += repeatInterval;
long currentTime = System.currentTimeMillis();
while(startTimeMilliseconds < currentTime) {
startTimeMilliseconds += repeatInterval;
}
alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, startTimeMilliseconds, repeatInterval, pendingIntent);
}


alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, startTimeMilliseconds, repeatInterval, pendingIntent);
if (updateScheduledNotificationsCache) {
ArrayList<NotificationDetails> scheduledNotifications = loadScheduledNotifications(context);
scheduledNotifications.add(notificationDetails);
Expand Down Expand Up @@ -404,6 +414,13 @@ public void onMethodCall(MethodCall call, Result result) {
result.success(null);
break;
}
case SHOW_DAILY_AT_TIME: {
Map<String, Object> arguments = call.arguments();
NotificationDetails notificationDetails = NotificationDetails.from(arguments);
repeatNotification(registrar.context(), notificationDetails, true);
result.success(null);
break;
}
case CANCEL_METHOD:
Integer id = call.arguments();
cancelNotification(id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public class NotificationDetails {
private static final String MILLISECONDS_SINCE_EPOCH = "millisecondsSinceEpoch";
private static final String CALLED_AT = "calledAt";
private static final String REPEAT_INTERVAL = "repeatInterval";
private static final String REPEAT_TIME = "repeatTime";
private static final String PLATFORM_SPECIFICS = "platformSpecifics";
private static final String AUTO_CANCEL = "autoCancel";
private static final String ONGOING = "ongoing";
Expand Down Expand Up @@ -65,6 +66,7 @@ public class NotificationDetails {
public NotificationStyle style;
public StyleInformation styleInformation;
public RepeatInterval repeatInterval;
public Time repeatTime;
public Long millisecondsSinceEpoch;
public Long calledAt;
public String payload;
Expand Down Expand Up @@ -92,6 +94,11 @@ public static NotificationDetails from(Map<String, Object> arguments) {
if(arguments.containsKey(REPEAT_INTERVAL)) {
notificationDetails.repeatInterval = RepeatInterval.values()[(Integer)arguments.get(REPEAT_INTERVAL)];
}
if (arguments.containsKey(REPEAT_TIME)) {
@SuppressWarnings("unchecked")
Map<String, Object> repeatTimeParams = (Map<String, Object>)arguments.get(REPEAT_TIME);
notificationDetails.repeatTime = Time.from(repeatTimeParams);
}
@SuppressWarnings("unchecked")
Map<String, Object> platformChannelSpecifics = (Map<String, Object>) arguments.get(PLATFORM_SPECIFICS);
if (platformChannelSpecifics != null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.dexterous.flutterlocalnotifications.models;

import java.util.Map;

public class Time {
private static final String HOUR = "hour";
private static final String MINUTE = "minute";
private static final String SECOND = "second";

public Integer hour = 0;
public Integer minute = 0;
public Integer second = 0;

public static Time from(Map<String, Object> arguments) {
Time time = new Time();
time.hour = (Integer) arguments.get(HOUR);
time.minute = (Integer) arguments.get(MINUTE);
time.second = (Integer) arguments.get(SECOND);
return time;
}
}
19 changes: 19 additions & 0 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,13 @@ class _MyAppState extends State<MyApp> {
onPressed: () async {
await _repeatNotification();
})),
new Padding(
padding: new EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 8.0),
child: new RaisedButton(
child: new Text('Repeat notification every day at 10:00:00 am (aprox)'),
onPressed: () async {
await _repeatDailyAtTime(new Time(10, 0, 0));
})),
new Padding(
padding: new EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 8.0),
child: new RaisedButton(
Expand Down Expand Up @@ -328,6 +335,18 @@ class _MyAppState extends State<MyApp> {
await flutterLocalNotificationsPlugin.periodicallyShow(0, 'repeating title',
'repeating body', RepeatInterval.EveryMinute, platformChannelSpecifics);
}

Future _repeatDailyAtTime(Time time) async {
NotificationDetailsAndroid androidPlatformChannelSpecifics =
new NotificationDetailsAndroid('repeatDailyAtTime channel id',
'repeatDailyAtTime channel name', 'repeatDailyAtTime description');
NotificationDetailsIOS iOSPlatformChannelSpecifics =
new NotificationDetailsIOS();
NotificationDetails platformChannelSpecifics = new NotificationDetails(
androidPlatformChannelSpecifics, iOSPlatformChannelSpecifics);
await flutterLocalNotificationsPlugin.showDailyAtTime(0, 'repeatDailyAtTime title',
'It\'s ${time.hour}:${time.minute}:${time.second} approximately' , time, platformChannelSpecifics);
}
}

class SecondScreen extends StatefulWidget {
Expand Down
56 changes: 47 additions & 9 deletions ios/Classes/FlutterLocalNotificationsPlugin.m
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#import "FlutterLocalNotificationsPlugin.h"
#import "NotificationTime.h"

static bool appResumingFromBackground;

Expand All @@ -9,6 +10,7 @@ @implementation FlutterLocalNotificationsPlugin
NSString *const SHOW_METHOD = @"show";
NSString *const SCHEDULE_METHOD = @"schedule";
NSString *const PERIODICALLY_SHOW_METHOD = @"periodicallyShow";
NSString *const SHOW_DAILY_AT_TIME_METHOD = @"showDailyAtTime";
NSString *const CANCEL_METHOD = @"cancel";
NSString *const CANCEL_ALL_METHOD = @"cancelAll";
NSString *const CHANNEL = @"dexterous.com/flutter/local_notifications";
Expand All @@ -29,6 +31,10 @@ @implementation FlutterLocalNotificationsPlugin
NSString *const PRESENT_BADGE = @"presentBadge";
NSString *const MILLISECONDS_SINCE_EPOCH = @"millisecondsSinceEpoch";
NSString *const REPEAT_INTERVAL = @"repeatInterval";
NSString *const REPEAT_TIME = @"repeatTime";
NSString *const HOUR = @"hour";
NSString *const MINUTE = @"minute";
NSString *const SECOND = @"second";

NSString *const NOTIFICATION_ID = @"NotificationId";
NSString *const PAYLOAD = @"payload";
Expand Down Expand Up @@ -149,15 +155,29 @@ - (void)showNotification:(FlutterMethodCall * _Nonnull)call result:(FlutterResul
}
NSNumber *secondsSinceEpoch;
NSNumber *repeatInterval;
NotificationTime *repeatTime;
if([SCHEDULE_METHOD isEqualToString:call.method]) {
secondsSinceEpoch = @([call.arguments[MILLISECONDS_SINCE_EPOCH] integerValue] / 1000);
} else if([PERIODICALLY_SHOW_METHOD isEqualToString:call.method]) {
} else if([PERIODICALLY_SHOW_METHOD isEqualToString:call.method] || [SHOW_DAILY_AT_TIME_METHOD isEqualToString:call.method]) {
if (call.arguments[REPEAT_TIME] != [NSNull null]) {
NSDictionary *timeArguments = (NSDictionary *) call.arguments[REPEAT_TIME];
repeatTime = [[NotificationTime alloc] init];
if (timeArguments[HOUR] != [NSNull null]) {
repeatTime.hour = @([timeArguments[HOUR] integerValue]);
}
if (timeArguments[MINUTE] != [NSNull null]) {
repeatTime.minute = @([timeArguments[MINUTE] integerValue]);
}
if (timeArguments[SECOND] != [NSNull null]) {
repeatTime.second = @([timeArguments[SECOND] integerValue]);
}
}
repeatInterval = @([call.arguments[REPEAT_INTERVAL] integerValue]);
}
if(@available(iOS 10.0, *)) {
[self showUserNotification:id title:title body:body secondsSinceEpoch:secondsSinceEpoch repeatInterval:repeatInterval presentAlert:presentAlert presentSound:presentSound presentBadge:presentBadge sound:sound payload:payload];
[self showUserNotification:id title:title body:body secondsSinceEpoch:secondsSinceEpoch repeatInterval:repeatInterval repeatTime:repeatTime presentAlert:presentAlert presentSound:presentSound presentBadge:presentBadge sound:sound payload:payload];
} else {
[self showLocalNotification:id title:title body:body secondsSinceEpoch:secondsSinceEpoch repeatInterval:repeatInterval presentAlert:presentAlert presentSound:presentSound presentBadge:presentBadge sound:sound payload:payload];
[self showLocalNotification:id title:title body:body secondsSinceEpoch:secondsSinceEpoch repeatInterval:repeatInterval repeatTime:repeatTime presentAlert:presentAlert presentSound:presentSound presentBadge:presentBadge sound:sound payload:payload];
}
result(nil);
}
Expand Down Expand Up @@ -198,7 +218,7 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
if([INITIALIZE_METHOD isEqualToString:call.method]) {
[self initialize:call result:result];

} else if ([SHOW_METHOD isEqualToString:call.method] || [SCHEDULE_METHOD isEqualToString:call.method] || [PERIODICALLY_SHOW_METHOD isEqualToString:call.method]) {
} else if ([SHOW_METHOD isEqualToString:call.method] || [SCHEDULE_METHOD isEqualToString:call.method] || [PERIODICALLY_SHOW_METHOD isEqualToString:call.method] || [SHOW_DAILY_AT_TIME_METHOD isEqualToString:call.method]) {
[self showNotification:call result:result];
} else if([CANCEL_METHOD isEqualToString:call.method]) {
[self cancelNotification:call result:result];
Expand All @@ -215,7 +235,7 @@ - (NSDictionary*)buildUserDict:(NSNumber *)id title:(NSString *)title presentAle
return userDict;
}

- (void) showUserNotification:(NSNumber *)id title:(NSString *)title body:(NSString *)body secondsSinceEpoch:(NSNumber *)secondsSinceEpoch repeatInterval:(NSNumber *)repeatInterval presentAlert:(bool)presentAlert presentSound:(bool)presentSound presentBadge:(bool)presentBadge sound:(NSString*)sound payload:(NSString *)payload NS_AVAILABLE_IOS(10.0) {
- (void) showUserNotification:(NSNumber *)id title:(NSString *)title body:(NSString *)body secondsSinceEpoch:(NSNumber *)secondsSinceEpoch repeatInterval:(NSNumber *)repeatInterval repeatTime:(NotificationTime *)repeatTime presentAlert:(bool)presentAlert presentSound:(bool)presentSound presentBadge:(bool)presentBadge sound:(NSString*)sound payload:(NSString *)payload NS_AVAILABLE_IOS(10.0) {
UNMutableNotificationContent* content = [[UNMutableNotificationContent alloc] init];
UNNotificationTrigger *trigger;
content.title = title;
Expand Down Expand Up @@ -248,8 +268,16 @@ - (void) showUserNotification:(NSNumber *)id title:(NSString *)title body:(NSStr
}
repeats = YES;
}
trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:timeInterval
repeats:repeats];
if (repeatTime != nil) {
NSDateComponents *dateComponent = [[NSDateComponents alloc] init];
[dateComponent setHour:[repeatTime.hour integerValue]];
[dateComponent setMinute:[repeatTime.minute integerValue]];
[dateComponent setSecond:[repeatTime.second integerValue]];
trigger = [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:dateComponent repeats: repeats];
} else {
trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:timeInterval
repeats:repeats];
}
} else {
NSDate *date = [NSDate dateWithTimeIntervalSince1970:[secondsSinceEpoch integerValue]];
NSCalendar *currentCalendar = [NSCalendar currentCalendar];
Expand All @@ -272,7 +300,7 @@ - (void) showUserNotification:(NSNumber *)id title:(NSString *)title body:(NSStr

}

- (void) showLocalNotification:(NSNumber *)id title:(NSString *)title body:(NSString *)body secondsSinceEpoch:(NSNumber *)secondsSinceEpoch repeatInterval:(NSNumber *)repeatInterval presentAlert:(bool)presentAlert presentSound:(bool)presentSound presentBadge:(bool)presentBadge sound:(NSString*)sound payload:(NSString *)payload {
- (void) showLocalNotification:(NSNumber *)id title:(NSString *)title body:(NSString *)body secondsSinceEpoch:(NSNumber *)secondsSinceEpoch repeatInterval:(NSNumber *)repeatInterval repeatTime:(NotificationTime *)repeatTime presentAlert:(bool)presentAlert presentSound:(bool)presentSound presentBadge:(bool)presentBadge sound:(NSString*)sound payload:(NSString *)payload {
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.alertBody = body;
if(@available(iOS 8.2, *)) {
Expand Down Expand Up @@ -310,7 +338,17 @@ - (void) showLocalNotification:(NSNumber *)id title:(NSString *)title body:(NSSt
notification.repeatInterval = NSCalendarUnitWeekOfYear;
break;
}
notification.fireDate = [NSDate dateWithTimeIntervalSinceNow:timeInterval];
if (repeatTime != nil) {
NSDate *now = [NSDate date];
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier: NSCalendarIdentifierGregorian];
NSDateComponents *components = [calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:now];
[components setHour:[repeatTime.hour integerValue]];
[components setMinute:[repeatTime.minute integerValue]];
[components setSecond:[repeatTime.second integerValue]];
notification.fireDate = [calendar dateFromComponents:components];
} else {
notification.fireDate = [NSDate dateWithTimeIntervalSinceNow:timeInterval];
}
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
return;
}
Expand Down
7 changes: 7 additions & 0 deletions ios/Classes/NotificationTime.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#import <Foundation/Foundation.h>

@interface NotificationTime : NSObject
@property(nonatomic, strong) NSNumber *hour;
@property(nonatomic, strong) NSNumber *minute;
@property(nonatomic, strong) NSNumber *second;
@end
5 changes: 5 additions & 0 deletions ios/Classes/NotificationTime.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#import "NotificationTime.h"

@implementation NotificationTime

@end
39 changes: 38 additions & 1 deletion lib/flutter_local_notifications.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,26 @@ typedef Future<dynamic> MessageHandler(String message);
/// The available intervals for periodically showing notifications
enum RepeatInterval { EveryMinute, Hourly, Daily, Weekly }

class Time {
final int hour;
final int minute;
final int second;

Time([this.hour = 0, this.minute = 0, this.second = 0]) {
assert(this.hour >= 0 && this.hour < 24);
assert(this.minute >= 0 && this.minute < 60);
assert(this.second >= 0 && this.second < 60);
}

Map<String, int> toMap() {
return <String, int> {
"hour": hour,
"minute": minute,
"second": second,
};
}
}

class FlutterLocalNotificationsPlugin {
factory FlutterLocalNotificationsPlugin() => _instance;

Expand Down Expand Up @@ -100,6 +120,23 @@ class FlutterLocalNotificationsPlugin {
});
}

Future showDailyAtTime(int id, String title, String body,
Time notificationTime, NotificationDetails notificationDetails,
{String payload}) async {
Map<String, dynamic> serializedPlatformSpecifics =
_retrievePlatformSpecificNotificationDetails(notificationDetails);
await _channel.invokeMethod('showDailyAtTime', <String, dynamic>{
'id': id,
'title': title,
'body': body,
'calledAt': new DateTime.now().millisecondsSinceEpoch,
'repeatInterval': RepeatInterval.Daily.index,
'repeatTime': notificationTime.toMap(),
'platformSpecifics': serializedPlatformSpecifics,
'payload': payload ?? ''
});
}

Map<String, dynamic> _retrievePlatformSpecificNotificationDetails(
NotificationDetails notificationDetails) {
Map<String, dynamic> serializedPlatformSpecifics;
Expand All @@ -125,4 +162,4 @@ class FlutterLocalNotificationsPlugin {
Future _handleMethod(MethodCall call) {
return onSelectNotification(call.arguments);
}
}
}