AutorÃa | Ultima modificación | Ver Log |
/** Copyright 2019 Google** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at** http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/#import <nanopb/pb.h>#import <nanopb/pb_decode.h>#import <nanopb/pb_encode.h>#import <GoogleDataTransport/GoogleDataTransport.h>#import <GoogleUtilities/GULAppEnvironmentUtil.h>#import "FirebaseMessaging/Sources/FIRMessagingCode.h"#import "FirebaseMessaging/Sources/FIRMessagingConstants.h"#import "FirebaseMessaging/Sources/FIRMessagingLogger.h"#import "FirebaseMessaging/Sources/Protogen/nanopb/me.nanopb.h"#import "FirebaseMessaging/Sources/Public/FirebaseMessaging/FIRMessagingExtensionHelper.h"static NSString *const kPayloadOptionsName = @"fcm_options";static NSString *const kPayloadOptionsImageURLName = @"image";static NSString *const kNoExtension = @"";static NSString *const kImagePathPrefix = @"image/";#pragma mark - nanopb helper functions/** Callocs a pb_bytes_array and copies the given NSData bytes into the bytes array.** @note Memory needs to be free manually, through pb_free or pb_release.* @param data The data to copy into the new bytes array.*/pb_bytes_array_t *FIRMessagingEncodeData(NSData *data) {pb_bytes_array_t *pbBytesArray = calloc(1, PB_BYTES_ARRAY_T_ALLOCSIZE(data.length));if (pbBytesArray != NULL) {[data getBytes:pbBytesArray->bytes length:data.length];pbBytesArray->size = (pb_size_t)data.length;}return pbBytesArray;}/** Callocs a pb_bytes_array and copies the given NSString's bytes into the bytes array.** @note Memory needs to be free manually, through pb_free or pb_release.* @param string The string to encode as pb_bytes.*/pb_bytes_array_t *FIRMessagingEncodeString(NSString *string) {NSData *stringBytes = [string dataUsingEncoding:NSUTF8StringEncoding];return FIRMessagingEncodeData(stringBytes);}@interface FIRMessagingMetricsLog : NSObject <GDTCOREventDataObject>@property(nonatomic) fm_MessagingClientEventExtension eventExtension;@end@implementation FIRMessagingMetricsLog- (instancetype)initWithEventExtension:(fm_MessagingClientEventExtension)eventExtension {self = [super init];if (self) {_eventExtension = eventExtension;}return self;}- (NSData *)transportBytes {pb_ostream_t sizestream = PB_OSTREAM_SIZING;// Encode 1 time to determine the size.if (!pb_encode(&sizestream, fm_MessagingClientEventExtension_fields, &_eventExtension)) {FIRMessagingLoggerError(kFIRMessagingServiceExtensionTransportBytesError,@"Error in nanopb encoding for size: %s", PB_GET_ERROR(&sizestream));}// Encode a 2nd time to actually get the bytes from it.size_t bufferSize = sizestream.bytes_written;CFMutableDataRef dataRef = CFDataCreateMutable(CFAllocatorGetDefault(), bufferSize);CFDataSetLength(dataRef, bufferSize);pb_ostream_t ostream = pb_ostream_from_buffer((void *)CFDataGetBytePtr(dataRef), bufferSize);if (!pb_encode(&ostream, fm_MessagingClientEventExtension_fields, &_eventExtension)) {FIRMessagingLoggerError(kFIRMessagingServiceExtensionTransportBytesError,@"Error in nanopb encoding for bytes: %s", PB_GET_ERROR(&ostream));}CFDataSetLength(dataRef, ostream.bytes_written);return CFBridgingRelease(dataRef);}@end@interface FIRMessagingExtensionHelper ()@property(nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver);@property(nonatomic, strong) UNMutableNotificationContent *bestAttemptContent;@end@implementation FIRMessagingExtensionHelper- (void)populateNotificationContent:(UNMutableNotificationContent *)contentwithContentHandler:(void (^)(UNNotificationContent *_Nonnull))contentHandler {self.contentHandler = [contentHandler copy];self.bestAttemptContent = content;// The `userInfo` property isn't available on newer versions of tvOS.#if TARGET_OS_IOS || TARGET_OS_OSX || TARGET_OS_WATCHNSString *currentImageURL = content.userInfo[kPayloadOptionsName][kPayloadOptionsImageURLName];if (!currentImageURL) {[self deliverNotification];return;}NSURL *attachmentURL = [NSURL URLWithString:currentImageURL];if (attachmentURL) {[self loadAttachmentForURL:attachmentURLcompletionHandler:^(UNNotificationAttachment *attachment) {if (attachment != nil) {self.bestAttemptContent.attachments = @[ attachment ];}[self deliverNotification];}];} else {FIRMessagingLoggerError(kFIRMessagingServiceExtensionImageInvalidURL,@"The Image URL provided is invalid %@.", currentImageURL);[self deliverNotification];}#else[self deliverNotification];#endif}#if TARGET_OS_IOS || TARGET_OS_OSX || TARGET_OS_WATCH- (NSString *)fileExtensionForResponse:(NSURLResponse *)response {NSString *suggestedPathExtension = [response.suggestedFilename pathExtension];if (suggestedPathExtension.length > 0) {return [NSString stringWithFormat:@".%@", suggestedPathExtension];}if ([response.MIMEType containsString:kImagePathPrefix]) {return [response.MIMEType stringByReplacingOccurrencesOfString:kImagePathPrefixwithString:@"."];}return kNoExtension;}- (void)loadAttachmentForURL:(NSURL *)attachmentURLcompletionHandler:(void (^)(UNNotificationAttachment *))completionHandler {__block UNNotificationAttachment *attachment = nil;NSURLSession *session = [NSURLSessionsessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];[[sessiondownloadTaskWithURL:attachmentURLcompletionHandler:^(NSURL *temporaryFileLocation, NSURLResponse *response, NSError *error) {if (error != nil) {FIRMessagingLoggerError(kFIRMessagingServiceExtensionImageNotDownloaded,@"Failed to download image given URL %@, error: %@\n",attachmentURL, error);completionHandler(attachment);return;}NSFileManager *fileManager = [NSFileManager defaultManager];NSString *fileExtension = [self fileExtensionForResponse:response];NSURL *localURL = [NSURLfileURLWithPath:[temporaryFileLocation.path stringByAppendingString:fileExtension]];[fileManager moveItemAtURL:temporaryFileLocation toURL:localURL error:&error];if (error) {FIRMessagingLoggerError(kFIRMessagingServiceExtensionLocalFileNotCreated,@"Failed to move the image file to local location: %@, error: %@\n", localURL,error);completionHandler(attachment);return;}attachment = [UNNotificationAttachment attachmentWithIdentifier:@""URL:localURLoptions:nilerror:&error];if (error) {FIRMessagingLoggerError(kFIRMessagingServiceExtensionImageNotAttached,@"Failed to create attachment with URL %@, error: %@\n",localURL, error);completionHandler(attachment);return;}completionHandler(attachment);}] resume];}#endif- (void)deliverNotification {if (self.contentHandler) {self.contentHandler(self.bestAttemptContent);}}- (void)exportDeliveryMetricsToBigQueryWithMessageInfo:(NSDictionary *)info {GDTCORTransport *transport = [[GDTCORTransport alloc] initWithMappingID:@"1249"transformers:niltarget:kGDTCORTargetFLL];fm_MessagingClientEventExtension eventExtension = fm_MessagingClientEventExtension_init_default;fm_MessagingClientEvent clientEvent = fm_MessagingClientEvent_init_default;if (!info[kFIRMessagingSenderID]) {FIRMessagingLoggerError(kFIRMessagingServiceExtensionInvalidProjectID,@"Delivery logging failed: Invalid project ID");return;}clientEvent.project_number = (int64_t)[info[kFIRMessagingSenderID] longLongValue];if (!info[kFIRMessagingMessageIDKey] ||![info[kFIRMessagingMessageIDKey] isKindOfClass:NSString.class]) {FIRMessagingLoggerWarn(kFIRMessagingServiceExtensionInvalidMessageID,@"Delivery logging failed: Invalid Message ID");return;}clientEvent.message_id = FIRMessagingEncodeString(info[kFIRMessagingMessageIDKey]);if (!info[kFIRMessagingFID] || ![info[kFIRMessagingFID] isKindOfClass:NSString.class]) {FIRMessagingLoggerWarn(kFIRMessagingServiceExtensionInvalidInstanceID,@"Delivery logging failed: Invalid Instance ID");return;}clientEvent.instance_id = FIRMessagingEncodeString(info[kFIRMessagingFID]);if ([info[@"aps"][kFIRMessagingMessageAPNSContentAvailableKey] intValue] == 1 &&![GULAppEnvironmentUtil isAppExtension]) {clientEvent.message_type = fm_MessagingClientEvent_MessageType_DATA_MESSAGE;} else {clientEvent.message_type = fm_MessagingClientEvent_MessageType_DISPLAY_NOTIFICATION;}clientEvent.sdk_platform = fm_MessagingClientEvent_SDKPlatform_IOS;NSString *bundleID = [NSBundle mainBundle].bundleIdentifier;if ([GULAppEnvironmentUtil isAppExtension]) {bundleID = [[self class] bundleIdentifierByRemovingLastPartFrom:bundleID];}if (bundleID) {clientEvent.package_name = FIRMessagingEncodeString(bundleID);}clientEvent.event = fm_MessagingClientEvent_Event_MESSAGE_DELIVERED;if (info[kFIRMessagingAnalyticsMessageLabel]) {clientEvent.analytics_label =FIRMessagingEncodeString(info[kFIRMessagingAnalyticsMessageLabel]);}if (info[kFIRMessagingAnalyticsComposerIdentifier]) {clientEvent.campaign_id =(int64_t)[info[kFIRMessagingAnalyticsComposerIdentifier] longLongValue];}if (info[kFIRMessagingAnalyticsComposerLabel]) {clientEvent.composer_label =FIRMessagingEncodeString(info[kFIRMessagingAnalyticsComposerLabel]);}eventExtension.messaging_client_event = &clientEvent;FIRMessagingMetricsLog *log =[[FIRMessagingMetricsLog alloc] initWithEventExtension:eventExtension];GDTCOREvent *event = [transport eventForTransport];event.dataObject = log;event.qosTier = GDTCOREventQoSFast;// Use this API for SDK service data events.[transport sendDataEvent:event];}+ (NSString *)bundleIdentifierByRemovingLastPartFrom:(NSString *)bundleIdentifier {NSString *bundleIDComponentsSeparator = @".";NSMutableArray<NSString *> *bundleIDComponents =[[bundleIdentifier componentsSeparatedByString:bundleIDComponentsSeparator] mutableCopy];[bundleIDComponents removeLastObject];return [bundleIDComponents componentsJoinedByString:bundleIDComponentsSeparator];}@end