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 MobileCoreServices ;
2021-10-14 09:57:32 +00:00
2017-08-22 15:52:32 +00:00
# import "objc/runtime.h"
2019-04-02 12:42:43 +00:00
# import < mach / mach . h >
2017-08-10 22:38:47 +00:00
2021-10-14 09:57:32 +00:00
# import < MatrixKit / MatrixKit . h >
# import "ShareManager.h"
# import "ShareViewController.h"
# import "ShareDataSource.h"
2021-10-14 09:05:28 +00:00
# ifdef IS_SHARE _EXTENSION
# import "RiotShareExtension-Swift.h"
# else
# import "Riot-Swift.h"
# endif
2017-08-21 20:33:06 +00:00
2019-03-21 11:50:02 +00:00
static const CGFloat kLargeImageSizeMaxDimension = 2048.0 ;
2017-08-16 20:01:54 +00:00
typedef NS_ENUM ( NSInteger , ImageCompressionMode )
{
ImageCompressionModeNone ,
ImageCompressionModeSmall ,
ImageCompressionModeMedium ,
ImageCompressionModeLarge
} ;
2021-10-14 09:57:32 +00:00
@ interface ShareManager ( ) < ShareViewControllerDelegate >
2021-10-14 09:05:28 +00:00
@ property ( nonatomic , strong , readonly ) NSArray < NSExtensionItem * > * extensionItems ;
2021-10-14 11:35:36 +00:00
@ property ( nonatomic , strong , readonly ) ShareViewController * shareViewController ;
2017-09-08 15:02:45 +00:00
2021-10-14 09:05:28 +00:00
@ property ( nonatomic , strong , readonly ) NSMutableArray < NSData * > * pendingImages ;
@ property ( nonatomic , strong , readonly ) NSMutableDictionary < NSString * , NSNumber * > * imageUploadProgresses ;
@ property ( nonatomic , strong , readonly ) id < Configurable > configuration ;
2017-08-16 20:01:54 +00:00
2021-10-14 09:05:28 +00:00
@ property ( nonatomic , strong ) MXKAccount * userAccount ;
@ property ( nonatomic , strong ) MXFileStore * fileStore ;
2017-08-26 11:54:25 +00:00
2021-10-14 09:05:28 +00:00
@ property ( nonatomic , assign ) ImageCompressionMode imageCompressionMode ;
@ property ( nonatomic , assign ) CGFloat actualLargeSize ;
2017-08-16 20:01:54 +00:00
@ end
2017-08-10 22:38:47 +00:00
2021-10-14 09:57:32 +00:00
@ implementation ShareManager
2017-08-10 22:38:47 +00:00
2021-10-14 11:35:36 +00:00
- ( instancetype ) initWithItems : ( NSArray < NSExtensionItem * > * ) items
2017-08-10 22:38:47 +00:00
{
2021-10-14 09:05:28 +00:00
if ( self = [ super init ] ) {
2017-09-29 08:37:54 +00:00
2021-10-14 11:35:36 +00:00
_extensionItems = items ;
2017-08-21 20:33:06 +00:00
2021-10-14 09:05:28 +00:00
_pendingImages = [ NSMutableArray array ] ;
_imageUploadProgresses = [ NSMutableDictionary dictionary ] ;
2017-08-22 15:56:30 +00:00
2021-10-14 09:05:28 +00:00
[ [ NSNotificationCenter defaultCenter ] addObserver : self selector : @ selector ( onMediaLoaderStateDidChange : ) name : kMXMediaLoaderStateDidChangeNotification object : nil ] ;
[ [ NSNotificationCenter defaultCenter ] addObserver : self selector : @ selector ( checkUserAccount ) name : kMXKAccountManagerDidRemoveAccountNotification object : nil ] ;
[ [ NSNotificationCenter defaultCenter ] addObserver : self selector : @ selector ( checkUserAccount ) name : NSExtensionHostWillEnterForegroundNotification object : nil ] ;
[ NSNotificationCenter . defaultCenter addObserver : self selector : @ selector ( didReceiveMemoryWarning : ) name : UIApplicationDidReceiveMemoryWarningNotification object : nil ] ;
2017-08-21 20:33:06 +00:00
2021-10-14 11:35:36 +00:00
_configuration = [ [ CommonConfiguration alloc ] init ] ;
2021-10-14 09:05:28 +00:00
[ _configuration setupSettings ] ;
2021-06-03 08:30:07 +00:00
2017-10-20 12:57:51 +00:00
// NSLog -> console . log file when not debugging the app
2021-06-03 08:30:07 +00:00
MXLogConfiguration * configuration = [ [ MXLogConfiguration alloc ] init ] ;
configuration . logLevel = MXLogLevelVerbose ;
configuration . logFilesSizeLimit = 0 ;
configuration . maxLogFilesCount = 10 ;
configuration . subLogName = @ "share" ;
// Redirect NSLogs to files only if we are not debugging
if ( ! isatty ( STDERR_FILENO ) ) {
configuration . redirectLogsToFiles = YES ;
2017-10-20 12:57:51 +00:00
}
2021-06-03 08:30:07 +00:00
[ MXLog configure : configuration ] ;
2017-09-08 15:02:45 +00:00
2021-10-14 09:05:28 +00:00
_shareViewController = [ [ ShareViewController alloc ] initWithType : ShareViewControllerTypeSend
currentState : ShareViewControllerAccountStateNotConfigured ] ;
[ _shareViewController setDelegate : self ] ;
// 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" ] ;
// Check the current matrix user .
[ self checkUserAccount ] ;
2017-09-08 15:02:45 +00:00
}
2017-09-29 08:37:54 +00:00
2021-10-14 09:05:28 +00:00
return self ;
2017-09-08 15:02:45 +00:00
}
2017-08-14 11:25:02 +00:00
# pragma mark - Public
2021-10-14 09:05:28 +00:00
- ( UIViewController * ) mainViewController
2017-08-21 20:33:06 +00:00
{
2021-10-14 09:05:28 +00:00
return self . shareViewController ;
}
# pragma mark - ShareViewControllerDelegate
- ( void ) shareViewControllerDidRequestShare : ( ShareViewController * ) shareViewController
forRoomIdentifier : ( NSString * ) roomIdentifier
{
MXSession * session = [ [ MXSession alloc ] initWithMatrixRestClient : [ [ MXRestClient alloc ] initWithCredentials : self . userAccount . mxCredentials andOnUnrecognizedCertificateBlock : nil ] ] ;
[ MXFileStore setPreloadOptions : 0 ] ;
2017-12-27 13:00:16 +00:00
2021-10-14 09:05:28 +00:00
MXWeakify ( session ) ;
[ session setStore : self . fileStore success : ^ {
MXStrongifyAndReturnIfNil ( session ) ;
2021-10-14 11:35:36 +00:00
session . crypto . warnOnUnknowDevices = NO ; // Do not warn for unknown devices . We have cross - signing now
2021-10-14 09:05:28 +00:00
2021-10-14 11:35:36 +00:00
MXRoom * selectedRoom = [ MXRoom loadRoomFromStore : self . fileStore withRoomId : roomIdentifier matrixSession : session ] ;
[ self sendContentToRoom : selectedRoom success : nil failure : ^ ( NSError * error ) {
[ self showFailureAlert : [ VectorL10n roomEventFailedToSend ] ] ;
2021-10-14 09:05:28 +00:00
} ] ;
} failure : ^ ( NSError * error ) {
2021-10-14 09:57:32 +00:00
MXLogError ( @ "[ShareManager] Failed preparign matrix session" ) ;
2021-10-14 09:05:28 +00:00
} ] ;
2017-08-21 20:33:06 +00:00
}
2021-10-14 09:05:28 +00:00
- ( void ) shareViewControllerDidRequestDismissal : ( ShareViewController * ) shareViewController
{
2021-10-14 11:35:36 +00:00
self . completionCallback ( ShareManagerResultCancelled ) ;
2021-10-14 09:05:28 +00:00
}
# pragma mark - Private
2021-10-14 11:35:36 +00:00
- ( void ) sendContentToRoom : ( MXRoom * ) room success : ( void ( ^ ) ( void ) ) success failure : ( void ( ^ ) ( NSError * ) ) failure
2017-08-10 22:38:47 +00:00
{
2019-02-08 13:43:21 +00:00
[ self resetPendingData ] ;
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
2019-02-08 10:04:51 +00:00
BOOL areAllAttachmentsImages = [ self areAllAttachmentsImages ] ;
2019-02-08 13:43:21 +00:00
NSMutableArray < NSItemProvider * > * pendingImagesItemProviders = [ NSMutableArray new ] ; // Used to keep NSItemProvider associated to pending images ( used only when all items are images ) .
2019-02-08 10:04:51 +00:00
__block NSError * firstRequestError = nil ;
2021-10-14 11:35:36 +00:00
dispatch_group _t dispatchGroup = dispatch_group _create ( ) ;
2019-02-08 10:04:51 +00:00
void ( ^ requestFailure ) ( NSError * ) = ^ ( NSError * requestError ) {
if ( requestError && ! firstRequestError )
{
firstRequestError = requestError ;
}
2021-10-14 11:35:36 +00:00
dispatch_group _leave ( dispatchGroup ) ;
2019-02-08 10:04:51 +00:00
} ;
2017-08-24 10:55:35 +00:00
2021-10-14 11:35:36 +00:00
MXWeakify ( self ) ;
2021-10-14 09:05:28 +00:00
for ( NSExtensionItem * item in self . extensionItems )
2017-08-10 22:38:47 +00:00
{
for ( NSItemProvider * itemProvider in item . attachments )
{
2017-08-14 11:25:02 +00:00
if ( [ itemProvider hasItemConformingToTypeIdentifier : UTTypeFileUrl ] )
{
2021-10-14 11:35:36 +00:00
dispatch_group _enter ( dispatchGroup ) ;
2021-10-14 09:05:28 +00:00
[ itemProvider loadItemForTypeIdentifier : UTTypeFileUrl options : nil completionHandler : ^ ( NSURL * fileUrl , NSError * error ) {
2017-08-24 08:47:27 +00:00
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
2021-10-14 11:35:36 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
[ self sendFileWithUrl : fileUrl toRoom : room successBlock : ^ {
dispatch_group _leave ( dispatchGroup ) ;
} failureBlock : requestFailure ] ;
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
{
2021-10-14 11:35:36 +00:00
dispatch_group _enter ( dispatchGroup ) ;
[ itemProvider loadItemForTypeIdentifier : UTTypeText options : nil completionHandler : ^ ( NSString * text , NSError * error ) {
2017-08-24 08:47:27 +00:00
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
2021-10-14 11:35:36 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
[ self sendText : text toRoom : room successBlock : ^ {
dispatch_group _leave ( dispatchGroup ) ;
} failureBlock : requestFailure ] ;
2017-08-24 08:47:27 +00:00
} ) ;
2017-08-10 22:38:47 +00:00
} ] ;
}
else if ( [ itemProvider hasItemConformingToTypeIdentifier : UTTypeURL ] )
{
2021-10-14 11:35:36 +00:00
dispatch_group _enter ( dispatchGroup ) ;
[ itemProvider loadItemForTypeIdentifier : UTTypeURL options : nil completionHandler : ^ ( NSURL * url , NSError * error ) {
2017-08-24 08:47:27 +00:00
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
2021-10-14 11:35:36 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
[ self sendText : url . absoluteString toRoom : room successBlock : ^ {
dispatch_group _leave ( dispatchGroup ) ;
} failureBlock : requestFailure ] ;
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 ;
2019-01-04 12:45:41 +00:00
2021-10-14 11:35:36 +00:00
dispatch_group _enter ( dispatchGroup ) ;
[ itemProvider loadItemForTypeIdentifier : UTTypeImage options : nil completionHandler : ^ ( id < NSSecureCoding > itemProviderItem , NSError * error ) {
MXStrongifyAndReturnIfNil ( self ) ;
2017-08-24 08:47:27 +00:00
2021-10-14 11:35:36 +00:00
itemProvider . isLoaded = YES ;
NSData * imageData ;
if ( [ ( NSObject * ) itemProviderItem isKindOfClass : [ NSData class ] ] )
{
imageData = ( NSData * ) itemProviderItem ;
}
else if ( [ ( NSObject * ) itemProviderItem isKindOfClass : [ NSURL class ] ] )
{
NSURL * imageURL = ( NSURL * ) itemProviderItem ;
imageData = [ NSData dataWithContentsOfURL : imageURL ] ;
}
else if ( [ ( NSObject * ) itemProviderItem isKindOfClass : [ UIImage class ] ] )
{
// An application can share directly an UIImage .
// The most common case is screenshot sharing without saving to file .
// As screenshot using PNG format when they are saved to file we also use PNG format when saving UIImage to NSData .
UIImage * image = ( UIImage * ) itemProviderItem ;
imageData = UIImagePNGRepresentation ( image ) ;
}
if ( imageData )
{
if ( areAllAttachmentsImages )
{
[ self . pendingImages addObject : imageData ] ;
[ pendingImagesItemProviders addObject : itemProvider ] ;
}
else
2021-10-14 09:05:28 +00:00
{
2021-10-14 11:35:36 +00:00
CGSize imageSize = [ self imageSizeFromImageData : imageData ] ;
self . imageCompressionMode = ImageCompressionModeNone ;
self . actualLargeSize = MAX ( imageSize . width , imageSize . height ) ;
[ self sendImageData : imageData withProvider : itemProvider toRoom : room successBlock : ^ {
dispatch_group _leave ( dispatchGroup ) ;
2021-10-14 09:05:28 +00:00
} failureBlock : requestFailure ] ;
}
2021-10-14 11:35:36 +00:00
}
else
{
MXLogError ( @ "[ShareManager] sendContentToRoom: failed to loadItemForTypeIdentifier. Error: %@" , error ) ;
dispatch_group _leave ( dispatchGroup ) ;
}
2021-10-14 09:05:28 +00:00
2021-10-14 11:35:36 +00:00
// Only prompt for image resize if all items are images
// Ignore showMediaCompressionPrompt setting due to memory constraints with full size images .
if ( areAllAttachmentsImages )
{
if ( [ self areAttachmentsFullyLoaded ] )
{
UIAlertController * compressionPrompt = [ self compressionPromptForPendingImagesWithShareBlock : ^ {
[ self sendImageDatas : self . pendingImages withProviders : pendingImagesItemProviders toRoom : room successBlock : ^ {
dispatch_group _leave ( dispatchGroup ) ;
} failureBlock : requestFailure ] ;
} ] ;
if ( compressionPrompt )
{
[ self presentCompressionPrompt : compressionPrompt ] ;
}
}
else
{
dispatch_group _leave ( dispatchGroup ) ;
}
}
} ] ;
}
else if ( [ itemProvider hasItemConformingToTypeIdentifier : UTTypeVideo ] )
{
dispatch_group _enter ( dispatchGroup ) ;
[ itemProvider loadItemForTypeIdentifier : UTTypeVideo options : nil completionHandler : ^ ( NSURL * videoLocalUrl , NSError * error ) {
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
MXStrongifyAndReturnIfNil ( self ) ;
[ self sendVideo : videoLocalUrl toRoom : room successBlock : ^ {
dispatch_group _leave ( dispatchGroup ) ;
} failureBlock : requestFailure ] ;
} ) ;
2021-10-14 09:05:28 +00:00
} ] ;
2017-08-14 11:25:02 +00:00
}
else if ( [ itemProvider hasItemConformingToTypeIdentifier : UTTypeMovie ] )
{
2021-10-14 11:35:36 +00:00
dispatch_group _enter ( dispatchGroup ) ;
[ itemProvider loadItemForTypeIdentifier : UTTypeMovie options : nil completionHandler : ^ ( NSURL * videoLocalUrl , NSError * error ) {
2021-10-14 09:05:28 +00:00
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
2021-10-14 11:35:36 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
[ self sendVideo : videoLocalUrl toRoom : room successBlock : ^ {
dispatch_group _leave ( dispatchGroup ) ;
} failureBlock : requestFailure ] ;
2021-10-14 09:05:28 +00:00
} ) ;
2021-10-14 11:35:36 +00:00
} ] ;
2017-08-10 22:38:47 +00:00
}
}
}
2019-02-08 10:04:51 +00:00
2021-10-14 11:35:36 +00:00
dispatch_group _notify ( dispatchGroup , dispatch_get _main _queue ( ) , ^ {
2019-02-08 10:04:51 +00:00
[ self resetPendingData ] ;
if ( firstRequestError )
{
2021-10-14 11:35:36 +00:00
failure ( firstRequestError ) ;
2019-02-08 10:04:51 +00:00
}
else
{
2021-10-14 11:35:36 +00:00
self . completionCallback ( ShareManagerResultFinished ) ;
2019-02-08 10:04:51 +00:00
}
} ) ;
2017-08-10 22:38:47 +00:00
}
2021-10-14 11:35:36 +00:00
- ( void ) showFailureAlert : ( NSString * ) title
2017-08-16 20:01:54 +00:00
{
2021-10-14 09:05:28 +00:00
UIAlertController * alertController = [ UIAlertController alertControllerWithTitle : title message : nil preferredStyle : UIAlertControllerStyleAlert ] ;
MXWeakify ( self ) ;
UIAlertAction * okAction = [ UIAlertAction actionWithTitle : [ MatrixKitL10n ok ] style : UIAlertActionStyleDefault handler : ^ ( UIAlertAction * action ) {
MXStrongifyAndReturnIfNil ( self ) ;
if ( self . completionCallback )
2017-08-16 20:01:54 +00:00
{
2021-10-14 09:57:32 +00:00
self . completionCallback ( ShareManagerResultFailed ) ;
2017-08-16 20:01:54 +00:00
}
2021-10-14 09:05:28 +00:00
} ] ;
[ alertController addAction : okAction ] ;
[ self . mainViewController presentViewController : alertController animated : YES completion : nil ] ;
2017-08-16 20:01:54 +00:00
}
2021-10-14 09:05:28 +00:00
- ( void ) checkUserAccount
2017-08-10 22:38:47 +00:00
{
2021-10-14 09:05:28 +00:00
// Force account manager to reload account from the local storage .
[ [ MXKAccountManager sharedManager ] forceReloadAccounts ] ;
if ( self . userAccount )
2017-08-17 22:27:56 +00:00
{
2021-10-14 09:05:28 +00:00
// 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-08-17 22:27:56 +00:00
}
2021-10-14 09:05:28 +00:00
if ( ! self . userAccount )
2017-08-17 22:27:56 +00:00
{
2021-10-14 09:05:28 +00:00
// We consider the first enabled account .
// TODO : Handle multiple accounts
self . userAccount = [ MXKAccountManager sharedManager ] . activeAccounts . firstObject ;
2017-08-17 22:27:56 +00:00
}
2017-08-25 09:55:37 +00:00
2021-10-14 09:05:28 +00:00
// Reset the file store to reload the room data .
if ( _fileStore )
{
[ _fileStore close ] ;
_fileStore = nil ;
}
2019-01-04 12:57:35 +00:00
2021-10-14 09:05:28 +00:00
if ( self . userAccount )
{
_fileStore = [ [ MXFileStore alloc ] initWithCredentials : self . userAccount . mxCredentials ] ;
ShareDataSource * roomDataSource = [ [ ShareDataSource alloc ] initWithMode : DataSourceModeRooms
fileStore : _fileStore
credentials : self . userAccount . mxCredentials ] ;
ShareDataSource * peopleDataSource = [ [ ShareDataSource alloc ] initWithMode : DataSourceModePeople
fileStore : _fileStore
credentials : self . userAccount . mxCredentials ] ;
[ self . shareViewController configureWithState : ShareViewControllerAccountStateConfigured
roomDataSource : roomDataSource
peopleDataSource : peopleDataSource ] ;
} else {
[ self . shareViewController configureWithState : ShareViewControllerAccountStateNotConfigured
roomDataSource : nil
peopleDataSource : nil ] ;
}
2017-08-10 22:38:47 +00:00
}
2017-11-09 14:59:42 +00:00
- ( 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
}
2019-01-04 12:52:26 +00:00
- ( BOOL ) isAPendingImageNotOrientedUp
{
BOOL isAPendingImageNotOrientedUp = NO ;
for ( NSData * imageData in self . pendingImages )
{
2019-02-06 15:05:36 +00:00
if ( [ self isImageOrientationNotUpOrUndeterminedForImageData : imageData ] )
2019-01-04 12:52:26 +00:00
{
2019-02-06 15:05:36 +00:00
isAPendingImageNotOrientedUp = YES ;
break ;
2019-01-04 12:52:26 +00:00
}
}
return isAPendingImageNotOrientedUp ;
2017-08-25 09:55:37 +00:00
}
2019-01-04 12:52:26 +00:00
// TODO : When select multiple images :
// - Enhance prompt to display sum of all file sizes for each compression .
// - Find a way to choose compression sizes for all images .
- ( UIAlertController * ) compressionPromptForPendingImagesWithShareBlock : ( void ( ^ ) ( void ) ) shareBlock
2017-08-16 20:01:54 +00:00
{
2019-01-04 12:52:26 +00:00
if ( ! self . pendingImages . count )
{
return nil ;
}
BOOL isAPendingImageNotOrientedUp = [ self isAPendingImageNotOrientedUp ] ;
NSData * firstImageData = self . pendingImages . firstObject ;
UIImage * firstImage = [ UIImage imageWithData : firstImageData ] ;
2017-08-16 20:01:54 +00:00
2019-01-04 12:52:26 +00:00
MXKImageCompressionSizes compressionSizes = [ MXKTools availableCompressionSizesForImage : firstImage originalFileSize : firstImageData . length ] ;
2017-08-16 20:01:54 +00:00
2021-10-14 11:35:36 +00:00
if ( compressionSizes . small . fileSize = = 0 && compressionSizes . medium . fileSize = = 0 && compressionSizes . large . fileSize = = 0 )
2017-08-16 20:01:54 +00:00
{
2021-10-14 11:35:36 +00:00
if ( isAPendingImageNotOrientedUp && self . pendingImages . count > 1 )
2017-08-16 20:01:54 +00:00
{
2021-10-14 11:35:36 +00:00
self . imageCompressionMode = ImageCompressionModeSmall ;
2017-08-16 20:01:54 +00:00
}
2021-10-14 11:35:36 +00:00
else
2017-08-16 20:01:54 +00:00
{
2021-10-14 11:35:36 +00:00
self . imageCompressionMode = ImageCompressionModeNone ;
2017-08-16 20:01:54 +00:00
}
2021-10-14 11:35:36 +00:00
MXLogDebug ( @ "[ShareManager] Send %lu image(s) without compression prompt using compression mode: %ld" , ( unsigned long ) self . pendingImages . count , ( long ) self . imageCompressionMode ) ;
shareBlock ( ) ;
return nil ;
}
UIAlertController * compressionPrompt = [ UIAlertController alertControllerWithTitle : [ MatrixKitL10n attachmentSizePromptTitle ]
message : [ MatrixKitL10n attachmentSizePromptMessage ]
preferredStyle : UIAlertControllerStyleActionSheet ] ;
if ( compressionSizes . small . fileSize )
{
NSString * title = [ MatrixKitL10n attachmentSmall : [ MXTools fileSizeToString : compressionSizes . small . fileSize ] ] ;
MXWeakify ( self ) ;
[ compressionPrompt addAction : [ UIAlertAction actionWithTitle : title style : UIAlertActionStyleDefault handler : ^ ( UIAlertAction * action ) {
MXStrongifyAndReturnIfNil ( self ) ;
2017-08-16 20:01:54 +00:00
2021-10-14 11:35:36 +00:00
self . imageCompressionMode = ImageCompressionModeSmall ;
[ self logCompressionSizeChoice : compressionSizes . large ] ;
2017-08-16 20:01:54 +00:00
2021-10-14 11:35:36 +00:00
shareBlock ( ) ;
} ] ] ;
}
if ( compressionSizes . medium . fileSize )
{
NSString * title = [ MatrixKitL10n attachmentMedium : [ MXTools fileSizeToString : compressionSizes . medium . fileSize ] ] ;
2017-08-16 20:01:54 +00:00
2021-10-14 11:35:36 +00:00
MXWeakify ( self ) ;
[ compressionPrompt addAction : [ UIAlertAction actionWithTitle : title style : UIAlertActionStyleDefault handler : ^ ( UIAlertAction * action ) {
MXStrongifyAndReturnIfNil ( self ) ;
2017-08-24 10:59:46 +00:00
2021-10-14 11:35:36 +00:00
self . imageCompressionMode = ImageCompressionModeMedium ;
[ self logCompressionSizeChoice : compressionSizes . large ] ;
2017-08-24 10:59:46 +00:00
2021-10-14 11:35:36 +00:00
shareBlock ( ) ;
} ] ] ;
}
// Do not offer the possibility to resize an image with a dimension above kLargeImageSizeMaxDimension , to prevent the risk of memory limit exception .
// TODO : Remove this condition when issue https : // github . com / vector - im / riot - ios / issues / 2341 will be fixed .
if ( compressionSizes . large . fileSize && ( MAX ( compressionSizes . large . imageSize . width , compressionSizes . large . imageSize . height ) <= kLargeImageSizeMaxDimension ) )
{
NSString * title = [ MatrixKitL10n attachmentLarge : [ MXTools fileSizeToString : compressionSizes . large . fileSize ] ] ;
2017-08-16 20:01:54 +00:00
2021-10-14 11:35:36 +00:00
MXWeakify ( self ) ;
[ compressionPrompt addAction : [ UIAlertAction actionWithTitle : title style : UIAlertActionStyleDefault handler : ^ ( UIAlertAction * action ) {
MXStrongifyAndReturnIfNil ( self ) ;
self . imageCompressionMode = ImageCompressionModeLarge ;
self . actualLargeSize = compressionSizes . actualLargeSize ;
[ self logCompressionSizeChoice : compressionSizes . large ] ;
shareBlock ( ) ;
} ] ] ;
2017-08-16 20:01:54 +00:00
}
2021-10-14 11:35:36 +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 ( ! isAPendingImageNotOrientedUp || ! compressionSizes . large . fileSize )
2017-08-24 10:59:46 +00:00
{
2021-10-14 11:35:36 +00:00
NSString * fileSizeString = [ MXTools fileSizeToString : compressionSizes . original . fileSize ] ;
2019-01-04 12:52:26 +00:00
2021-10-14 11:35:36 +00:00
NSString * title = [ MatrixKitL10n attachmentOriginal : fileSizeString ] ;
2019-04-02 13:01:50 +00:00
2021-10-14 11:35:36 +00:00
MXWeakify ( self ) ;
[ compressionPrompt addAction : [ UIAlertAction actionWithTitle : title style : UIAlertActionStyleDefault handler : ^ ( UIAlertAction * action ) {
MXStrongifyAndReturnIfNil ( self ) ;
self . imageCompressionMode = ImageCompressionModeNone ;
[ self logCompressionSizeChoice : compressionSizes . large ] ;
2017-08-24 10:59:46 +00:00
shareBlock ( ) ;
2021-10-14 11:35:36 +00:00
} ] ] ;
2017-08-24 10:59:46 +00:00
}
2017-08-16 20:01:54 +00:00
2021-10-14 11:35:36 +00:00
[ compressionPrompt addAction : [ UIAlertAction actionWithTitle : [ MatrixKitL10n cancel ]
style : UIAlertActionStyleCancel
handler : nil ] ] ;
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
{
2021-10-14 09:05:28 +00:00
[ self . shareViewController showProgressIndicator ] ;
2017-08-20 21:39:08 +00:00
}
2017-08-22 15:52:32 +00:00
- ( BOOL ) areAttachmentsFullyLoaded
{
2021-10-14 09:05:28 +00:00
for ( NSExtensionItem * item in self . extensionItems )
2017-08-22 15:52:32 +00:00
{
for ( NSItemProvider * itemProvider in item . attachments )
{
if ( itemProvider . isLoaded = = NO )
{
return NO ;
}
}
}
return YES ;
}
2019-02-08 10:04:51 +00:00
- ( BOOL ) areAllAttachmentsImages
{
2021-10-14 09:05:28 +00:00
for ( NSExtensionItem * item in self . extensionItems )
2019-02-08 10:04:51 +00:00
{
for ( NSItemProvider * itemProvider in item . attachments )
{
if ( ! [ itemProvider hasItemConformingToTypeIdentifier : ( __bridge NSString * ) kUTTypeImage ] )
{
return NO ;
}
}
}
return YES ;
}
2019-02-06 15:05:36 +00:00
- ( NSString * ) utiFromImageTypeItemProvider : ( NSItemProvider * ) itemProvider
{
NSString * uti ;
NSString * utiPNG = ( __bridge NSString * ) kUTTypePNG ;
NSString * utiJPEG = ( __bridge NSString * ) kUTTypeJPEG ;
if ( [ itemProvider hasItemConformingToTypeIdentifier : utiPNG ] )
{
uti = utiPNG ;
}
else if ( [ itemProvider hasItemConformingToTypeIdentifier : utiJPEG ] )
{
uti = utiJPEG ;
}
else
{
uti = itemProvider . registeredTypeIdentifiers . firstObject ;
}
return uti ;
}
- ( NSString * ) utiFromImageData : ( NSData * ) imageData
{
CGImageSourceRef imageSource = CGImageSourceCreateWithData ( ( CFDataRef ) imageData , NULL ) ;
NSString * uti = ( NSString * ) CGImageSourceGetType ( imageSource ) ;
CFRelease ( imageSource ) ;
return uti ;
}
- ( NSString * ) mimeTypeFromUTI : ( NSString * ) uti
{
return ( __bridge _transfer NSString * ) UTTypeCopyPreferredTagWithClass ( ( __bridge CFStringRef ) uti , kUTTagClassMIMEType ) ;
}
- ( BOOL ) isResizingSupportedForImageData : ( NSData * ) imageData
{
NSString * imageUTI = [ self utiFromImageData : imageData ] ;
return [ self isResizingSupportedForUTI : imageUTI ] ;
}
- ( BOOL ) isResizingSupportedForUTI : ( NSString * ) imageUTI
{
if ( [ imageUTI isEqualToString : ( __bridge NSString * ) kUTTypePNG ] || [ imageUTI isEqualToString : ( __bridge NSString * ) kUTTypeJPEG ] )
{
return YES ;
}
return NO ;
}
- ( CGSize ) imageSizeFromImageData : ( NSData * ) imageData
{
CGFloat width = 0.0 f ;
CGFloat height = 0.0 f ;
CGImageSourceRef imageSource = CGImageSourceCreateWithData ( ( CFDataRef ) imageData , NULL ) ;
CFDictionaryRef imageProperties = CGImageSourceCopyPropertiesAtIndex ( imageSource , 0 , NULL ) ;
CFRelease ( imageSource ) ;
if ( imageProperties ! = NULL )
{
CFNumberRef widthNumber = CFDictionaryGetValue ( imageProperties , kCGImagePropertyPixelWidth ) ;
CFNumberRef heightNumber = CFDictionaryGetValue ( imageProperties , kCGImagePropertyPixelHeight ) ;
CFNumberRef orientationNumber = CFDictionaryGetValue ( imageProperties , kCGImagePropertyOrientation ) ;
if ( widthNumber ! = NULL )
{
CFNumberGetValue ( widthNumber , kCFNumberCGFloatType , & width ) ;
}
if ( heightNumber ! = NULL )
{
CFNumberGetValue ( heightNumber , kCFNumberCGFloatType , & height ) ;
}
// Check orientation and flip size if required
if ( orientationNumber ! = NULL )
{
int orientation ;
CFNumberGetValue ( orientationNumber , kCFNumberIntType , & orientation ) ;
// For orientation from kCGImagePropertyOrientationLeftMirrored to kCGImagePropertyOrientationLeft flip size
if ( orientation >= 5 )
{
CGFloat tempWidth = width ;
width = height ;
height = tempWidth ;
}
}
CFRelease ( imageProperties ) ;
}
return CGSizeMake ( width , height ) ;
}
- ( NSNumber * ) cgImageimageOrientationNumberFromImageData : ( NSData * ) imageData
{
NSNumber * orientationNumber ;
CGImageSourceRef imageSource = CGImageSourceCreateWithData ( ( CFDataRef ) imageData , NULL ) ;
CFDictionaryRef imageProperties = CGImageSourceCopyPropertiesAtIndex ( imageSource , 0 , NULL ) ;
CFRelease ( imageSource ) ;
if ( imageProperties ! = NULL )
{
CFNumberRef orientationNum = CFDictionaryGetValue ( imageProperties , kCGImagePropertyOrientation ) ;
// Check orientation and flip size if required
if ( orientationNum ! = NULL )
{
orientationNumber = ( __bridge NSNumber * ) orientationNum ;
}
CFRelease ( imageProperties ) ;
}
return orientationNumber ;
}
- ( BOOL ) isImageOrientationNotUpOrUndeterminedForImageData : ( NSData * ) imageData
{
BOOL isImageNotOrientedUp = YES ;
NSNumber * cgImageOrientationNumber = [ self cgImageimageOrientationNumberFromImageData : imageData ] ;
if ( cgImageOrientationNumber && cgImageOrientationNumber . unsignedIntegerValue = = ( NSUInteger ) kCGImagePropertyOrientationUp )
{
isImageNotOrientedUp = NO ;
}
return isImageNotOrientedUp ;
}
2019-04-02 13:01:50 +00:00
- ( void ) logCompressionSizeChoice : ( MXKImageCompressionSize ) compressionSize
{
NSString * fileSize = [ MXTools fileSizeToString : compressionSize . fileSize round : NO ] ;
NSUInteger imageWidth = compressionSize . imageSize . width ;
NSUInteger imageHeight = compressionSize . imageSize . height ;
2021-10-14 09:57:32 +00:00
MXLogDebug ( @ "[ShareManager] User choose image compression with output size %lu x %lu (output file size: %@)" , ( unsigned long ) imageWidth , ( unsigned long ) imageHeight , fileSize ) ;
MXLogDebug ( @ "[ShareManager] Number of images to send: %lu" , ( unsigned long ) self . pendingImages . count ) ;
2019-04-02 13:01:50 +00:00
}
2019-04-02 12:42:43 +00:00
// Log memory usage .
// NOTE : This result may not be reliable for all iOS versions ( see https : // forums . developer . apple . com / thread / 64665 for more information ) .
- ( void ) logMemoryUsage
{
struct task_basic _info basicInfo ;
mach_msg _type _number _t size = TASK_BASIC _INFO _COUNT ;
kern_return _t kerr = task_info ( mach_task _self ( ) ,
TASK_BASIC _INFO ,
( task_info _t ) & basicInfo ,
& size ) ;
vm_size _t memoryUsedInBytes = basicInfo . resident_size ;
CGFloat memoryUsedInMegabytes = memoryUsedInBytes / ( 1024 * 1024 ) ;
if ( kerr = = KERN_SUCCESS )
{
2021-10-14 09:57:32 +00:00
MXLogDebug ( @ "[ShareManager] Memory in use (in MB): %f" , memoryUsedInMegabytes ) ;
2019-04-02 12:42:43 +00:00
}
else
{
2021-10-14 09:57:32 +00:00
MXLogDebug ( @ "[ShareManager] Error with task_info(): %s" , mach_error _string ( kerr ) ) ;
2019-04-02 12:42:43 +00:00
}
}
2021-10-14 09:05:28 +00:00
- ( void ) presentCompressionPrompt : ( UIAlertController * ) compressionPrompt
{
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
[ compressionPrompt popoverPresentationController ] . sourceView = self . mainViewController . view ;
[ compressionPrompt popoverPresentationController ] . sourceRect = self . mainViewController . view . frame ;
[ self . mainViewController presentViewController : compressionPrompt animated : YES completion : nil ] ;
} ) ;
}
2019-04-02 12:42:43 +00:00
2017-08-16 20:01:54 +00:00
# pragma mark - Notifications
2018-11-10 13:28:08 +00:00
- ( void ) onMediaLoaderStateDidChange : ( NSNotification * ) notification
2017-08-16 20:01:54 +00:00
{
2018-11-10 13:28:08 +00:00
MXMediaLoader * loader = ( MXMediaLoader * ) notification . object ;
// Consider only upload progress
switch ( loader . state ) {
case MXMediaLoaderStateUploadInProgress :
2017-08-22 15:52:32 +00:00
{
2018-11-10 13:28:08 +00:00
self . imageUploadProgresses [ loader . uploadId ] = ( NSNumber * ) loader . statisticsDict [ kMXMediaLoaderProgressValueKey ] ;
2021-10-14 09:05:28 +00:00
const NSInteger totalImagesCount = self . pendingImages . count ;
CGFloat totalProgress = 0.0 ;
for ( NSNumber * progress in self . imageUploadProgresses . allValues )
2018-11-10 13:28:08 +00:00
{
2021-10-14 09:05:28 +00:00
totalProgress + = progress . floatValue / totalImagesCount ;
2018-11-10 13:28:08 +00:00
}
2021-10-14 09:05:28 +00:00
[ self . shareViewController setProgress : totalProgress ] ;
2018-11-10 13:28:08 +00:00
break ;
2017-08-22 15:52:32 +00:00
}
2018-11-10 13:28:08 +00:00
default :
break ;
2017-08-16 20:01:54 +00:00
}
}
2019-03-11 17:04:42 +00:00
- ( void ) didReceiveMemoryWarning : ( NSNotification * ) notification
{
2021-10-14 09:57:32 +00:00
MXLogDebug ( @ "[ShareManager] Did receive memory warning" ) ;
2019-04-02 12:42:43 +00:00
[ self logMemoryUsage ] ;
2019-03-11 17:04:42 +00:00
}
2017-08-14 11:25:02 +00:00
# pragma mark - Sharing
2019-02-08 10:04:51 +00:00
- ( void ) sendText : ( NSString * ) text toRoom : ( MXRoom * ) room successBlock : ( dispatch_block _t ) successBlock 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 )
{
2021-10-14 09:57:32 +00:00
MXLogError ( @ "[ShareManager] loadItemForTypeIdentifier: failed." ) ;
2021-10-14 11:35:36 +00:00
failureBlock ( nil ) ;
2017-08-14 11:25:02 +00:00
return ;
}
2017-08-17 15:28:08 +00:00
2017-08-31 13:51:49 +00:00
[ room sendTextMessage : text success : ^ ( NSString * eventId ) {
2021-10-14 11:35:36 +00:00
successBlock ( ) ;
2017-08-14 11:25:02 +00:00
} failure : ^ ( NSError * error ) {
2021-10-14 09:57:32 +00:00
MXLogError ( @ "[ShareManager] sendTextMessage failed with error %@" , error ) ;
2021-10-14 11:35:36 +00:00
failureBlock ( error ) ;
2017-08-14 11:25:02 +00:00
} ] ;
}
2019-02-08 10:04:51 +00:00
- ( void ) sendFileWithUrl : ( NSURL * ) fileUrl toRoom : ( MXRoom * ) room successBlock : ( dispatch_block _t ) successBlock 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 )
{
2021-10-14 09:57:32 +00:00
MXLogError ( @ "[ShareManager] loadItemForTypeIdentifier: failed." ) ;
2021-10-14 11:35:36 +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 ) ;
2019-02-06 15:05:36 +00:00
mimeType = [ self mimeTypeFromUTI : ( __bridge NSString * ) uti ] ;
2017-08-21 22:31:43 +00:00
CFRelease ( uti ) ;
2017-08-17 15:28:08 +00:00
2017-08-31 13:51:49 +00:00
[ room sendFile : fileUrl mimeType : mimeType localEcho : nil success : ^ ( NSString * eventId ) {
2021-10-14 11:35:36 +00:00
successBlock ( ) ;
2017-08-14 11:25:02 +00:00
} failure : ^ ( NSError * error ) {
2021-10-14 09:57:32 +00:00
MXLogError ( @ "[ShareManager] sendFile failed with error %@" , error ) ;
2021-10-14 11:35:36 +00:00
failureBlock ( error ) ;
2017-08-31 13:51:49 +00:00
} keepActualFilename : YES ] ;
2017-08-14 11:25:02 +00:00
}
2019-02-08 13:43:21 +00:00
- ( void ) sendImageData : ( NSData * ) imageData withProvider : ( NSItemProvider * ) itemProvider toRoom : ( MXRoom * ) room successBlock : ( dispatch_block _t ) successBlock failureBlock : ( void ( ^ ) ( NSError * error ) ) failureBlock
2017-08-14 11:25:02 +00:00
{
2019-02-08 13:43:21 +00:00
[ self didStartSendingToRoom : room ] ;
NSString * imageUTI ;
NSString * mimeType ;
// Try to get UTI plus mime type from NSItemProvider
imageUTI = [ self utiFromImageTypeItemProvider : itemProvider ] ;
if ( imageUTI )
2019-01-04 12:55:46 +00:00
{
2019-02-08 13:43:21 +00:00
mimeType = [ self mimeTypeFromUTI : imageUTI ] ;
}
if ( ! mimeType )
{
// Try to get UTI plus mime type from image data
2019-01-04 12:55:46 +00:00
2019-02-08 13:43:21 +00:00
imageUTI = [ self utiFromImageData : imageData ] ;
if ( imageUTI )
{
mimeType = [ self mimeTypeFromUTI : imageUTI ] ;
}
}
// Sanity check
if ( ! mimeType )
{
2021-10-14 09:57:32 +00:00
MXLogError ( @ "[ShareManager] sendImage failed. Cannot determine MIME type of %@" , itemProvider ) ;
2019-01-04 12:55:46 +00:00
if ( failureBlock )
{
failureBlock ( nil ) ;
}
return ;
}
2019-02-08 13:43:21 +00:00
CGSize imageSize ;
NSData * finalImageData ;
2017-11-09 14:59:42 +00:00
2019-02-08 13:43:21 +00:00
// Only resize JPEG or PNG files
if ( [ self isResizingSupportedForUTI : imageUTI ] )
2017-08-14 11:25:02 +00:00
{
2019-02-08 13:43:21 +00:00
UIImage * convertedImage ;
CGSize newImageSize ;
switch ( self . imageCompressionMode ) {
case ImageCompressionModeSmall :
newImageSize = CGSizeMake ( MXKTOOLS_SMALL _IMAGE _SIZE , MXKTOOLS_SMALL _IMAGE _SIZE ) ;
break ;
case ImageCompressionModeMedium :
newImageSize = CGSizeMake ( MXKTOOLS_MEDIUM _IMAGE _SIZE , MXKTOOLS_MEDIUM _IMAGE _SIZE ) ;
break ;
case ImageCompressionModeLarge :
newImageSize = CGSizeMake ( self . actualLargeSize , self . actualLargeSize ) ;
break ;
default :
newImageSize = CGSizeZero ;
break ;
}
if ( CGSizeEqualToSize ( newImageSize , CGSizeZero ) )
2017-08-14 11:25:02 +00:00
{
2019-02-08 13:43:21 +00:00
// No resize to make
// Make sure the uploaded image orientation is up
if ( [ self isImageOrientationNotUpOrUndeterminedForImageData : imageData ] )
2017-11-09 14:59:42 +00:00
{
2019-02-08 13:43:21 +00:00
UIImage * image = [ UIImage imageWithData : imageData ] ;
convertedImage = [ MXKTools forceImageOrientationUp : image ] ;
2017-11-09 14:59:42 +00:00
}
2019-02-08 13:43:21 +00:00
}
else
{
// Resize the image and set image in right orientation too
convertedImage = [ MXKTools resizeImageWithData : imageData toFitInSize : newImageSize ] ;
}
if ( convertedImage )
{
if ( [ imageUTI isEqualToString : ( __bridge NSString * ) kUTTypePNG ] )
2017-08-22 15:52:32 +00:00
{
2019-02-08 13:43:21 +00:00
finalImageData = UIImagePNGRepresentation ( convertedImage ) ;
2017-08-22 15:52:32 +00:00
}
2019-02-08 13:43:21 +00:00
else if ( [ imageUTI isEqualToString : ( __bridge NSString * ) kUTTypeJPEG ] )
2019-01-04 12:55:46 +00:00
{
2019-02-08 13:43:21 +00:00
finalImageData = UIImageJPEGRepresentation ( convertedImage , 0.9 ) ;
2019-01-04 12:55:46 +00:00
}
2017-08-22 15:52:32 +00:00
2019-02-08 13:43:21 +00:00
imageSize = convertedImage . size ;
}
else
{
finalImageData = imageData ;
imageSize = [ self imageSizeFromImageData : imageData ] ;
}
}
else
{
finalImageData = imageData ;
imageSize = [ self imageSizeFromImageData : imageData ] ;
}
UIImage * thumbnail = nil ;
// Thumbnail is useful only in case of encrypted room
if ( room . summary . isEncrypted )
{
thumbnail = [ MXKTools resizeImageWithData : imageData toFitInSize : CGSizeMake ( 800 , 600 ) ] ;
}
[ room sendImage : finalImageData withImageSize : imageSize mimeType : mimeType andThumbnail : thumbnail localEcho : nil success : ^ ( NSString * eventId ) {
2021-10-14 11:35:36 +00:00
successBlock ( ) ;
2019-02-08 13:43:21 +00:00
} failure : ^ ( NSError * error ) {
2021-10-14 09:57:32 +00:00
MXLogError ( @ "[ShareManager] sendImage failed with error %@" , error ) ;
2021-10-14 11:35:36 +00:00
failureBlock ( error ) ;
2019-02-08 13:43:21 +00:00
} ] ;
}
- ( void ) sendImageDatas : ( NSMutableArray * ) imageDatas withProviders : ( NSArray * ) itemProviders toRoom : ( MXRoom * ) room successBlock : ( dispatch_block _t ) successBlock failureBlock : ( void ( ^ ) ( NSError * error ) ) failureBlock
{
if ( imageDatas . count = = 0 || imageDatas . count ! = itemProviders . count )
{
2021-10-14 09:57:32 +00:00
MXLogError ( @ "[ShareManager] sendImages: no images to send." ) ;
2021-10-14 11:35:36 +00:00
failureBlock ( nil ) ;
2019-02-08 13:43:21 +00:00
return ;
}
[ self didStartSendingToRoom : room ] ;
dispatch_group _t requestsGroup = dispatch_group _create ( ) ;
__block NSError * firstRequestError ;
NSUInteger index = 0 ;
for ( NSData * imageData in imageDatas )
{
@ autoreleasepool
{
dispatch_group _enter ( requestsGroup ) ;
2019-02-06 15:05:36 +00:00
2019-02-08 13:43:21 +00:00
NSItemProvider * itemProvider = itemProviders [ index ] ;
[ self sendImageData : imageData withProvider : itemProvider toRoom : room successBlock : ^ {
dispatch_group _leave ( requestsGroup ) ;
} failureBlock : ^ ( NSError * error ) {
2019-02-06 15:05:36 +00:00
2019-02-08 13:43:21 +00:00
if ( error && ! firstRequestError )
2019-02-06 15:05:36 +00:00
{
2019-02-08 13:43:21 +00:00
firstRequestError = error ;
2019-02-06 15:05:36 +00:00
}
2019-02-08 13:43:21 +00:00
dispatch_group _leave ( requestsGroup ) ;
} ] ;
}
index + + ;
}
dispatch_group _notify ( requestsGroup , dispatch_get _main _queue ( ) , ^ {
if ( firstRequestError )
{
2021-10-14 11:35:36 +00:00
failureBlock ( firstRequestError ) ;
2019-02-08 13:43:21 +00:00
}
else
{
2021-10-14 11:35:36 +00:00
successBlock ( ) ;
2019-01-04 12:55:46 +00:00
}
2019-02-08 13:43:21 +00:00
} ) ;
2017-08-14 11:25:02 +00:00
}
2019-02-08 10:04:51 +00:00
- ( void ) sendVideo : ( NSURL * ) videoLocalUrl toRoom : ( MXRoom * ) room successBlock : ( dispatch_block _t ) successBlock failureBlock : ( void ( ^ ) ( NSError * error ) ) failureBlock
2017-08-14 11:25:02 +00:00
{
2021-07-09 09:26:02 +00:00
AVURLAsset * videoAsset = [ [ AVURLAsset alloc ] initWithURL : videoLocalUrl options : nil ] ;
2021-08-16 19:01:35 +00:00
MXWeakify ( self ) ;
2021-08-23 11:38:04 +00:00
// Ignore showMediaCompressionPrompt setting due to memory constraints when encrypting large videos .
2021-10-14 09:05:28 +00:00
UIAlertController * compressionPrompt = [ MXKTools videoConversionPromptForVideoAsset : videoAsset withCompletion : ^ ( NSString * presetName ) {
2021-08-16 19:01:35 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
// If the preset name is nil , the user cancelled .
if ( ! presetName )
2017-08-17 15:28:08 +00:00
{
2021-08-16 19:01:35 +00:00
return ;
2017-08-17 15:28:08 +00:00
}
2021-08-16 19:01:35 +00:00
// Set the chosen video conversion preset .
[ MXSDKOptions sharedInstance ] . videoConversionPresetName = presetName ;
[ self didStartSendingToRoom : room ] ;
if ( ! videoLocalUrl )
2017-08-14 11:25:02 +00:00
{
2021-10-14 09:57:32 +00:00
MXLogError ( @ "[ShareManager] loadItemForTypeIdentifier: failed." ) ;
2021-10-14 11:35:36 +00:00
failureBlock ( nil ) ;
2021-08-16 19:01:35 +00:00
return ;
2017-08-14 11:25:02 +00:00
}
2021-08-16 19:01:35 +00:00
// Retrieve the video frame at 1 sec to define the video thumbnail
AVAssetImageGenerator * assetImageGenerator = [ AVAssetImageGenerator assetImageGeneratorWithAsset : videoAsset ] ;
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 ) ;
[ room sendVideoAsset : videoAsset withThumbnail : videoThumbnail localEcho : nil success : ^ ( NSString * eventId ) {
2021-10-14 11:35:36 +00:00
successBlock ( ) ;
2021-08-16 19:01:35 +00:00
} failure : ^ ( NSError * error ) {
2021-10-14 09:57:32 +00:00
MXLogError ( @ "[ShareManager] Failed sending video with error %@" , error ) ;
2021-10-14 11:35:36 +00:00
failureBlock ( error ) ;
2021-08-16 19:01:35 +00:00
} ] ;
2017-08-14 11:25:02 +00:00
} ] ;
2021-08-16 19:01:35 +00:00
2021-10-14 09:05:28 +00:00
[ self presentCompressionPrompt : compressionPrompt ] ;
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
2021-10-14 09:57:32 +00:00
@ implementation NSItemProvider ( ShareManager )
2017-08-22 15:52:32 +00:00
- ( void ) setIsLoaded : ( BOOL ) isLoaded
{
2019-01-07 23:24:11 +00:00
NSNumber * number = @ ( isLoaded ) ;
2017-08-22 15:52:32 +00:00
objc_setAssociatedObject ( self , @ selector ( isLoaded ) , number , OBJC_ASSOCIATION _RETAIN _NONATOMIC ) ;
}
- ( BOOL ) isLoaded
{
NSNumber * number = objc_getAssociatedObject ( self , @ selector ( isLoaded ) ) ;
return number . boolValue ;
}
@ end