2017-08-17 15:28:08 +00:00
/ *
Copyright 2017 Aram Sargsyan
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
2017-09-08 15:02:45 +00:00
distributed under the License is distributed on an "AS IS" BASIS ,
2017-08-17 15:28:08 +00:00
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 .
* /
2017-08-10 22:38:47 +00:00
# import "ShareExtensionManager.h"
2017-08-25 09:55:37 +00:00
# import "SharePresentingViewController.h"
2017-11-23 15:09:19 +00:00
# import < MatrixKit / MatrixKit . h >
2017-08-10 22:38:47 +00:00
@ import MobileCoreServices ;
2017-08-22 15:52:32 +00:00
# import "objc/runtime.h"
2017-08-10 22:38:47 +00:00
2017-09-29 08:37:54 +00:00
NSString * const kShareExtensionManagerDidUpdateAccountDataNotification = @ "kShareExtensionManagerDidUpdateAccountDataNotification" ;
2017-08-21 20:33:06 +00:00
2017-08-16 20:01:54 +00:00
typedef NS_ENUM ( NSInteger , ImageCompressionMode )
{
ImageCompressionModeNone ,
ImageCompressionModeSmall ,
ImageCompressionModeMedium ,
ImageCompressionModeLarge
} ;
2017-09-08 15:02:45 +00:00
2017-08-16 20:01:54 +00:00
@ interface ShareExtensionManager ( )
2017-09-08 15:02:45 +00:00
@ property ( nonatomic , readwrite ) MXKAccount * userAccount ;
2017-08-26 11:54:25 +00:00
@ property ( nonatomic ) NSMutableArray < NSData * > * pendingImages ;
@ property ( nonatomic ) NSMutableDictionary < NSString * , NSNumber * > * imageUploadProgresses ;
@ property ( nonatomic ) ImageCompressionMode imageCompressionMode ;
@ property ( nonatomic ) CGFloat actualLargeSize ;
2017-08-16 20:01:54 +00:00
@ end
2017-08-10 22:38:47 +00:00
@ implementation ShareExtensionManager
# pragma mark - Lifecycle
+ ( instancetype ) sharedManager
{
static ShareExtensionManager * sharedInstance = nil ;
static dispatch_once _t onceToken ;
dispatch_once ( & onceToken , ^ {
2017-09-29 08:37:54 +00:00
2017-08-10 22:38:47 +00:00
sharedInstance = [ [ self alloc ] init ] ;
2017-08-21 20:33:06 +00:00
2017-08-22 15:52:32 +00:00
sharedInstance . pendingImages = [ NSMutableArray array ] ;
sharedInstance . imageUploadProgresses = [ NSMutableDictionary dictionary ] ;
2017-08-22 15:56:30 +00:00
2017-08-16 20:01:54 +00:00
[ [ NSNotificationCenter defaultCenter ] addObserver : sharedInstance selector : @ selector ( onMediaUploadProgress : ) name : kMXMediaUploadProgressNotification object : nil ] ;
2017-08-21 20:33:06 +00:00
2017-09-08 15:02:45 +00:00
// Add observer to handle logout
[ [ NSNotificationCenter defaultCenter ] addObserver : sharedInstance selector : @ selector ( checkUserAccount ) name : kMXKAccountManagerDidRemoveAccountNotification object : nil ] ;
// Add observer on the Extension host
[ [ NSNotificationCenter defaultCenter ] addObserver : sharedInstance selector : @ selector ( checkUserAccount ) name : NSExtensionHostWillEnterForegroundNotification object : nil ] ;
2017-08-25 09:55:37 +00:00
MXSDKOptions * sdkOptions = [ MXSDKOptions sharedInstance ] ;
2017-08-21 20:33:06 +00:00
// Apply the application group
2017-08-25 09:55:37 +00:00
sdkOptions . applicationGroupIdentifier = @ "group.im.vector" ;
// Disable identicon use
sdkOptions . disableIdenticonUseForUserAvatar = YES ;
2017-09-29 08:37:54 +00:00
// Enable e2e encryption for newly created MXSession
sdkOptions . enableCryptoWhenStartingMXSession = YES ;
2017-12-27 13:00:16 +00:00
// Customize the localized string table
[ NSBundle mxk_customizeLocalizedStringTableName : @ "Vector" ] ;
2017-10-20 12:57:51 +00:00
// NSLog -> console . log file when not debugging the app
if ( ! isatty ( STDERR_FILENO ) )
{
2017-10-20 15:13:12 +00:00
[ MXLogger setSubLogName : @ "share" ] ;
2017-10-20 12:57:51 +00:00
[ MXLogger redirectNSLogToFiles : YES ] ;
}
2017-08-10 22:38:47 +00:00
} ) ;
return sharedInstance ;
}
2017-09-08 15:02:45 +00:00
- ( void ) checkUserAccount
{
// Force account manager to reload account from the local storage .
[ [ MXKAccountManager sharedManager ] forceReloadAccounts ] ;
if ( self . userAccount )
{
// Check whether the used account is still the first active one
MXKAccount * firstAccount = [ MXKAccountManager sharedManager ] . activeAccounts . firstObject ;
// Compare the access token
if ( ! firstAccount || ! [ self . userAccount . mxCredentials . accessToken isEqualToString : firstAccount . mxCredentials . accessToken ] )
{
// Remove this account
self . userAccount = nil ;
}
}
2017-09-29 08:37:54 +00:00
if ( ! self . userAccount )
{
// We consider the first enabled account .
// TODO : Handle multiple accounts
self . userAccount = [ MXKAccountManager sharedManager ] . activeAccounts . firstObject ;
}
// Reset the file store to reload the room data .
if ( _fileStore )
{
[ _fileStore close ] ;
_fileStore = nil ;
}
2017-09-08 15:02:45 +00:00
if ( self . userAccount )
{
2017-09-29 08:37:54 +00:00
_fileStore = [ [ MXFileStore alloc ] initWithCredentials : self . userAccount . mxCredentials ] ;
2017-09-08 15:02:45 +00:00
}
2017-09-29 08:37:54 +00:00
// Post notification
[ [ NSNotificationCenter defaultCenter ] postNotificationName : kShareExtensionManagerDidUpdateAccountDataNotification object : self . userAccount userInfo : nil ] ;
2017-09-08 15:02:45 +00:00
}
2017-08-14 11:25:02 +00:00
# pragma mark - Public
2017-08-21 20:33:06 +00:00
- ( void ) setShareExtensionContext : ( NSExtensionContext * ) shareExtensionContext
{
_shareExtensionContext = shareExtensionContext ;
2017-09-29 08:37:54 +00:00
2017-12-27 13:00:16 +00:00
// Set up runtime language on each context update .
NSUserDefaults * sharedUserDefaults = [ MXKAppSettings standardAppSettings ] . sharedUserDefaults ;
NSString * language = [ sharedUserDefaults objectForKey : @ "appLanguage" ] ;
[ NSBundle mxk_setLanguage : language ] ;
[ NSBundle mxk_setFallbackLanguage : @ "en" ] ;
2017-09-29 08:37:54 +00:00
// Check the current matrix user .
[ self checkUserAccount ] ;
2017-08-21 20:33:06 +00:00
}
2017-09-29 08:37:54 +00:00
- ( void ) sendContentToRoom : ( MXRoom * ) room failureBlock : ( void ( ^ ) ( NSError * error ) ) failureBlock
2017-08-10 22:38:47 +00:00
{
NSString * UTTypeText = ( __bridge NSString * ) kUTTypeText ;
NSString * UTTypeURL = ( __bridge NSString * ) kUTTypeURL ;
NSString * UTTypeImage = ( __bridge NSString * ) kUTTypeImage ;
NSString * UTTypeVideo = ( __bridge NSString * ) kUTTypeVideo ;
2017-08-14 11:25:02 +00:00
NSString * UTTypeFileUrl = ( __bridge NSString * ) kUTTypeFileURL ;
NSString * UTTypeMovie = ( __bridge NSString * ) kUTTypeMovie ;
2017-08-10 22:38:47 +00:00
2017-08-17 15:28:08 +00:00
__weak typeof ( self ) weakSelf = self ;
2017-08-16 20:01:54 +00:00
2017-11-09 14:59:42 +00:00
[ self resetPendingData ] ;
2017-08-24 10:55:35 +00:00
2017-08-10 22:38:47 +00:00
for ( NSExtensionItem * item in self . shareExtensionContext . inputItems )
{
for ( NSItemProvider * itemProvider in item . attachments )
{
2017-08-14 11:25:02 +00:00
if ( [ itemProvider hasItemConformingToTypeIdentifier : UTTypeFileUrl ] )
{
[ itemProvider loadItemForTypeIdentifier : UTTypeFileUrl options : nil completionHandler : ^ ( NSURL * fileUrl , NSError * _Null _unspecified error ) {
2017-08-24 08:47:27 +00:00
// Switch back on the main thread to handle correctly the UI change
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
2017-08-31 13:51:49 +00:00
[ self sendFileWithUrl : fileUrl toRoom : room extensionItem : item failureBlock : failureBlock ] ;
2017-08-24 08:47:27 +00:00
}
} ) ;
2017-08-14 11:25:02 +00:00
} ] ;
}
else if ( [ itemProvider hasItemConformingToTypeIdentifier : UTTypeText ] )
2017-08-10 22:38:47 +00:00
{
[ itemProvider loadItemForTypeIdentifier : UTTypeText options : nil completionHandler : ^ ( NSString * text , NSError * _Null _unspecified error ) {
2017-08-24 08:47:27 +00:00
// Switch back on the main thread to handle correctly the UI change
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
2017-08-31 13:51:49 +00:00
[ self sendText : text toRoom : room extensionItem : item failureBlock : failureBlock ] ;
2017-08-24 08:47:27 +00:00
}
} ) ;
2017-08-10 22:38:47 +00:00
} ] ;
}
else if ( [ itemProvider hasItemConformingToTypeIdentifier : UTTypeURL ] )
{
[ itemProvider loadItemForTypeIdentifier : UTTypeURL options : nil completionHandler : ^ ( NSURL * url , NSError * _Null _unspecified error ) {
2017-08-24 08:47:27 +00:00
// Switch back on the main thread to handle correctly the UI change
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
2017-08-31 13:51:49 +00:00
[ self sendText : url . absoluteString toRoom : room extensionItem : item failureBlock : failureBlock ] ;
2017-08-24 08:47:27 +00:00
}
} ) ;
2017-08-10 22:38:47 +00:00
} ] ;
}
else if ( [ itemProvider hasItemConformingToTypeIdentifier : UTTypeImage ] )
{
2017-08-22 15:52:32 +00:00
itemProvider . isLoaded = NO ;
2017-08-10 22:38:47 +00:00
[ itemProvider loadItemForTypeIdentifier : UTTypeImage options : nil completionHandler : ^ ( NSData * imageData , NSError * _Null _unspecified error )
{
2017-08-17 15:28:08 +00:00
if ( weakSelf )
{
2018-01-22 18:36:51 +00:00
typeof ( self ) self = weakSelf ;
2017-08-22 15:52:32 +00:00
itemProvider . isLoaded = YES ;
2018-03-08 15:48:33 +00:00
if ( imageData )
{
[ self . pendingImages addObject : imageData ] ;
}
else
{
NSLog ( @ "[ShareExtensionManager] sendContentToRoom: failed to loadItemForTypeIdentifier. Error: %@" , error ) ;
}
2017-08-22 15:52:32 +00:00
if ( [ self areAttachmentsFullyLoaded ] )
{
2017-11-09 14:59:42 +00:00
UIAlertController * compressionPrompt = [ self compressionPromptForImage : self . pendingImages . firstObject shareBlock : ^ {
2017-08-31 13:51:49 +00:00
[ self sendImages : self . pendingImages withProviders : item . attachments toRoom : room extensionItem : item failureBlock : failureBlock ] ;
2017-08-22 15:52:32 +00:00
} ] ;
2018-02-05 14:36:56 +00:00
if ( compressionPrompt )
{
[ self . delegate shareExtensionManager : self showImageCompressionPrompt : compressionPrompt ] ;
}
2017-08-22 15:52:32 +00:00
}
2017-08-17 15:28:08 +00:00
}
2017-08-10 22:38:47 +00:00
} ] ;
}
else if ( [ itemProvider hasItemConformingToTypeIdentifier : UTTypeVideo ] )
{
2017-08-24 08:47:27 +00:00
[ itemProvider loadItemForTypeIdentifier : UTTypeVideo options : nil completionHandler : ^ ( NSURL * videoLocalUrl , NSError * _Null _unspecified error ) {
// Switch back on the main thread to handle correctly the UI change
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
2017-08-31 13:51:49 +00:00
[ self sendVideo : videoLocalUrl toRoom : room extensionItem : item failureBlock : failureBlock ] ;
2017-08-24 08:47:27 +00:00
}
} ) ;
2017-08-14 11:25:02 +00:00
} ] ;
}
else if ( [ itemProvider hasItemConformingToTypeIdentifier : UTTypeMovie ] )
{
2017-08-24 08:47:27 +00:00
[ itemProvider loadItemForTypeIdentifier : UTTypeMovie options : nil completionHandler : ^ ( NSURL * videoLocalUrl , NSError * _Null _unspecified error ) {
// Switch back on the main thread to handle correctly the UI change
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
2017-08-31 13:51:49 +00:00
[ self sendVideo : videoLocalUrl toRoom : room extensionItem : item failureBlock : failureBlock ] ;
2017-08-24 08:47:27 +00:00
}
} ) ;
2017-08-10 22:38:47 +00:00
} ] ;
}
}
}
}
2017-08-16 20:01:54 +00:00
- ( BOOL ) hasImageTypeContent
{
for ( NSExtensionItem * item in self . shareExtensionContext . inputItems )
{
for ( NSItemProvider * itemProvider in item . attachments )
{
if ( [ itemProvider hasItemConformingToTypeIdentifier : ( __bridge NSString * ) kUTTypeImage ] )
{
return YES ;
}
}
}
return NO ;
}
2017-08-17 22:27:56 +00:00
- ( void ) terminateExtensionCanceled : ( BOOL ) canceled
2017-08-10 22:38:47 +00:00
{
2017-08-17 22:27:56 +00:00
if ( canceled )
{
[ self . shareExtensionContext cancelRequestWithError : [ NSError errorWithDomain : @ "MXUserCancelErrorDomain" code : 4201 userInfo : nil ] ] ;
}
else
{
[ self . shareExtensionContext cancelRequestWithError : [ NSError errorWithDomain : @ "MXFailureErrorDomain" code : 500 userInfo : nil ] ] ;
}
2017-08-25 09:55:37 +00:00
[ self . primaryViewController destroy ] ;
self . primaryViewController = nil ;
2017-08-10 22:38:47 +00:00
}
2017-11-09 14:59:42 +00:00
# pragma mark - Private
- ( void ) resetPendingData
2017-09-15 15:17:03 +00:00
{
2017-11-09 14:59:42 +00:00
[ self . pendingImages removeAllObjects ] ;
[ self . imageUploadProgresses removeAllObjects ] ;
2017-09-15 15:17:03 +00:00
}
2017-08-25 09:55:37 +00:00
- ( void ) completeRequestReturningItems : ( nullable NSArray * ) items completionHandler : ( void ( ^ __nullable ) ( BOOL expired ) ) completionHandler ;
{
[ self . shareExtensionContext completeRequestReturningItems : items completionHandler : completionHandler ] ;
[ self . primaryViewController destroy ] ;
self . primaryViewController = nil ;
}
2017-11-09 14:59:42 +00:00
- ( UIAlertController * ) compressionPromptForImage : ( NSData * ) imageData shareBlock : ( void ( ^ ) ( ) ) shareBlock
2017-08-16 20:01:54 +00:00
{
UIAlertController * compressionPrompt ;
2017-11-09 14:59:42 +00:00
UIImage * image = [ UIImage imageWithData : imageData ] ;
2017-08-16 20:01:54 +00:00
2017-08-24 10:59:46 +00:00
// Get available sizes for this image
2017-11-09 14:59:42 +00:00
MXKImageCompressionSizes compressionSizes = [ MXKTools availableCompressionSizesForImage : image originalFileSize : imageData . length ] ;
2017-08-16 20:01:54 +00:00
// Apply the compression mode
if ( compressionSizes . small . fileSize || compressionSizes . medium . fileSize || compressionSizes . large . fileSize )
{
__weak typeof ( self ) weakSelf = self ;
compressionPrompt = [ UIAlertController alertControllerWithTitle : [ NSBundle mxk_localizedStringForKey : @ "attachment_size_prompt" ] message : nil preferredStyle : UIAlertControllerStyleActionSheet ] ;
if ( compressionSizes . small . fileSize )
{
NSString * resolution = [ NSString stringWithFormat : @ "%@ (%d x %d)" , [ MXTools fileSizeToString : compressionSizes . small . fileSize round : NO ] , ( int ) compressionSizes . small . imageSize . width , ( int ) compressionSizes . small . imageSize . height ] ;
NSString * title = [ NSString stringWithFormat : [ NSBundle mxk_localizedStringForKey : @ "attachment_small" ] , resolution ] ;
[ compressionPrompt addAction : [ UIAlertAction actionWithTitle : title
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
// Send the small image
self . imageCompressionMode = ImageCompressionModeSmall ;
if ( shareBlock )
{
shareBlock ( ) ;
}
[ compressionPrompt dismissViewControllerAnimated : YES completion : nil ] ;
}
} ] ] ;
}
if ( compressionSizes . medium . fileSize )
{
NSString * resolution = [ NSString stringWithFormat : @ "%@ (%d x %d)" , [ MXTools fileSizeToString : compressionSizes . medium . fileSize round : NO ] , ( int ) compressionSizes . medium . imageSize . width , ( int ) compressionSizes . medium . imageSize . height ] ;
NSString * title = [ NSString stringWithFormat : [ NSBundle mxk_localizedStringForKey : @ "attachment_medium" ] , resolution ] ;
[ compressionPrompt addAction : [ UIAlertAction actionWithTitle : title
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
// Send the medium image
self . imageCompressionMode = ImageCompressionModeMedium ;
if ( shareBlock )
{
shareBlock ( ) ;
}
[ compressionPrompt dismissViewControllerAnimated : YES completion : nil ] ;
}
} ] ] ;
}
if ( compressionSizes . large . fileSize )
{
NSString * resolution = [ NSString stringWithFormat : @ "%@ (%d x %d)" , [ MXTools fileSizeToString : compressionSizes . large . fileSize round : NO ] , ( int ) compressionSizes . large . imageSize . width , ( int ) compressionSizes . large . imageSize . height ] ;
NSString * title = [ NSString stringWithFormat : [ NSBundle mxk_localizedStringForKey : @ "attachment_large" ] , resolution ] ;
[ compressionPrompt addAction : [ UIAlertAction actionWithTitle : title
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
// Send the large image
self . imageCompressionMode = ImageCompressionModeLarge ;
self . actualLargeSize = compressionSizes . actualLargeSize ;
if ( shareBlock )
{
shareBlock ( ) ;
}
[ compressionPrompt dismissViewControllerAnimated : YES completion : nil ] ;
}
} ] ] ;
}
2017-08-24 10:59:46 +00:00
// To limit memory consumption , we suggest the original resolution only if the image orientation is up , or if the image size is moderate
if ( image . imageOrientation = = UIImageOrientationUp || ! compressionSizes . large . fileSize )
{
NSString * resolution = [ NSString stringWithFormat : @ "%@ (%d x %d)" , [ MXTools fileSizeToString : compressionSizes . original . fileSize round : NO ] , ( int ) compressionSizes . original . imageSize . width , ( int ) compressionSizes . original . imageSize . height ] ;
NSString * title = [ NSString stringWithFormat : [ NSBundle mxk_localizedStringForKey : @ "attachment_original" ] , resolution ] ;
[ compressionPrompt addAction : [ UIAlertAction actionWithTitle : title
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
2017-08-16 20:01:54 +00:00
2017-08-24 10:59:46 +00:00
if ( weakSelf )
2017-08-16 20:01:54 +00:00
{
2017-08-24 10:59:46 +00:00
typeof ( self ) self = weakSelf ;
self . imageCompressionMode = ImageCompressionModeNone ;
if ( shareBlock )
{
shareBlock ( ) ;
}
[ compressionPrompt dismissViewControllerAnimated : YES completion : nil ] ;
2017-08-16 20:01:54 +00:00
}
2017-08-24 10:59:46 +00:00
} ] ] ;
}
2017-08-16 20:01:54 +00:00
[ compressionPrompt addAction : [ UIAlertAction actionWithTitle : [ NSBundle mxk_localizedStringForKey : @ "cancel" ]
2018-01-19 02:43:28 +00:00
style : UIAlertActionStyleCancel
2017-08-16 20:01:54 +00:00
handler : ^ ( UIAlertAction * action ) {
if ( weakSelf )
{
[ compressionPrompt dismissViewControllerAnimated : YES completion : nil ] ;
}
} ] ] ;
}
2017-08-24 10:59:46 +00:00
else
{
self . imageCompressionMode = ImageCompressionModeNone ;
if ( shareBlock )
{
shareBlock ( ) ;
}
}
2017-08-16 20:01:54 +00:00
return compressionPrompt ;
}
2017-08-31 13:51:49 +00:00
- ( void ) didStartSendingToRoom : ( MXRoom * ) room
2017-08-20 21:39:08 +00:00
{
if ( [ self . delegate respondsToSelector : @ selector ( shareExtensionManager : didStartSendingContentToRoom : ) ] )
{
2017-08-31 13:51:49 +00:00
[ self . delegate shareExtensionManager : self didStartSendingContentToRoom : room ] ;
2017-08-20 21:39:08 +00:00
}
}
2017-08-22 15:52:32 +00:00
- ( BOOL ) areAttachmentsFullyLoaded
{
for ( NSExtensionItem * item in self . shareExtensionContext . inputItems )
{
for ( NSItemProvider * itemProvider in item . attachments )
{
if ( itemProvider . isLoaded = = NO )
{
return NO ;
}
}
}
return YES ;
}
2017-08-16 20:01:54 +00:00
# pragma mark - Notifications
- ( void ) onMediaUploadProgress : ( NSNotification * ) notification
{
2017-08-22 15:52:32 +00:00
self . imageUploadProgresses [ notification . object ] = ( NSNumber * ) notification . userInfo [ kMXMediaLoaderProgressValueKey ] ;
2017-08-16 20:01:54 +00:00
if ( [ self . delegate respondsToSelector : @ selector ( shareExtensionManager : mediaUploadProgress : ) ] )
{
2017-08-22 15:52:32 +00:00
const NSInteger totalImagesCount = self . pendingImages . count ;
CGFloat totalProgress = 0.0 ;
for ( NSNumber * progress in self . imageUploadProgresses . allValues )
{
totalProgress + = progress . floatValue / totalImagesCount ;
}
[ self . delegate shareExtensionManager : self mediaUploadProgress : totalProgress ] ;
2017-08-16 20:01:54 +00:00
}
}
2017-08-14 11:25:02 +00:00
# pragma mark - Sharing
2017-09-29 08:37:54 +00:00
- ( void ) sendText : ( NSString * ) text toRoom : ( MXRoom * ) room extensionItem : ( NSExtensionItem * ) extensionItem failureBlock : ( void ( ^ ) ( NSError * error ) ) failureBlock
2017-08-14 11:25:02 +00:00
{
2017-08-31 13:51:49 +00:00
[ self didStartSendingToRoom : room ] ;
2017-08-14 11:25:02 +00:00
if ( ! text )
{
NSLog ( @ "[ShareExtensionManager] loadItemForTypeIdentifier: failed." ) ;
if ( failureBlock )
{
2017-09-29 08:37:54 +00:00
failureBlock ( nil ) ;
2017-08-14 11:25:02 +00:00
}
return ;
}
2017-08-17 15:28:08 +00:00
__weak typeof ( self ) weakSelf = self ;
2017-08-31 13:51:49 +00:00
[ room sendTextMessage : text success : ^ ( NSString * eventId ) {
2017-08-17 15:28:08 +00:00
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
2017-08-25 09:55:37 +00:00
[ self completeRequestReturningItems : @ [ extensionItem ] completionHandler : nil ] ;
2017-08-17 15:28:08 +00:00
}
2017-08-14 11:25:02 +00:00
} failure : ^ ( NSError * error ) {
NSLog ( @ "[ShareExtensionManager] sendTextMessage failed." ) ;
if ( failureBlock )
{
2017-09-29 08:37:54 +00:00
failureBlock ( error ) ;
2017-08-14 11:25:02 +00:00
}
} ] ;
}
2017-09-29 08:37:54 +00:00
- ( void ) sendFileWithUrl : ( NSURL * ) fileUrl toRoom : ( MXRoom * ) room extensionItem : ( NSExtensionItem * ) extensionItem failureBlock : ( void ( ^ ) ( NSError * error ) ) failureBlock
2017-08-14 11:25:02 +00:00
{
2017-08-31 13:51:49 +00:00
[ self didStartSendingToRoom : room ] ;
2017-08-14 11:25:02 +00:00
if ( ! fileUrl )
{
NSLog ( @ "[ShareExtensionManager] loadItemForTypeIdentifier: failed." ) ;
if ( failureBlock )
{
2017-09-29 08:37:54 +00:00
failureBlock ( nil ) ;
2017-08-14 11:25:02 +00:00
}
return ;
}
2017-08-21 22:31:43 +00:00
NSString * mimeType ;
CFStringRef uti = UTTypeCreatePreferredIdentifierForTag ( kUTTagClassFilenameExtension , ( __bridge CFStringRef ) [ fileUrl pathExtension ] , NULL ) ;
mimeType = ( __bridge _transfer NSString * ) UTTypeCopyPreferredTagWithClass ( uti , kUTTagClassMIMEType ) ;
CFRelease ( uti ) ;
2017-08-17 15:28:08 +00:00
__weak typeof ( self ) weakSelf = self ;
2017-08-31 13:51:49 +00:00
[ room sendFile : fileUrl mimeType : mimeType localEcho : nil success : ^ ( NSString * eventId ) {
2017-08-17 15:28:08 +00:00
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
2017-08-25 09:55:37 +00:00
[ self completeRequestReturningItems : @ [ extensionItem ] completionHandler : nil ] ;
2017-08-17 15:28:08 +00:00
}
2017-08-14 11:25:02 +00:00
} failure : ^ ( NSError * error ) {
NSLog ( @ "[ShareExtensionManager] sendFile failed." ) ;
if ( failureBlock )
{
2017-09-29 08:37:54 +00:00
failureBlock ( error ) ;
2017-08-14 11:25:02 +00:00
}
2017-08-31 13:51:49 +00:00
} keepActualFilename : YES ] ;
2017-08-14 11:25:02 +00:00
}
2017-08-24 22:42:35 +00:00
2017-09-29 08:37:54 +00:00
- ( void ) sendImages : ( NSMutableArray * ) imageDatas withProviders : ( NSArray * ) itemProviders toRoom : ( MXRoom * ) room extensionItem : ( NSExtensionItem * ) extensionItem failureBlock : ( void ( ^ ) ( NSError * error ) ) failureBlock
2017-08-14 11:25:02 +00:00
{
2017-08-20 21:39:08 +00:00
[ self didStartSendingToRoom : room ] ;
2017-08-22 15:52:32 +00:00
2017-11-09 14:59:42 +00:00
__block NSUInteger count = imageDatas . count ;
2017-08-24 22:26:53 +00:00
for ( NSInteger index = 0 ; index < imageDatas . count ; index + + )
2017-08-14 11:25:02 +00:00
{
2017-08-24 22:26:53 +00:00
NSItemProvider * itemProvider = itemProviders [ index ] ;
2017-08-24 22:42:35 +00:00
NSData * imageData = imageDatas [ index ] ;
UIImage * image = [ UIImage imageWithData : imageData ] ;
2017-08-24 22:26:53 +00:00
2017-11-09 14:59:42 +00:00
if ( ! image )
2017-08-14 11:25:02 +00:00
{
2017-08-22 15:52:32 +00:00
NSLog ( @ "[ShareExtensionManager] loadItemForTypeIdentifier: failed." ) ;
if ( failureBlock )
{
2017-09-29 08:37:54 +00:00
failureBlock ( nil ) ;
2017-08-22 15:52:32 +00:00
failureBlock = nil ;
}
return ;
2017-08-14 11:25:02 +00:00
}
2017-08-21 13:28:58 +00:00
2017-08-22 15:52:32 +00:00
// Prepare the image
2017-11-09 14:59:42 +00:00
UIImage * convertedImage = image ;
2017-08-21 13:28:58 +00:00
2017-08-22 15:52:32 +00:00
if ( self . imageCompressionMode = = ImageCompressionModeSmall )
{
2017-11-09 14:59:42 +00:00
convertedImage = [ MXKTools reduceImage : image toFitInSize : CGSizeMake ( MXKTOOLS_SMALL _IMAGE _SIZE , MXKTOOLS_SMALL _IMAGE _SIZE ) ] ;
2017-08-22 15:52:32 +00:00
}
else if ( self . imageCompressionMode = = ImageCompressionModeMedium )
{
2017-11-09 14:59:42 +00:00
convertedImage = [ MXKTools reduceImage : image toFitInSize : CGSizeMake ( MXKTOOLS_MEDIUM _IMAGE _SIZE , MXKTOOLS_MEDIUM _IMAGE _SIZE ) ] ;
2017-08-22 15:52:32 +00:00
}
else if ( self . imageCompressionMode = = ImageCompressionModeLarge )
{
2017-11-09 14:59:42 +00:00
convertedImage = [ MXKTools reduceImage : image toFitInSize : CGSizeMake ( self . actualLargeSize , self . actualLargeSize ) ] ;
2017-08-22 15:52:32 +00:00
}
2017-08-24 22:42:35 +00:00
// Make sure the uploaded image orientation is up
2017-11-09 14:59:42 +00:00
convertedImage = [ MXKTools forceImageOrientationUp : convertedImage ] ;
2017-08-22 15:52:32 +00:00
NSString * mimeType ;
if ( [ itemProvider hasItemConformingToTypeIdentifier : ( __bridge NSString * ) kUTTypePNG ] )
2017-08-21 13:28:58 +00:00
{
2017-08-22 15:52:32 +00:00
mimeType = @ "image/png" ;
2017-11-09 14:59:42 +00:00
if ( convertedImage ! = image )
{
imageData = UIImagePNGRepresentation ( convertedImage ) ;
}
2017-08-21 13:28:58 +00:00
}
2017-11-09 14:59:42 +00:00
else if ( [ itemProvider hasItemConformingToTypeIdentifier : ( __bridge NSString * ) kUTTypeJPEG ] )
2017-08-17 15:28:08 +00:00
{
2017-08-22 15:52:32 +00:00
mimeType = @ "image/jpeg" ;
2017-11-09 14:59:42 +00:00
if ( convertedImage ! = image )
{
imageData = UIImageJPEGRepresentation ( convertedImage , 0.9 ) ;
}
2017-08-17 15:28:08 +00:00
}
2018-02-05 14:36:56 +00:00
else
{
// Other image types like GIF
NSString * imageFileName = itemProvider . registeredTypeIdentifiers [ 0 ] ;
mimeType = ( __bridge _transfer NSString * ) UTTypeCopyPreferredTagWithClass ( ( __bridge CFStringRef ) imageFileName , kUTTagClassMIMEType ) ;
}
// Sanity check
if ( ! mimeType )
{
NSLog ( @ "[ShareExtensionManager] sendImage failed. Cannot determine MIME type of %@" , itemProvider ) ;
if ( failureBlock )
{
failureBlock ( nil ) ;
}
return ;
}
2017-08-22 15:52:32 +00:00
UIImage * thumbnail = nil ;
// Thumbnail is useful only in case of encrypted room
if ( room . state . isEncrypted )
2017-08-14 11:25:02 +00:00
{
2017-11-09 14:59:42 +00:00
thumbnail = [ MXKTools reduceImage : convertedImage toFitInSize : CGSizeMake ( 800 , 600 ) ] ;
if ( thumbnail = = convertedImage )
2017-08-22 15:52:32 +00:00
{
thumbnail = nil ;
}
2017-08-14 11:25:02 +00:00
}
2017-08-22 15:52:32 +00:00
__weak typeof ( self ) weakSelf = self ;
2017-11-09 14:59:42 +00:00
[ room sendImage : imageData withImageSize : convertedImage . size mimeType : mimeType andThumbnail : thumbnail localEcho : nil success : ^ ( NSString * eventId ) {
if ( ! - - count && weakSelf )
2017-08-22 15:52:32 +00:00
{
typeof ( self ) self = weakSelf ;
2017-11-09 14:59:42 +00:00
[ self resetPendingData ] ;
[ self . shareExtensionContext completeRequestReturningItems : @ [ extensionItem ] completionHandler : nil ] ;
2017-08-22 15:52:32 +00:00
}
2017-11-09 14:59:42 +00:00
2017-08-22 15:52:32 +00:00
} failure : ^ ( NSError * error ) {
2017-11-09 14:59:42 +00:00
NSLog ( @ "[ShareExtensionManager] sendImage failed." ) ;
if ( failureBlock )
2017-08-22 15:52:32 +00:00
{
2017-11-09 14:59:42 +00:00
failureBlock ( error ) ;
2017-08-22 15:52:32 +00:00
}
} ] ;
}
2017-08-14 11:25:02 +00:00
}
2017-09-29 08:37:54 +00:00
- ( void ) sendVideo : ( NSURL * ) videoLocalUrl toRoom : ( MXRoom * ) room extensionItem : ( NSExtensionItem * ) extensionItem failureBlock : ( void ( ^ ) ( NSError * error ) ) failureBlock
2017-08-14 11:25:02 +00:00
{
2017-08-20 21:39:08 +00:00
[ self didStartSendingToRoom : room ] ;
2017-08-14 11:25:02 +00:00
if ( ! videoLocalUrl )
{
NSLog ( @ "[ShareExtensionManager] loadItemForTypeIdentifier: failed." ) ;
if ( failureBlock )
{
2017-09-29 08:37:54 +00:00
failureBlock ( nil ) ;
2017-08-14 11:25:02 +00:00
}
return ;
}
// Retrieve the video frame at 1 sec to define the video thumbnail
AVURLAsset * urlAsset = [ [ AVURLAsset alloc ] initWithURL : videoLocalUrl options : nil ] ;
AVAssetImageGenerator * assetImageGenerator = [ AVAssetImageGenerator assetImageGeneratorWithAsset : urlAsset ] ;
assetImageGenerator . appliesPreferredTrackTransform = YES ;
CMTime time = CMTimeMake ( 1 , 1 ) ;
CGImageRef imageRef = [ assetImageGenerator copyCGImageAtTime : time actualTime : NULL error : nil ] ;
// Finalize video attachment
UIImage * videoThumbnail = [ [ UIImage alloc ] initWithCGImage : imageRef ] ;
CFRelease ( imageRef ) ;
2017-08-17 15:28:08 +00:00
__weak typeof ( self ) weakSelf = self ;
2017-08-14 11:25:02 +00:00
[ room sendVideo : videoLocalUrl withThumbnail : videoThumbnail localEcho : nil success : ^ ( NSString * eventId ) {
2017-08-17 15:28:08 +00:00
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
2017-08-25 09:55:37 +00:00
[ self completeRequestReturningItems : @ [ extensionItem ] completionHandler : nil ] ;
2017-08-17 15:28:08 +00:00
}
2017-08-14 11:25:02 +00:00
} failure : ^ ( NSError * error ) {
NSLog ( @ "[ShareExtensionManager] sendVideo failed." ) ;
if ( failureBlock )
{
2017-09-29 08:37:54 +00:00
failureBlock ( error ) ;
2017-08-14 11:25:02 +00:00
}
} ] ;
}
2017-08-10 22:38:47 +00:00
@ end
2017-08-22 15:52:32 +00:00
@ implementation NSItemProvider ( ShareExtensionManager )
- ( void ) setIsLoaded : ( BOOL ) isLoaded
{
NSNumber * number = [ NSNumber numberWithBool : isLoaded ] ;
objc_setAssociatedObject ( self , @ selector ( isLoaded ) , number , OBJC_ASSOCIATION _RETAIN _NONATOMIC ) ;
}
- ( BOOL ) isLoaded
{
NSNumber * number = objc_getAssociatedObject ( self , @ selector ( isLoaded ) ) ;
return number . boolValue ;
}
@ end