Added Android code
[wl-app.git] / iOS / Pods / FirebaseMessaging / Firebase / Messaging / FIRMessagingDelayedMessageQueue.m
1 /*
2  * Copyright 2017 Google
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #import "FIRMessagingDelayedMessageQueue.h"
18
19 #import "Protos/GtalkCore.pbobjc.h"
20
21 #import "FIRMessagingDefines.h"
22 #import "FIRMessagingRmqManager.h"
23 #import "FIRMessagingUtilities.h"
24
25 static const int kMaxQueuedMessageCount = 10;
26
27 @interface FIRMessagingDelayedMessageQueue ()
28
29 @property(nonatomic, readonly, weak) id<FIRMessagingRmqScanner> rmqScanner;
30 @property(nonatomic, readonly, copy) FIRMessagingSendDelayedMessagesHandler sendDelayedMessagesHandler;
31
32 @property(nonatomic, readwrite, assign) int persistedMessageCount;
33 // the scheduled timeout or -1 if not set
34 @property(nonatomic, readwrite, assign) int64_t scheduledTimeoutMilliseconds;
35 // The time  of the last scan of the message DB,
36 // used to avoid retrieving messages more than once.
37 @property(nonatomic, readwrite, assign) int64_t lastDBScanTimestampSeconds;
38
39 @property(nonatomic, readwrite, strong) NSMutableArray *messages;
40 @property(nonatomic, readwrite, strong) NSTimer *sendTimer;
41
42 @end
43
44 @implementation FIRMessagingDelayedMessageQueue
45
46 - (instancetype)init {
47   FIRMessagingInvalidateInitializer();
48 }
49
50 - (instancetype)initWithRmqScanner:(id<FIRMessagingRmqScanner>)rmqScanner
51         sendDelayedMessagesHandler:(FIRMessagingSendDelayedMessagesHandler)sendDelayedMessagesHandler {
52   _FIRMessagingDevAssert(sendDelayedMessagesHandler, @"Invalid nil callback for delayed messages");
53   self = [super init];
54   if (self) {
55     _rmqScanner = rmqScanner;
56     _sendDelayedMessagesHandler = sendDelayedMessagesHandler;
57     _messages = [NSMutableArray arrayWithCapacity:10];
58     _scheduledTimeoutMilliseconds = -1;
59   }
60   return self;
61 }
62
63 - (BOOL)queueMessage:(GtalkDataMessageStanza *)message {
64   if (self.messages.count >= kMaxQueuedMessageCount) {
65     return NO;
66   }
67   if (message.ttl == 0) {
68     // ttl=0 messages aren't persisted, add it to memory
69     [self.messages addObject:message];
70   } else {
71     self.persistedMessageCount++;
72   }
73   int64_t timeoutMillis = [self calculateTimeoutInMillisWithDelayInSeconds:message.maxDelay];
74   if (![self isTimeoutScheduled] || timeoutMillis < self.scheduledTimeoutMilliseconds) {
75     [self scheduleTimeoutInMillis:timeoutMillis];
76   }
77   return YES;
78 }
79
80 - (NSArray *)removeDelayedMessages {
81   [self cancelTimeout];
82   if ([self messageCount] == 0) {
83     return @[];
84   }
85
86   NSMutableArray *delayedMessages = [NSMutableArray array];
87   // add the ttl=0 messages
88   if (self.messages.count) {
89     [delayedMessages addObjectsFromArray:delayedMessages];
90     [self.messages removeAllObjects];
91   }
92
93   // add persistent messages
94   if (self.persistedMessageCount > 0) {
95     FIRMessaging_WEAKIFY(self);
96     [self.rmqScanner scanWithRmqMessageHandler:nil
97                             dataMessageHandler:^(int64_t rmqId, GtalkDataMessageStanza *stanza) {
98                               FIRMessaging_STRONGIFY(self);
99                               if ([stanza hasMaxDelay] &&
100                                   [stanza sent] >= self.lastDBScanTimestampSeconds) {
101                                 [delayedMessages addObject:stanza];
102                               }
103                             }];
104     self.lastDBScanTimestampSeconds = FIRMessagingCurrentTimestampInSeconds();
105     self.persistedMessageCount = 0;
106   }
107   return delayedMessages;
108 }
109
110 - (void)sendMessages {
111   if (self.sendDelayedMessagesHandler) {
112     self.sendDelayedMessagesHandler([self removeDelayedMessages]);
113   }
114 }
115
116 #pragma mark - Private
117
118 - (NSInteger)messageCount {
119   return self.messages.count + self.persistedMessageCount;
120 }
121
122 - (BOOL)isTimeoutScheduled {
123   return self.scheduledTimeoutMilliseconds > 0;
124 }
125
126 - (int64_t)calculateTimeoutInMillisWithDelayInSeconds:(int)delay {
127   return FIRMessagingCurrentTimestampInMilliseconds() + delay * 1000.0;
128 }
129
130 - (void)scheduleTimeoutInMillis:(int64_t)time {
131   [self cancelTimeout];
132   self.scheduledTimeoutMilliseconds = time;
133   double delay = (time - FIRMessagingCurrentTimestampInMilliseconds()) / 1000.0;
134   [self performSelector:@selector(sendMessages) withObject:self afterDelay:delay];
135 }
136
137 - (void)cancelTimeout {
138   if ([self isTimeoutScheduled]) {
139     [NSObject cancelPreviousPerformRequestsWithTarget:self
140                                              selector:@selector(sendMessages)
141                                                object:nil];
142     self.scheduledTimeoutMilliseconds = -1;
143   }
144 }
145
146 @end