Merge pull request #1447 from aramsargsyan/app-extension-improvements

App extension improvements
This commit is contained in:
giomfo 2017-08-18 11:32:46 +02:00 committed by GitHub
commit ee74daa1c4
19 changed files with 1564 additions and 35 deletions

View file

@ -53,7 +53,7 @@ pod 'OLMKit'
pod 'Realm', '~> 2.8.1'
# The tagged version on which this version of Riot has been built
pod 'MatrixKit', '0.6.0'
pod 'MatrixKit', '0.6.1'
# The lastest release available on the CocoaPods repository
#pod 'MatrixKit/AppExtension'

View file

@ -8,18 +8,21 @@
/* Begin PBXBuildFile section */
05D592A32FF1D1877B89F73C /* libPods-Riot.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FD9D0BDE9232898950554DD5 /* libPods-Riot.a */; };
243517981F375B6800D0683E /* FallbackViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2466B7531F2F80B800AE27B0 /* FallbackViewController.m */; };
243517991F375B7100D0683E /* FallbackViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2466B7541F2F80B800AE27B0 /* FallbackViewController.xib */; };
2435179A1F375B8A00D0683E /* ShareViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2466B75C1F2F80B800AE27B0 /* ShareViewController.m */; };
2435179B1F375B8F00D0683E /* SharePresentingViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2466B75A1F2F80B800AE27B0 /* SharePresentingViewController.m */; };
2435179C1F375B9400D0683E /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 2466B7551F2F80B800AE27B0 /* Info.plist */; };
2435179D1F375BA100D0683E /* ShareViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2466B75D1F2F80B800AE27B0 /* ShareViewController.xib */; };
2435179E1F375BA600D0683E /* RoomsListViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2466B7581F2F80B800AE27B0 /* RoomsListViewController.m */; };
2435179F1F375C0F00D0683E /* Vector.strings in Resources */ = {isa = PBXBuildFile; fileRef = 327382C01F276AED00356143 /* Vector.strings */; };
245FC3ED1F3D079A00603C6A /* ShareExtensionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 245FC3EB1F3CAF9800603C6A /* ShareExtensionManager.m */; };
245FC3EF1F3DD30800603C6A /* RecentCellData.m in Sources */ = {isa = PBXBuildFile; fileRef = F083BC071E7009EC00A9B29C /* RecentCellData.m */; };
2466B73E1F2DFAC100AE27B0 /* animatedLogo-4.png in Resources */ = {isa = PBXBuildFile; fileRef = F083BB201E7009EC00A9B29C /* animatedLogo-4.png */; };
24B5103E1EFA7083004C6AD2 /* ReadReceiptsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 24B5103D1EFA7083004C6AD2 /* ReadReceiptsViewController.m */; };
24B510401EFA88CC004C6AD2 /* ReadReceiptsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 24B5103F1EFA88CC004C6AD2 /* ReadReceiptsViewController.xib */; };
24CBEC591F0EAD310093EABB /* RiotShareExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 24CBEC4E1F0EAD310093EABB /* RiotShareExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
24D6B3581F3C90D300FC7A71 /* ShareRecentsDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 24D6B3571F3C90D300FC7A71 /* ShareRecentsDataSource.m */; };
24D6B3591F3CA02900FC7A71 /* SharePresentingViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 24D6B34B1F3C8F8A00FC7A71 /* SharePresentingViewController.m */; };
24D6B35A1F3CA02C00FC7A71 /* ShareViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 24D6B34D1F3C8F8A00FC7A71 /* ShareViewController.m */; };
24D6B35B1F3CA03300FC7A71 /* ShareViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 24D6B34E1F3C8F8A00FC7A71 /* ShareViewController.xib */; };
24D6B35C1F3CA03600FC7A71 /* RoomsListViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 24D6B3491F3C8F8A00FC7A71 /* RoomsListViewController.m */; };
24D6B35D1F3CA03A00FC7A71 /* FallbackViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 24D6B3461F3C8F8A00FC7A71 /* FallbackViewController.m */; };
24D6B35E1F3CA03E00FC7A71 /* FallbackViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 24D6B3471F3C8F8A00FC7A71 /* FallbackViewController.xib */; };
24EEE5A01F23A08900B3C705 /* RoomTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = F083BCED1E7009EC00A9B29C /* RoomTableViewCell.m */; };
24EEE5A11F23A09A00B3C705 /* RiotDesignValues.m in Sources */ = {isa = PBXBuildFile; fileRef = F083BC171E7009EC00A9B29C /* RiotDesignValues.m */; };
24EEE5A21F23A8B400B3C705 /* MXRoom+Riot.m in Sources */ = {isa = PBXBuildFile; fileRef = F083BBE81E7009EC00A9B29C /* MXRoom+Riot.m */; };
@ -545,22 +548,26 @@
/* Begin PBXFileReference section */
12AA0005C8B3D8D8162584C5 /* Pods-RiotShareExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RiotShareExtension.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RiotShareExtension/Pods-RiotShareExtension.debug.xcconfig"; sourceTree = "<group>"; };
2466B7521F2F80B800AE27B0 /* FallbackViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FallbackViewController.h; path = RiotShareExtension/FallbackViewController.h; sourceTree = SOURCE_ROOT; };
2466B7531F2F80B800AE27B0 /* FallbackViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FallbackViewController.m; path = RiotShareExtension/FallbackViewController.m; sourceTree = SOURCE_ROOT; };
2466B7541F2F80B800AE27B0 /* FallbackViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = FallbackViewController.xib; path = RiotShareExtension/FallbackViewController.xib; sourceTree = SOURCE_ROOT; };
245FC3EA1F3CAF9800603C6A /* ShareExtensionManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShareExtensionManager.h; sourceTree = "<group>"; };
245FC3EB1F3CAF9800603C6A /* ShareExtensionManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ShareExtensionManager.m; sourceTree = "<group>"; };
2466B7551F2F80B800AE27B0 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = RiotShareExtension/Info.plist; sourceTree = SOURCE_ROOT; };
2466B7561F2F80B800AE27B0 /* RiotShareExtension.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; name = RiotShareExtension.entitlements; path = RiotShareExtension/RiotShareExtension.entitlements; sourceTree = SOURCE_ROOT; };
2466B7571F2F80B800AE27B0 /* RoomsListViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RoomsListViewController.h; path = RiotShareExtension/RoomsListViewController.h; sourceTree = SOURCE_ROOT; };
2466B7581F2F80B800AE27B0 /* RoomsListViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RoomsListViewController.m; path = RiotShareExtension/RoomsListViewController.m; sourceTree = SOURCE_ROOT; };
2466B7591F2F80B800AE27B0 /* SharePresentingViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SharePresentingViewController.h; path = RiotShareExtension/SharePresentingViewController.h; sourceTree = SOURCE_ROOT; };
2466B75A1F2F80B800AE27B0 /* SharePresentingViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SharePresentingViewController.m; path = RiotShareExtension/SharePresentingViewController.m; sourceTree = SOURCE_ROOT; };
2466B75B1F2F80B800AE27B0 /* ShareViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ShareViewController.h; path = RiotShareExtension/ShareViewController.h; sourceTree = SOURCE_ROOT; };
2466B75C1F2F80B800AE27B0 /* ShareViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ShareViewController.m; path = RiotShareExtension/ShareViewController.m; sourceTree = SOURCE_ROOT; };
2466B75D1F2F80B800AE27B0 /* ShareViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = ShareViewController.xib; path = RiotShareExtension/ShareViewController.xib; sourceTree = SOURCE_ROOT; };
24B5103C1EFA7083004C6AD2 /* ReadReceiptsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReadReceiptsViewController.h; sourceTree = "<group>"; };
24B5103D1EFA7083004C6AD2 /* ReadReceiptsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReadReceiptsViewController.m; sourceTree = "<group>"; };
24B5103F1EFA88CC004C6AD2 /* ReadReceiptsViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ReadReceiptsViewController.xib; sourceTree = "<group>"; };
24CBEC4E1F0EAD310093EABB /* RiotShareExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = RiotShareExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
24D6B3451F3C8F8A00FC7A71 /* FallbackViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FallbackViewController.h; sourceTree = "<group>"; };
24D6B3461F3C8F8A00FC7A71 /* FallbackViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FallbackViewController.m; sourceTree = "<group>"; };
24D6B3471F3C8F8A00FC7A71 /* FallbackViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = FallbackViewController.xib; sourceTree = "<group>"; };
24D6B3481F3C8F8A00FC7A71 /* RoomsListViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RoomsListViewController.h; sourceTree = "<group>"; };
24D6B3491F3C8F8A00FC7A71 /* RoomsListViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RoomsListViewController.m; sourceTree = "<group>"; };
24D6B34A1F3C8F8A00FC7A71 /* SharePresentingViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SharePresentingViewController.h; sourceTree = "<group>"; };
24D6B34B1F3C8F8A00FC7A71 /* SharePresentingViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SharePresentingViewController.m; sourceTree = "<group>"; };
24D6B34C1F3C8F8A00FC7A71 /* ShareViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShareViewController.h; sourceTree = "<group>"; };
24D6B34D1F3C8F8A00FC7A71 /* ShareViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ShareViewController.m; sourceTree = "<group>"; };
24D6B34E1F3C8F8A00FC7A71 /* ShareViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ShareViewController.xib; sourceTree = "<group>"; };
24D6B3561F3C90D300FC7A71 /* ShareRecentsDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShareRecentsDataSource.h; sourceTree = "<group>"; };
24D6B3571F3C90D300FC7A71 /* ShareRecentsDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ShareRecentsDataSource.m; sourceTree = "<group>"; };
3205ED7B1E976C8A003D65FA /* DirectoryServerPickerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DirectoryServerPickerViewController.h; sourceTree = "<group>"; };
3205ED7C1E976C8A003D65FA /* DirectoryServerPickerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DirectoryServerPickerViewController.m; sourceTree = "<group>"; };
3205ED811E97725E003D65FA /* DirectoryServerTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DirectoryServerTableViewCell.h; sourceTree = "<group>"; };
@ -1216,21 +1223,43 @@
children = (
2466B7551F2F80B800AE27B0 /* Info.plist */,
2466B7561F2F80B800AE27B0 /* RiotShareExtension.entitlements */,
2466B7591F2F80B800AE27B0 /* SharePresentingViewController.h */,
2466B75A1F2F80B800AE27B0 /* SharePresentingViewController.m */,
2466B75B1F2F80B800AE27B0 /* ShareViewController.h */,
2466B75C1F2F80B800AE27B0 /* ShareViewController.m */,
2466B75D1F2F80B800AE27B0 /* ShareViewController.xib */,
2466B7571F2F80B800AE27B0 /* RoomsListViewController.h */,
2466B7581F2F80B800AE27B0 /* RoomsListViewController.m */,
2466B7521F2F80B800AE27B0 /* FallbackViewController.h */,
2466B7531F2F80B800AE27B0 /* FallbackViewController.m */,
2466B7541F2F80B800AE27B0 /* FallbackViewController.xib */,
24D6B3441F3C8F8A00FC7A71 /* ViewController */,
24D6B3551F3C8FCC00FC7A71 /* Model */,
);
name = RiotShareExtension;
path = "Riot Share Extension";
sourceTree = "<group>";
};
24D6B3441F3C8F8A00FC7A71 /* ViewController */ = {
isa = PBXGroup;
children = (
24D6B34A1F3C8F8A00FC7A71 /* SharePresentingViewController.h */,
24D6B34B1F3C8F8A00FC7A71 /* SharePresentingViewController.m */,
24D6B34C1F3C8F8A00FC7A71 /* ShareViewController.h */,
24D6B34D1F3C8F8A00FC7A71 /* ShareViewController.m */,
24D6B34E1F3C8F8A00FC7A71 /* ShareViewController.xib */,
24D6B3481F3C8F8A00FC7A71 /* RoomsListViewController.h */,
24D6B3491F3C8F8A00FC7A71 /* RoomsListViewController.m */,
24D6B3451F3C8F8A00FC7A71 /* FallbackViewController.h */,
24D6B3461F3C8F8A00FC7A71 /* FallbackViewController.m */,
24D6B3471F3C8F8A00FC7A71 /* FallbackViewController.xib */,
);
name = ViewController;
path = RiotShareExtension/ViewController;
sourceTree = SOURCE_ROOT;
};
24D6B3551F3C8FCC00FC7A71 /* Model */ = {
isa = PBXGroup;
children = (
245FC3EA1F3CAF9800603C6A /* ShareExtensionManager.h */,
245FC3EB1F3CAF9800603C6A /* ShareExtensionManager.m */,
24D6B3561F3C90D300FC7A71 /* ShareRecentsDataSource.h */,
24D6B3571F3C90D300FC7A71 /* ShareRecentsDataSource.m */,
);
name = Model;
path = RiotShareExtension/Model;
sourceTree = SOURCE_ROOT;
};
3205ED801E97725E003D65FA /* Directory */ = {
isa = PBXGroup;
children = (
@ -2313,16 +2342,16 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
2435179D1F375BA100D0683E /* ShareViewController.xib in Resources */,
24D6B35B1F3CA03300FC7A71 /* ShareViewController.xib in Resources */,
24EEE5B51F2607C500B3C705 /* SegmentedViewController.xib in Resources */,
24EEE5A91F25529900B3C705 /* cancel@2x.png in Resources */,
2435179C1F375B9400D0683E /* Info.plist in Resources */,
243517991F375B7100D0683E /* FallbackViewController.xib in Resources */,
24EEE5A81F25529600B3C705 /* cancel@3x.png in Resources */,
2466B73E1F2DFAC100AE27B0 /* animatedLogo-4.png in Resources */,
2435179F1F375C0F00D0683E /* Vector.strings in Resources */,
24EEE5AF1F25F0F500B3C705 /* Images.xcassets in Resources */,
24EEE5AA1F25529C00B3C705 /* cancel.png in Resources */,
24D6B35E1F3CA03E00FC7A71 /* FallbackViewController.xib in Resources */,
24EEE5A41F24C06E00B3C705 /* (null) in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -2770,13 +2799,16 @@
buildActionMask = 2147483647;
files = (
24EEE5A01F23A08900B3C705 /* RoomTableViewCell.m in Sources */,
24D6B3581F3C90D300FC7A71 /* ShareRecentsDataSource.m in Sources */,
245FC3EF1F3DD30800603C6A /* RecentCellData.m in Sources */,
24D6B35C1F3CA03600FC7A71 /* RoomsListViewController.m in Sources */,
24D6B35A1F3CA02C00FC7A71 /* ShareViewController.m in Sources */,
24D6B3591F3CA02900FC7A71 /* SharePresentingViewController.m in Sources */,
24EEE5A31F23A8C300B3C705 /* AvatarGenerator.m in Sources */,
2435179E1F375BA600D0683E /* RoomsListViewController.m in Sources */,
243517981F375B6800D0683E /* FallbackViewController.m in Sources */,
2435179B1F375B8F00D0683E /* SharePresentingViewController.m in Sources */,
24D6B35D1F3CA03A00FC7A71 /* FallbackViewController.m in Sources */,
24EEE5A11F23A09A00B3C705 /* RiotDesignValues.m in Sources */,
2435179A1F375B8A00D0683E /* ShareViewController.m in Sources */,
24EEE5A21F23A8B400B3C705 /* MXRoom+Riot.m in Sources */,
245FC3ED1F3D079A00603C6A /* ShareExtensionManager.m in Sources */,
24EEE5B41F2607C000B3C705 /* SegmentedViewController.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;

View file

@ -50,6 +50,7 @@
"rename" = "Rename";
"collapse" = "collapse";
"send_to" = "Send to %@";
"sending" = "Sending";
// Authentication
"auth_login" = "Log in";

View file

@ -29,7 +29,7 @@
/**
Update the information displayed by the cell.
@param rooom the room to render.
@param room the room to render.
*/
- (void)render:(MXRoom *)room;

View file

@ -31,7 +31,7 @@
<key>NSExtensionActivationSupportsMovieWithMaxCount</key>
<integer>1</integer>
<key>NSExtensionActivationSupportsImageWithMaxCount</key>
<integer>3</integer>
<integer>1</integer>
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
<integer>1</integer>
<key>NSExtensionActivationSupportsText</key>

View file

@ -0,0 +1,89 @@
/*
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
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 <Foundation/Foundation.h>
#import <MatrixKit/MatrixKit.h>
@class ShareExtensionManager;
/**
The protocol for the manager's delegate
*/
@protocol ShareExtensionManagerDelegate <NSObject>
@required
/**
Called when an image is going to be shared to show a compression prompt
@param extensionManager the ShareExtensionManager object that called the method
@param compressionPrompt the prompt that was prepared for the image which is going to be shared
*/
- (void)shareExtensionManager:(ShareExtensionManager *)extensionManager showImageCompressionPrompt:(UIAlertController *)compressionPrompt;
@optional
/**
Called when the progress of the uploading media changes
@param extensionManager the ShareExtensionManager object that called the method
@param progress the current progress
*/
- (void)shareExtensionManager:(ShareExtensionManager *)extensionManager mediaUploadProgress:(CGFloat)progress;
@end
/**
A class used to share content from the extension
*/
@interface ShareExtensionManager : NSObject
/**
The share extension context that represents a user's sharing request, also stores the content to be shared
*/
@property (nonatomic) NSExtensionContext *shareExtensionContext;
/**
A delegate used to notify about needed UI changes when sharing
*/
@property (nonatomic) id<ShareExtensionManagerDelegate> delegate;
/**
The singleton instance
*/
+ (instancetype)sharedManager;
/**
Send the content that the user has chosen to a room
@param room the room to send the content to
@param failureBlock the code to be executed when sharing has failed for whatever reason
note: there is no "successBlock" parameter because when the sharing succeds, the extension needs to close itself
*/
- (void)sendContentToRoom:(MXRoom *)room failureBlock:(void(^)())failureBlock;
/**
Checks if there is an image in the user chosen content
@return YES if there is, NO otherwise
*/
- (BOOL)hasImageTypeContent;
/**
Terminate the extension and return to the app that started it
@param canceled YES if the user chose to cancel the sharing, NO otherwise
*/
- (void)terminateExtensionCanceled:(BOOL)canceled;
@end

View file

@ -0,0 +1,484 @@
/*
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
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 "ShareExtensionManager.h"
#import "MXKPieChartHUD.h"
@import MobileCoreServices;
typedef NS_ENUM(NSInteger, ImageCompressionMode)
{
ImageCompressionModeNone,
ImageCompressionModeSmall,
ImageCompressionModeMedium,
ImageCompressionModeLarge
};
@interface ShareExtensionManager ()
@property ImageCompressionMode imageCompressionMode;
@property CGFloat actualLargeSize;
@end
@implementation ShareExtensionManager
#pragma mark - Lifecycle
+ (instancetype)sharedManager
{
static ShareExtensionManager *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
[[NSNotificationCenter defaultCenter] addObserver:sharedInstance selector:@selector(onMediaUploadProgress:) name:kMXMediaUploadProgressNotification object:nil];
});
return sharedInstance;
}
#pragma mark - Public
- (void)sendContentToRoom:(MXRoom *)room failureBlock:(void(^)())failureBlock
{
NSString *UTTypeText = (__bridge NSString *)kUTTypeText;
NSString *UTTypeURL = (__bridge NSString *)kUTTypeURL;
NSString *UTTypeImage = (__bridge NSString *)kUTTypeImage;
NSString *UTTypeVideo = (__bridge NSString *)kUTTypeVideo;
NSString *UTTypeFileUrl = (__bridge NSString *)kUTTypeFileURL;
NSString *UTTypeMovie = (__bridge NSString *)kUTTypeMovie;
__weak typeof(self) weakSelf = self;
for (NSExtensionItem *item in self.shareExtensionContext.inputItems)
{
for (NSItemProvider *itemProvider in item.attachments)
{
if ([itemProvider hasItemConformingToTypeIdentifier:UTTypeFileUrl])
{
[itemProvider loadItemForTypeIdentifier:UTTypeFileUrl options:nil completionHandler:^(NSURL *fileUrl, NSError * _Null_unspecified error) {
if (weakSelf)
{
typeof(self) self = weakSelf;
[self sendFileWithUrl:fileUrl toRoom:room extensionItem:item failureBlock:failureBlock];
}
}];
}
else if ([itemProvider hasItemConformingToTypeIdentifier:UTTypeText])
{
[itemProvider loadItemForTypeIdentifier:UTTypeText options:nil completionHandler:^(NSString *text, NSError * _Null_unspecified error) {
if (weakSelf)
{
typeof(self) self = weakSelf;
[self sendText:text toRoom:room extensionItem:item failureBlock:failureBlock];
}
}];
}
else if ([itemProvider hasItemConformingToTypeIdentifier:UTTypeURL])
{
[itemProvider loadItemForTypeIdentifier:UTTypeURL options:nil completionHandler:^(NSURL *url, NSError * _Null_unspecified error) {
if (weakSelf)
{
typeof(self) self = weakSelf;
[self sendText:url.absoluteString toRoom:room extensionItem:item failureBlock:failureBlock];
}
}];
}
else if ([itemProvider hasItemConformingToTypeIdentifier:UTTypeImage])
{
[itemProvider loadItemForTypeIdentifier:UTTypeImage options:nil completionHandler:^(NSData *imageData, NSError * _Null_unspecified error)
{
if (weakSelf)
{
typeof(self) self = weakSelf;
UIImage *image = [[UIImage alloc] initWithData:imageData];
UIAlertController *compressionPrompt = [self compressionPromptForImage:image shareBlock:^{
[self sendImage:imageData withProvider:itemProvider toRoom:room extensionItem:item failureBlock:failureBlock];
}];
[self.delegate shareExtensionManager:self showImageCompressionPrompt:compressionPrompt];
}
}];
}
else if ([itemProvider hasItemConformingToTypeIdentifier:UTTypeVideo])
{
[itemProvider loadItemForTypeIdentifier:UTTypeVideo options:nil completionHandler:^(NSURL *videoLocalUrl, NSError * _Null_unspecified error)
{
if (weakSelf)
{
typeof(self) self = weakSelf;
[self sendVideo:videoLocalUrl toRoom:room extensionItem:item failureBlock:failureBlock];
}
}];
}
else if ([itemProvider hasItemConformingToTypeIdentifier:UTTypeMovie])
{
[itemProvider loadItemForTypeIdentifier:UTTypeMovie options:nil completionHandler:^(NSURL *videoLocalUrl, NSError * _Null_unspecified error)
{
if (weakSelf)
{
typeof(self) self = weakSelf;
[self sendVideo:videoLocalUrl toRoom:room extensionItem:item failureBlock:failureBlock];
}
}];
}
}
}
}
- (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;
}
- (void)terminateExtensionCanceled:(BOOL)canceled
{
if (canceled)
{
[self.shareExtensionContext cancelRequestWithError:[NSError errorWithDomain:@"MXUserCancelErrorDomain" code:4201 userInfo:nil]];
}
else
{
[self.shareExtensionContext cancelRequestWithError:[NSError errorWithDomain:@"MXFailureErrorDomain" code:500 userInfo:nil]];
}
}
- (UIAlertController *)compressionPromptForImage:(UIImage *)image shareBlock:(nonnull void(^)())shareBlock
{
UIAlertController *compressionPrompt;
// Get availabe sizes for this image
MXKImageCompressionSizes compressionSizes = [MXKTools availableCompressionSizesForImage:image];
// 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];
}
}]];
}
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) {
if (weakSelf)
{
typeof(self) self = weakSelf;
self.imageCompressionMode = ImageCompressionModeNone;
if (shareBlock)
{
shareBlock();
}
[compressionPrompt dismissViewControllerAnimated:YES completion:nil];
}
}]];
[compressionPrompt addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
[compressionPrompt dismissViewControllerAnimated:YES completion:nil];
}
}]];
}
return compressionPrompt;
}
#pragma mark - Notifications
- (void)onMediaUploadProgress:(NSNotification *)notification
{
if ([self.delegate respondsToSelector:@selector(shareExtensionManager:mediaUploadProgress:)])
{
[self.delegate shareExtensionManager:self mediaUploadProgress:((NSNumber *)notification.userInfo[kMXMediaLoaderProgressValueKey]).floatValue];
}
}
#pragma mark - Sharing
- (void)sendText:(NSString *)text toRoom:(MXRoom *)room extensionItem:(NSExtensionItem *)extensionItem failureBlock:(void(^)())failureBlock
{
if (!text)
{
NSLog(@"[ShareExtensionManager] loadItemForTypeIdentifier: failed.");
if (failureBlock)
{
failureBlock();
}
return;
}
__weak typeof(self) weakSelf = self;
[room sendTextMessage:text success:^(NSString *eventId) {
if (weakSelf)
{
typeof(self) self = weakSelf;
[self.shareExtensionContext completeRequestReturningItems:@[extensionItem] completionHandler:nil];
}
} failure:^(NSError *error) {
NSLog(@"[ShareExtensionManager] sendTextMessage failed.");
if (failureBlock)
{
failureBlock();
}
}];
}
- (void)sendFileWithUrl:(NSURL *)fileUrl toRoom:(MXRoom *)room extensionItem:(NSExtensionItem *)extensionItem failureBlock:(void(^)())failureBlock
{
if (!fileUrl)
{
NSLog(@"[ShareExtensionManager] loadItemForTypeIdentifier: failed.");
if (failureBlock)
{
failureBlock();
}
return;
}
NSString *mimeType = [fileUrl pathExtension];
__weak typeof(self) weakSelf = self;
[room sendFile:fileUrl mimeType:mimeType localEcho:nil success:^(NSString *eventId) {
if (weakSelf)
{
typeof(self) self = weakSelf;
[self.shareExtensionContext completeRequestReturningItems:@[extensionItem] completionHandler:nil];
}
} failure:^(NSError *error) {
NSLog(@"[ShareExtensionManager] sendFile failed.");
if (failureBlock)
{
failureBlock();
}
}];
}
- (void)sendImage:(NSData *)imageData withProvider:(NSItemProvider*)itemProvider toRoom:(MXRoom *)room extensionItem:(NSExtensionItem *)extensionItem failureBlock:(void(^)())failureBlock
{
if (!imageData)
{
NSLog(@"[ShareExtensionManager] loadItemForTypeIdentifier: failed.");
if (failureBlock)
{
failureBlock();
}
return;
}
//Send the image
UIImage *image = [[UIImage alloc] initWithData:imageData];
if (self.imageCompressionMode == ImageCompressionModeSmall)
{
image = [MXKTools reduceImage:image toFitInSize:CGSizeMake(MXKTOOLS_SMALL_IMAGE_SIZE, MXKTOOLS_SMALL_IMAGE_SIZE)];
}
else if (self.imageCompressionMode == ImageCompressionModeMedium)
{
image = [MXKTools reduceImage:image toFitInSize:CGSizeMake(MXKTOOLS_MEDIUM_IMAGE_SIZE, MXKTOOLS_MEDIUM_IMAGE_SIZE)];
}
else if (self.imageCompressionMode == ImageCompressionModeLarge)
{
image = [MXKTools reduceImage:image toFitInSize:CGSizeMake(self.actualLargeSize, self.actualLargeSize)];
}
NSString *mimeType;
if ([itemProvider hasItemConformingToTypeIdentifier:(__bridge NSString *)kUTTypePNG])
{
mimeType = @"image/png";
}
else if ([itemProvider hasItemConformingToTypeIdentifier:(__bridge NSString *)kUTTypeJPEG])
{
mimeType = @"image/jpeg";
}
else
{
image = [[UIImage alloc] initWithData:UIImageJPEGRepresentation(image, 1.0)];
mimeType = @"image/jpeg";
}
UIImage *thumbnail = nil;
// Thumbnail is useful only in case of encrypted room
if (room.state.isEncrypted)
{
thumbnail = [MXKTools reduceImage:image toFitInSize:CGSizeMake(800, 600)];
if (thumbnail == image)
{
thumbnail = nil;
}
}
__weak typeof(self) weakSelf = self;
[room sendImage:imageData withImageSize:image.size mimeType:mimeType andThumbnail:thumbnail localEcho:nil success:^(NSString *eventId) {
if (weakSelf)
{
typeof(self) self = weakSelf;
[self.shareExtensionContext completeRequestReturningItems:@[extensionItem] completionHandler:nil];
}
} failure:^(NSError *error) {
NSLog(@"[ShareExtensionManager] sendImage failed.");
if (failureBlock)
{
failureBlock();
}
}];
}
- (void)sendVideo:(NSURL *)videoLocalUrl toRoom:(MXRoom *)room extensionItem:(NSExtensionItem *)extensionItem failureBlock:(void(^)())failureBlock
{
if (!videoLocalUrl)
{
NSLog(@"[ShareExtensionManager] loadItemForTypeIdentifier: failed.");
if (failureBlock)
{
failureBlock();
}
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);
__weak typeof(self) weakSelf = self;
[room sendVideo:videoLocalUrl withThumbnail:videoThumbnail localEcho:nil success:^(NSString *eventId) {
if (weakSelf)
{
typeof(self) self = weakSelf;
[self.shareExtensionContext completeRequestReturningItems:@[extensionItem] completionHandler:nil];
}
} failure:^(NSError *error) {
NSLog(@"[ShareExtensionManager] sendVideo failed.");
if (failureBlock)
{
failureBlock();
}
}];
}
@end

View file

@ -0,0 +1,29 @@
/*
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
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 <MatrixKit/MatrixKit.h>
typedef NS_ENUM(NSInteger, ShareRecentsDataSourceMode)
{
RecentsDataSourceModePeople,
RecentsDataSourceModeRooms
};
@interface ShareRecentsDataSource : MXKRecentsDataSource
- (instancetype)initWithMatrixSession:(MXSession *)mxSession dataSourceMode:(ShareRecentsDataSourceMode)dataSourceMode;
@end

View file

@ -0,0 +1,144 @@
/*
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
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 "ShareRecentsDataSource.h"
#import "RoomTableViewCell.h"
#import "RecentCellData.h"
@interface ShareRecentsDataSource ()
@property (nonatomic, readwrite) ShareRecentsDataSourceMode dataSourceMode;
@property NSMutableArray *recentRooms;
@property NSMutableArray *recentPeople;
@end
@implementation ShareRecentsDataSource
- (instancetype)initWithMatrixSession:(MXSession *)mxSession dataSourceMode:(ShareRecentsDataSourceMode)dataSourceMode
{
self = [super initWithMatrixSession:mxSession];
if (self)
{
self.dataSourceMode = dataSourceMode;
_recentRooms = [NSMutableArray array];
_recentPeople = [NSMutableArray array];
}
return self;
}
#pragma mark - Private
- (void)updateArrays
{
[self.recentPeople removeAllObjects];
[self.recentRooms removeAllObjects];
MXKSessionRecentsDataSource *recentsDataSource = displayedRecentsDataSourceArray.firstObject;
NSInteger count = recentsDataSource.numberOfCells;
for (int index = 0; index < count; index++)
{
id<MXKRecentCellDataStoring> cellData = [recentsDataSource cellDataAtIndex:index];
MXRoom* room = cellData.roomSummary.room;
if (self.dataSourceMode == RecentsDataSourceModePeople)
{
if (room.isDirect)
{
[self.recentPeople addObject:cellData];
}
}
else if (self.dataSourceMode == RecentsDataSourceModeRooms)
{
if (!room.isDirect)
{
[self.recentRooms addObject:cellData];
}
}
}
}
#pragma mark - MXKRecentsDataSource
- (Class)cellDataClassForCellIdentifier:(NSString *)identifier
{
return RecentCellData.class;
}
- (id<MXKRecentCellDataStoring>)cellDataAtIndexPath:(NSIndexPath *)indexPath
{
id<MXKRecentCellDataStoring> cellData = nil;
if (self.dataSourceMode == RecentsDataSourceModePeople)
{
cellData = self.recentPeople[indexPath.row];
}
else
{
cellData = self.recentRooms[indexPath.row];
}
return cellData;
}
- (void)dataSource:(MXKDataSource*)dataSource didCellChange:(id)changes
{
[super dataSource:dataSource didCellChange:changes];
[self updateArrays];
[self.delegate dataSource:self didCellChange:changes];
}
#pragma mark - UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (self.dataSourceMode == RecentsDataSourceModePeople)
{
return self.recentPeople.count;
}
else
{
return self.recentRooms.count;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
RoomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[RoomTableViewCell defaultReuseIdentifier]];
MXRoom *room = [self cellDataAtIndexPath:indexPath].roomSummary.room;
[cell render:room];
if (!room.summary.displayname.length && !cell.titleLabel.text.length)
{
cell.titleLabel.text = NSLocalizedStringFromTable(@"room_displayname_no_title", @"Vector", nil);
}
return cell;
}
@end

View file

@ -0,0 +1,21 @@
/*
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
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 <MatrixKit/MatrixKit.h>
@interface FallbackViewController : MXKViewController
@end

View file

@ -0,0 +1,42 @@
/*
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
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 "FallbackViewController.h"
#import "RiotDesignValues.h"
@interface FallbackViewController ()
@property (weak, nonatomic) IBOutlet UILabel *titleLabel;
@end
@implementation FallbackViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.titleLabel.textColor = kRiotSecondaryTextColor;
self.titleLabel.text = NSLocalizedStringFromTable(@"auth_share_extension_prompt", @"Vector", nil);
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end

View file

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="12120" systemVersion="16F73" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12088"/>
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="FallbackViewController">
<connections>
<outlet property="titleLabel" destination="9jC-d4-VqL" id="J3Y-oL-e29"/>
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="4cM-h2-CkH">
<rect key="frame" x="56.5" y="133.5" width="262.5" height="400"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="animatedLogo-4.png" translatesAutoresizingMaskIntoConstraints="NO" id="bAn-eK-zqg">
<rect key="frame" x="20" y="0.0" width="222.5" height="372.5"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="252" verticalCompressionResistancePriority="752" text="loc:prompt" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="9jC-d4-VqL">
<rect key="frame" x="0.0" y="372.5" width="262.5" height="27.5"/>
<fontDescription key="fontDescription" type="system" pointSize="23"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="bAn-eK-zqg" secondAttribute="trailing" constant="20" id="5V5-75-4Cp"/>
<constraint firstItem="bAn-eK-zqg" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="4cM-h2-CkH" secondAttribute="leading" constant="20" id="C2v-9e-YjU"/>
<constraint firstAttribute="trailing" secondItem="9jC-d4-VqL" secondAttribute="trailing" id="DrH-g4-EBL"/>
<constraint firstItem="bAn-eK-zqg" firstAttribute="top" secondItem="4cM-h2-CkH" secondAttribute="top" id="HKr-Vy-i8P"/>
<constraint firstAttribute="bottom" secondItem="9jC-d4-VqL" secondAttribute="bottom" id="LNV-E1-kCG"/>
<constraint firstItem="9jC-d4-VqL" firstAttribute="leading" secondItem="4cM-h2-CkH" secondAttribute="leading" id="RGz-7h-0dt"/>
<constraint firstItem="9jC-d4-VqL" firstAttribute="top" secondItem="bAn-eK-zqg" secondAttribute="bottom" id="bgG-1y-R8q"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="4cM-h2-CkH" firstAttribute="width" secondItem="i5M-Pr-FkT" secondAttribute="width" multiplier="0.7" id="8Lf-tE-JWS"/>
<constraint firstItem="4cM-h2-CkH" firstAttribute="centerX" secondItem="i5M-Pr-FkT" secondAttribute="centerX" id="QsS-hg-ULo"/>
<constraint firstItem="4cM-h2-CkH" firstAttribute="centerY" secondItem="i5M-Pr-FkT" secondAttribute="centerY" id="taY-I3-zBr"/>
<constraint firstItem="4cM-h2-CkH" firstAttribute="height" secondItem="i5M-Pr-FkT" secondAttribute="height" multiplier="0.6" id="uaL-do-Rlq"/>
</constraints>
<point key="canvasLocation" x="33.5" y="64.5"/>
</view>
</objects>
<resources>
<image name="animatedLogo-4.png" width="300" height="300"/>
</resources>
</document>

View file

@ -0,0 +1,25 @@
/*
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
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 <UIKit/UIKit.h>
#import "MXRoom+Riot.h"
#import "ShareRecentsDataSource.h"
@interface RoomsListViewController : UIViewController <MXKDataSourceDelegate>
+ (instancetype)listViewControllerWithDataSource:(ShareRecentsDataSource *)dataSource failureBlock:(void(^)())failureBlock;
@end

View file

@ -0,0 +1,219 @@
/*
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
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 "RoomsListViewController.h"
#import "RoomTableViewCell.h"
#import "NSBundle+MatrixKit.h"
#import "ShareExtensionManager.h"
#import "RecentCellData.h"
#import "RiotDesignValues.h"
#import "MXKPieChartView.h"
#import "MXKPieChartHUD.h"
@interface RoomsListViewController () <UITableViewDelegate, UISearchBarDelegate, ShareExtensionManagerDelegate>
@property (nonatomic) ShareRecentsDataSource *dataSource;
@property (copy) void (^failureBlock)();
@property (nonatomic) UITableView *mainTableView;
@property (nonatomic) UISearchBar *searchBar;
@property (nonatomic) MXKPieChartHUD *hudView;
@end
@implementation RoomsListViewController
#pragma mark - Lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
[self configureTableView];
[self configureSearchBar];
}
#pragma mark - Public
+ (instancetype)listViewControllerWithDataSource:(ShareRecentsDataSource *)dataSource failureBlock:(void(^)())failureBlock
{
RoomsListViewController *listViewController = [[self class] new];
listViewController.dataSource = dataSource;
listViewController.failureBlock = failureBlock;
return listViewController;
}
#pragma mark - Views
- (void)configureTableView
{
self.mainTableView = [[UITableView alloc] initWithFrame:self.view.frame style:UITableViewStylePlain];
self.mainTableView.dataSource = self.dataSource;
self.mainTableView.delegate = self;
[self.mainTableView registerNib:[RoomTableViewCell nib] forCellReuseIdentifier:[RoomTableViewCell defaultReuseIdentifier]];
[self.view addSubview:self.mainTableView];
self.mainTableView.translatesAutoresizingMaskIntoConstraints = NO;
self.view.translatesAutoresizingMaskIntoConstraints = NO;
NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:self.mainTableView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeWidth multiplier:1 constant:0];
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:self.mainTableView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeHeight multiplier:1 constant:0];
NSLayoutConstraint *centerXConstraint = [NSLayoutConstraint constraintWithItem:self.mainTableView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1 constant:0];
NSLayoutConstraint *centerYConstraint = [NSLayoutConstraint constraintWithItem:self.mainTableView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:1 constant:0];
[NSLayoutConstraint activateConstraints:@[widthConstraint, heightConstraint, centerXConstraint, centerYConstraint]];
}
- (void)configureSearchBar
{
self.searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0.0, 0.0, self.view.frame.size.width, 44.0)];
self.searchBar.searchBarStyle = UISearchBarStyleMinimal;
self.searchBar.placeholder = NSLocalizedStringFromTable(@"search_default_placeholder", @"Vector", nil);
self.searchBar.tintColor = kRiotColorGreen;
self.searchBar.showsCancelButton = YES;
self.searchBar.delegate = self;
self.mainTableView.tableHeaderView = self.searchBar;
}
#pragma mark - Private
- (void)showShareAlertForRoomPath:(NSIndexPath *)indexPath
{
NSString *receipantName = [self.dataSource getRoomAtIndexPath:indexPath].riotDisplayname;
if (!receipantName.length)
{
receipantName = NSLocalizedStringFromTable(@"room_displayname_no_title", @"Vector", nil);
}
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:NSLocalizedStringFromTable(@"send_to", @"Vector", nil), receipantName] message:nil preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"] style:UIAlertActionStyleCancel handler:nil];
[alertController addAction:cancelAction];
UIAlertAction *sendAction = [UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"send"] style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
MXRoom *selectedRoom = [self.dataSource getRoomAtIndexPath:indexPath];
self.hudView = [MXKPieChartHUD showLoadingHudOnView:self.view WithMessage:NSLocalizedStringFromTable(@"sending", @"Vector", nil)];
[ShareExtensionManager sharedManager].delegate = self;
self.parentViewController.view.userInteractionEnabled = NO;
[[ShareExtensionManager sharedManager] sendContentToRoom:selectedRoom failureBlock:^{
[self showFailureAlert];
}];
}];
[alertController addAction:sendAction];
[self presentViewController:alertController animated:YES completion:nil];
}
- (void)showFailureAlert
{
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"room_event_failed_to_send", @"Vector", nil) message:nil preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *okAction = [UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"ok"] style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
if (self.failureBlock)
{
self.failureBlock();
}
}];
[alertController addAction:okAction];
[self presentViewController:alertController animated:YES completion:nil];
}
#pragma mark - UITableViewDelegate
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return [RoomTableViewCell cellHeight];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
[self showShareAlertForRoomPath:indexPath];
}
#pragma mark - MXKDataSourceDelegate
- (void)dataSource:(MXKDataSource*)dataSource didCellChange:(id)changes
{
if (dataSource == self.dataSource)
{
[self.mainTableView reloadData];
}
}
- (Class<MXKCellRendering>)cellViewClassForCellData:(MXKCellData*)cellData
{
if ([cellData isKindOfClass:[RecentCellData class]])
{
return [RoomTableViewCell class];
}
return nil;
}
- (NSString *)cellReuseIdentifierForCellData:(MXKCellData*)cellData
{
if ([cellData isKindOfClass:[MXKRecentCellData class]])
{
return [RoomTableViewCell defaultReuseIdentifier];
}
return nil;
}
#pragma mark - UISearchBarDelegate
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
if (searchText.length)
{
[self.dataSource searchWithPatterns:@[searchText]];
}
else
{
[self.dataSource searchWithPatterns:nil];
}
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
{
[searchBar resignFirstResponder];
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar
{
[searchBar resignFirstResponder];
[self.dataSource searchWithPatterns:nil];
}
#pragma mark - ShareExtensionManagerDelegate
- (void)shareExtensionManager:(ShareExtensionManager *)extensionManager showImageCompressionPrompt:(UIAlertController *)compressionPrompt
{
[compressionPrompt popoverPresentationController].sourceView = self.view;
[compressionPrompt popoverPresentationController].sourceRect = self.view.frame;
[self presentViewController:compressionPrompt animated:YES completion:nil];
}
- (void)shareExtensionManager:(ShareExtensionManager *)extensionManager mediaUploadProgress:(CGFloat)progress
{
[self.hudView setProgress:progress];
}
@end

View file

@ -0,0 +1,21 @@
/*
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
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 <UIKit/UIKit.h>
@interface SharePresentingViewController : UIViewController
@end

View file

@ -0,0 +1,55 @@
/*
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
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 "SharePresentingViewController.h"
#import "ShareViewController.h"
#import "ShareExtensionManager.h"
@interface SharePresentingViewController ()
@end
@implementation SharePresentingViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[ShareExtensionManager sharedManager].shareExtensionContext = self.extensionContext;
[self presentShareViewController];
}
- (void)presentShareViewController
{
ShareViewController *shareViewController = [[ShareViewController alloc] init];
shareViewController.providesPresentationContextTransitionStyle = YES;
shareViewController.definesPresentationContext = YES;
shareViewController.modalPresentationStyle = UIModalPresentationOverFullScreen;
shareViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentViewController:shareViewController animated:YES completion:nil];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end

View file

@ -0,0 +1,23 @@
/*
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
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 <UIKit/UIKit.h>
#import <Social/Social.h>
#import <MatrixKit/MatrixKit.h>
@interface ShareViewController : MXKViewController
@end

View file

@ -0,0 +1,178 @@
/*
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
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 "ShareViewController.h"
#import "SegmentedViewController.h"
#import "RoomsListViewController.h"
#import "FallbackViewController.h"
#import "ShareRecentsDataSource.h"
#import "ShareExtensionManager.h"
@interface ShareViewController ()
@property (nonatomic) NSArray <MXRoom *> *rooms;
@property (nonatomic) MXKRecentsDataSource *recentsDataSource;
@property (weak, nonatomic) IBOutlet UIView *masterContainerView;
@property (weak, nonatomic) IBOutlet UILabel *tittleLabel;
@property (weak, nonatomic) IBOutlet UIView *contentView;
@property (nonatomic) SegmentedViewController *segmentedViewController;
@end
@implementation ShareViewController
#pragma mark - Lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
[self prepareSession];
[self configureViews];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onSessionSync:) name:kMXSessionDidSyncNotification object:nil];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
#pragma mark - Private
- (void)prepareSession
{
// Apply the application group
[MXKAppSettings standardAppSettings].applicationGroup = @"group.im.vector";
// Prepare account manager
MXKAccountManager *accountManager = [MXKAccountManager sharedManager];
// Use MXFileStore as MXStore to permanently store events.
accountManager.storeClass = [MXFileStore class];
// Start a matrix session for each enabled accounts.
NSLog(@"[AppDelegate] initMatrixSessions: prepareSessionForActiveAccounts");
[accountManager prepareSessionForActiveAccounts];
// Resume all existing matrix sessions
NSArray *mxAccounts = accountManager.activeAccounts;
for (MXKAccount *account in mxAccounts)
{
[account resume];
[self addMatrixSession:account.mxSession];
}
}
- (void)configureViews
{
self.masterContainerView.layer.cornerRadius = 7;
if (self.mainSession)
{
self.tittleLabel.text = [NSString stringWithFormat:NSLocalizedStringFromTable(@"send_to", @"Vector", nil), @""];
[self configureSegmentedViewController];
}
else
{
NSDictionary *infoDictionary = [NSBundle mainBundle].infoDictionary;
NSString *bundleDisplayName = [infoDictionary objectForKey:@"CFBundleDisplayName"];
self.tittleLabel.text = bundleDisplayName;
[self configureFallbackViewController];
}
}
- (void)configureSegmentedViewController
{
self.segmentedViewController = [SegmentedViewController segmentedViewController];
NSArray *titles = @[NSLocalizedStringFromTable(@"title_rooms", @"Vector", nil) , NSLocalizedStringFromTable(@"title_people", @"Vector", nil)];
void (^failureBlock)() = ^void() {
[self dismissViewControllerAnimated:YES completion:^{
[[ShareExtensionManager sharedManager] terminateExtensionCanceled:NO];
}];
};
ShareRecentsDataSource *roomsDataSource = [[ShareRecentsDataSource alloc] initWithMatrixSession:self.mainSession dataSourceMode:RecentsDataSourceModeRooms];
RoomsListViewController *roomsViewController = [RoomsListViewController listViewControllerWithDataSource:roomsDataSource failureBlock:failureBlock];
roomsDataSource.delegate = roomsViewController;
ShareRecentsDataSource *peopleDataSource = [[ShareRecentsDataSource alloc] initWithMatrixSession:self.mainSession dataSourceMode:RecentsDataSourceModePeople];
RoomsListViewController *peopleViewController = [RoomsListViewController listViewControllerWithDataSource:peopleDataSource failureBlock:failureBlock];
peopleDataSource.delegate = peopleViewController;
[self.segmentedViewController initWithTitles:titles viewControllers:@[roomsViewController, peopleViewController] defaultSelected:0];
[self addChildViewController:self.segmentedViewController];
[self.contentView addSubview:self.segmentedViewController.view];
[self.segmentedViewController didMoveToParentViewController:self];
[self autoPinSubviewEdges:self.segmentedViewController.view toSuperviewEdges:self.contentView];
}
- (void)configureFallbackViewController
{
FallbackViewController *fallbackVC = [FallbackViewController new];
[self addChildViewController:fallbackVC];
[self.contentView addSubview:fallbackVC.view];
[fallbackVC didMoveToParentViewController:self];
[self autoPinSubviewEdges:fallbackVC.view toSuperviewEdges:self.contentView];
}
- (void)autoPinSubviewEdges:(UIView *)subview toSuperviewEdges:(UIView *)superview
{
subview.translatesAutoresizingMaskIntoConstraints = NO;
NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:subview attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:superview attribute:NSLayoutAttributeWidth multiplier:1 constant:0];
widthConstraint.active = YES;
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:subview attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:superview attribute:NSLayoutAttributeHeight multiplier:1 constant:0];
heightConstraint.active = YES;
NSLayoutConstraint *centerXConstraint = [NSLayoutConstraint constraintWithItem:subview attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:superview attribute:NSLayoutAttributeCenterX multiplier:1 constant:0];
centerXConstraint.active = YES;
NSLayoutConstraint *centerYConstraint = [NSLayoutConstraint constraintWithItem:subview attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:superview attribute:NSLayoutAttributeCenterY multiplier:1 constant:0];
centerYConstraint.active = YES;
}
#pragma mark - Notifications
- (void)onSessionSync:(NSNotification *)notification
{
if ([notification.object isEqual:self.mainSession] && !self.rooms.count)
{
self.recentsDataSource = [[MXKRecentsDataSource alloc] initWithMatrixSession:self.mainSession];
}
}
#pragma mark - Actions
- (IBAction)close:(UIButton *)sender
{
[self dismissViewControllerAnimated:YES completion:^{
[[ShareExtensionManager sharedManager] terminateExtensionCanceled:YES];
}];
}
@end

View file

@ -0,0 +1,103 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="12120" systemVersion="16F73" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12088"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="ShareViewController">
<connections>
<outlet property="contentView" destination="jAn-9F-DlU" id="NWV-TS-MGK"/>
<outlet property="masterContainerView" destination="oax-z3-vv0" id="KvN-B4-ZkF"/>
<outlet property="tittleLabel" destination="BQ5-AW-rsV" id="qm6-T7-3LB"/>
<outlet property="view" destination="Bej-An-0PZ" id="sgO-5Q-y1c"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view opaque="NO" contentMode="scaleToFill" id="Bej-An-0PZ">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view alpha="0.59999999999999998" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="0SU-xL-B4a" userLabel="Overlay View">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<color key="backgroundColor" red="0.16337278091968011" green="0.16337278091968011" blue="0.16337278091968011" alpha="1" colorSpace="custom" customColorSpace="displayP3"/>
</view>
<view clipsSubviews="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="oax-z3-vv0" userLabel="Master Container View">
<rect key="frame" x="30" y="58.5" width="315" height="550"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="7Ye-Ni-Nsr" userLabel="Title Container View">
<rect key="frame" x="0.0" y="0.0" width="315" height="40"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="BQ5-AW-rsV" userLabel="Title Label">
<rect key="frame" x="132.5" y="8" width="51.5" height="24"/>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="20"/>
<color key="textColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="RBD-vS-rcr">
<rect key="frame" x="5" y="9" width="30" height="22"/>
<constraints>
<constraint firstAttribute="width" constant="30" id="Pym-oR-KoO"/>
</constraints>
<state key="normal" image="cancel.png"/>
<connections>
<action selector="close:" destination="-1" eventType="touchUpInside" id="rko-d0-x9l"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="BQ5-AW-rsV" firstAttribute="centerY" secondItem="7Ye-Ni-Nsr" secondAttribute="centerY" id="1d9-JD-CVF"/>
<constraint firstItem="RBD-vS-rcr" firstAttribute="leading" secondItem="7Ye-Ni-Nsr" secondAttribute="leading" constant="5" id="Swk-mu-OcC"/>
<constraint firstItem="BQ5-AW-rsV" firstAttribute="centerX" secondItem="7Ye-Ni-Nsr" secondAttribute="centerX" id="ZDz-XD-buD"/>
<constraint firstAttribute="height" constant="40" id="dFb-An-mc6"/>
<constraint firstItem="RBD-vS-rcr" firstAttribute="centerY" secondItem="7Ye-Ni-Nsr" secondAttribute="centerY" id="wUd-cN-mek"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="jAn-9F-DlU">
<rect key="frame" x="0.0" y="40" width="315" height="510"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</view>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="jAn-9F-DlU" secondAttribute="trailing" id="1ce-br-3uU"/>
<constraint firstItem="jAn-9F-DlU" firstAttribute="top" secondItem="7Ye-Ni-Nsr" secondAttribute="bottom" id="1zc-qn-eFO"/>
<constraint firstItem="7Ye-Ni-Nsr" firstAttribute="leading" secondItem="oax-z3-vv0" secondAttribute="leading" id="RTO-at-ZR0"/>
<constraint firstAttribute="trailing" secondItem="7Ye-Ni-Nsr" secondAttribute="trailing" id="RtW-o3-kOS"/>
<constraint firstAttribute="height" relation="lessThanOrEqual" constant="550" id="Uon-Cb-HY2"/>
<constraint firstItem="7Ye-Ni-Nsr" firstAttribute="top" secondItem="oax-z3-vv0" secondAttribute="top" id="YIj-72-BeJ"/>
<constraint firstAttribute="width" relation="lessThanOrEqual" constant="550" id="de2-m2-xUY"/>
<constraint firstItem="jAn-9F-DlU" firstAttribute="leading" secondItem="oax-z3-vv0" secondAttribute="leading" id="hju-02-xMk"/>
<constraint firstAttribute="bottom" secondItem="jAn-9F-DlU" secondAttribute="bottom" id="rDT-Zr-eUB"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="0SU-xL-B4a" secondAttribute="bottom" id="51f-Wf-Yg2"/>
<constraint firstAttribute="bottom" secondItem="oax-z3-vv0" secondAttribute="bottom" priority="750" constant="10" id="Qjd-aK-VSL"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="oax-z3-vv0" secondAttribute="trailing" constant="30" id="VUX-RZ-Jke"/>
<constraint firstItem="0SU-xL-B4a" firstAttribute="top" secondItem="Bej-An-0PZ" secondAttribute="top" id="avs-GA-7eh"/>
<constraint firstAttribute="trailing" secondItem="0SU-xL-B4a" secondAttribute="trailing" id="bgh-PD-Xqx"/>
<constraint firstAttribute="trailing" secondItem="oax-z3-vv0" secondAttribute="trailing" priority="750" constant="30" id="cYZ-zQ-1Dh"/>
<constraint firstItem="oax-z3-vv0" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="Bej-An-0PZ" secondAttribute="leading" constant="30" id="h8V-gU-iRt"/>
<constraint firstItem="oax-z3-vv0" firstAttribute="centerY" secondItem="Bej-An-0PZ" secondAttribute="centerY" id="lbe-HZ-ZsR"/>
<constraint firstItem="oax-z3-vv0" firstAttribute="top" secondItem="Bej-An-0PZ" secondAttribute="top" priority="750" constant="40" id="loD-b9-Eog"/>
<constraint firstItem="oax-z3-vv0" firstAttribute="centerX" secondItem="Bej-An-0PZ" secondAttribute="centerX" id="mzY-Ok-RAp"/>
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="oax-z3-vv0" secondAttribute="bottom" constant="10" id="qnO-IP-fp3"/>
<constraint firstItem="oax-z3-vv0" firstAttribute="leading" secondItem="Bej-An-0PZ" secondAttribute="leading" priority="750" constant="30" id="sYU-AK-85d"/>
<constraint firstItem="oax-z3-vv0" firstAttribute="top" relation="greaterThanOrEqual" secondItem="Bej-An-0PZ" secondAttribute="top" constant="40" id="vXO-uW-nFN"/>
<constraint firstItem="0SU-xL-B4a" firstAttribute="leading" secondItem="Bej-An-0PZ" secondAttribute="leading" id="wol-Zy-dCb"/>
</constraints>
<point key="canvasLocation" x="39.5" y="89.5"/>
</view>
</objects>
<resources>
<image name="cancel.png" width="20" height="20"/>
</resources>
</document>