forked from mirrors/linphone-iphone
enable save image/video to Photos
This commit is contained in:
parent
f5b683e9d7
commit
317665a90b
3 changed files with 133 additions and 13 deletions
|
|
@ -1,11 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="17701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
|
|
@ -30,6 +28,7 @@
|
|||
<outlet property="messageImageView" destination="yMW-cT-bpU" id="MNr-F2-abQ"/>
|
||||
<outlet property="messageText" destination="cx9-0K-P9L" id="kPh-s4-Ioy"/>
|
||||
<outlet property="playButton" destination="cvc-tl-Pcf" id="eKJ-2T-LUl"/>
|
||||
<outlet property="plusLongGestureRecognizer" destination="daf-cW-dRj" id="O5u-t0-uMe"/>
|
||||
<outlet property="resendRecognizer" destination="5ZI-Ip-lGl" id="G2r-On-6mV"/>
|
||||
<outlet property="totalView" destination="8I3-n2-0kS" id="aa8-j9-saW"/>
|
||||
</connections>
|
||||
|
|
@ -93,7 +92,7 @@
|
|||
<rect key="frame" x="10" y="51" width="277" height="4"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
</progressView>
|
||||
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="N75-gL-R6t" userLabel="downloadButton" customClass="UIRoundBorderedButton">
|
||||
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="N75-gL-R6t" userLabel="downloadButton" customClass="UIRoundBorderedButton">
|
||||
<rect key="frame" x="87" y="58" width="113" height="20"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Download"/>
|
||||
|
|
@ -105,7 +104,7 @@
|
|||
<action selector="onDownloadClick:" destination="-1" eventType="touchUpInside" id="8BO-9E-iOX"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="6dl-Nz-rdv" userLabel="cancelButton" customClass="UIRoundBorderedButton">
|
||||
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="6dl-Nz-rdv" userLabel="cancelButton" customClass="UIRoundBorderedButton">
|
||||
<rect key="frame" x="87" y="58" width="113" height="20"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Cancel"/>
|
||||
|
|
@ -120,8 +119,8 @@
|
|||
</button>
|
||||
</subviews>
|
||||
</view>
|
||||
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="cvc-tl-Pcf" userLabel="playButton" customClass="UIRoundBorderedButton">
|
||||
<rect key="frame" x="158" y="106" width="50" height="25"/>
|
||||
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="cvc-tl-Pcf" userLabel="playButton" customClass="UIRoundBorderedButton">
|
||||
<rect key="frame" x="157" y="106" width="50" height="25"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Cancel"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
|
|
@ -157,7 +156,7 @@
|
|||
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="IGl-nl-xIE" userLabel="fileButton">
|
||||
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="IGl-nl-xIE" userLabel="fileButton">
|
||||
<rect key="frame" x="180" y="0.0" width="50" height="50"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<color key="backgroundColor" red="1" green="0.39905477280000001" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
|
|
@ -172,6 +171,7 @@
|
|||
<connections>
|
||||
<outletCollection property="gestureRecognizers" destination="5ZI-Ip-lGl" appends="YES" id="1iY-46-rRR"/>
|
||||
<outletCollection property="gestureRecognizers" destination="aDF-hC-ddO" appends="YES" id="FIv-pl-I8J"/>
|
||||
<outletCollection property="gestureRecognizers" destination="daf-cW-dRj" appends="YES" id="qgk-YT-Grl"/>
|
||||
</connections>
|
||||
</view>
|
||||
</subviews>
|
||||
|
|
@ -189,14 +189,19 @@
|
|||
<action selector="onImageClick:" destination="-1" id="feN-LT-89b"/>
|
||||
</connections>
|
||||
</tapGestureRecognizer>
|
||||
<pongPressGestureRecognizer allowableMovement="10" minimumPressDuration="0.5" id="daf-cW-dRj" userLabel="plusClick">
|
||||
<connections>
|
||||
<action selector="onPlusClick:" destination="-1" id="oFW-FN-6xV"/>
|
||||
</connections>
|
||||
</pongPressGestureRecognizer>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="avatar.png" width="259" height="259"/>
|
||||
<image name="chat_read.png" width="25" height="25"/>
|
||||
<image name="avatar.png" width="414.39999389648438" height="414.39999389648438"/>
|
||||
<image name="chat_read.png" width="20" height="20"/>
|
||||
<image name="color_A.png" width="2" height="2"/>
|
||||
<image name="color_G.png" width="2" height="2"/>
|
||||
<image name="color_I.png" width="2" height="2"/>
|
||||
<image name="color_M.png" width="2" height="2"/>
|
||||
<image name="linphone_logo.png" width="26" height="26"/>
|
||||
<image name="linphone_logo.png" width="41.599998474121094" height="42.400001525878906"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@
|
|||
@property(strong, nonatomic) IBOutlet UITapGestureRecognizer *imageGestureRecognizer;
|
||||
@property (weak, nonatomic) IBOutlet UIButton *fileButton;
|
||||
@property (weak, nonatomic) IBOutlet UIView *fileView;
|
||||
@property (strong, nonatomic) IBOutlet UILongPressGestureRecognizer *plusLongGestureRecognizer;
|
||||
|
||||
- (void)setEvent:(LinphoneEventLog *)event;
|
||||
- (void)setChatMessage:(LinphoneChatMessage *)message;
|
||||
|
|
@ -50,6 +51,7 @@
|
|||
- (IBAction)onResendClick:(id)event;
|
||||
- (IBAction)onPlayClick:(id)sender;
|
||||
- (IBAction)onFileClick:(id)sender;
|
||||
- (IBAction)onPlusClick:(id)sender;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@
|
|||
|
||||
- (void)setChatMessage:(LinphoneChatMessage *)amessage {
|
||||
_imageGestureRecognizer.enabled = NO;
|
||||
_plusLongGestureRecognizer.enabled = NO;
|
||||
_messageImageView.image = nil;
|
||||
_finalImage.image = nil;
|
||||
_finalImage.hidden = TRUE;
|
||||
|
|
@ -133,6 +134,7 @@ static const CGFloat CELL_IMAGE_X_MARGIN = 100;
|
|||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
_fileName.hidden = _fileView.hidden = _fileButton.hidden = NO;
|
||||
_imageGestureRecognizer.enabled = NO;
|
||||
_plusLongGestureRecognizer.enabled = NO;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -144,6 +146,7 @@ static const CGFloat CELL_IMAGE_X_MARGIN = 100;
|
|||
[_messageImageView stopLoading];
|
||||
_messageImageView.hidden = YES;
|
||||
_imageGestureRecognizer.enabled = YES;
|
||||
_plusLongGestureRecognizer.enabled = YES;
|
||||
_finalImage.hidden = NO;
|
||||
[self layoutSubviews];
|
||||
});
|
||||
|
|
@ -187,10 +190,12 @@ static const CGFloat CELL_IMAGE_X_MARGIN = 100;
|
|||
UIImage *image = [[UIImage alloc] initWithData:data];
|
||||
[self loadImageAsset:nil image:image];
|
||||
_imageGestureRecognizer.enabled = YES;
|
||||
_plusLongGestureRecognizer.enabled = YES;
|
||||
} else {
|
||||
// support previous versions
|
||||
[self loadFirstImage:localImage type:PHAssetMediaTypeImage];
|
||||
_imageGestureRecognizer.enabled = YES;
|
||||
_plusLongGestureRecognizer.enabled = YES;
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^ {
|
||||
UIImage *image = [chatTableView.imagesInChatroom objectForKey:localImage];
|
||||
|
|
@ -208,10 +213,12 @@ static const CGFloat CELL_IMAGE_X_MARGIN = 100;
|
|||
UIImage* image = [UIChatBubbleTextCell getImageFromVideoUrl:[ChatConversationView getCacheFileUrl:localVideo]];
|
||||
[self loadImageAsset:nil image:image];
|
||||
_imageGestureRecognizer.enabled = NO;
|
||||
_plusLongGestureRecognizer.enabled = YES;
|
||||
} else {
|
||||
// support previous versions
|
||||
[self loadFirstImage:localVideo type:PHAssetMediaTypeVideo];
|
||||
_imageGestureRecognizer.enabled = NO;
|
||||
_plusLongGestureRecognizer.enabled = YES;
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^ {
|
||||
PHFetchResult<PHAsset *> *assets = [PHAsset fetchAssetsWithLocalIdentifiers:[NSArray arrayWithObject:localVideo] options:nil];
|
||||
|
|
@ -251,11 +258,13 @@ static const CGFloat CELL_IMAGE_X_MARGIN = 100;
|
|||
UIImage* image = [UIChatBubbleTextCell getImageFromVideoUrl:newVersion? [ChatConversationView getCacheFileUrl:localFile] : [VIEW(ChatConversationView) getICloudFileUrl:localFile]];
|
||||
[self loadImageAsset:nil image:image];
|
||||
_imageGestureRecognizer.enabled = NO;
|
||||
_plusLongGestureRecognizer.enabled = YES;
|
||||
} else if ([localFile hasSuffix:@"JPG"] || [localFile hasSuffix:@"PNG"] || [localFile hasSuffix:@"jpg"] || [localFile hasSuffix:@"png"]) {
|
||||
NSData *data = newVersion? [ChatConversationView getCacheFileData:localFile] : [NSData dataWithContentsOfURL:[VIEW(ChatConversationView) getICloudFileUrl:localFile]];
|
||||
UIImage *image = [[UIImage alloc] initWithData:data];
|
||||
[self loadImageAsset:nil image:image];
|
||||
_imageGestureRecognizer.enabled = YES;
|
||||
_plusLongGestureRecognizer.enabled = YES;
|
||||
} else {
|
||||
NSString *text = [NSString stringWithFormat:@"📎 %@",localFile];
|
||||
_fileName.text = text;
|
||||
|
|
@ -361,6 +370,110 @@ static const CGFloat CELL_IMAGE_X_MARGIN = 100;
|
|||
}];
|
||||
}
|
||||
|
||||
- (IBAction)onPlusClick:(id)sender {
|
||||
UILongPressGestureRecognizer *gesture = (UILongPressGestureRecognizer *)sender;
|
||||
if (gesture.state != UIGestureRecognizerStateBegan) {
|
||||
// allow only one click once time
|
||||
return;
|
||||
}
|
||||
DTActionSheet *sheet = [[DTActionSheet alloc] initWithTitle:@""];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[sheet addButtonWithTitle:NSLocalizedString(@"Save to Photos", nil)
|
||||
block:^() {
|
||||
ChatConversationView *view = VIEW(ChatConversationView);
|
||||
LinphoneContent *content = linphone_chat_message_get_file_transfer_information(self.message);
|
||||
NSString *name = [NSString stringWithUTF8String:linphone_content_get_name(content)];
|
||||
// get download path
|
||||
NSString *filePath = [[LinphoneManager cacheDirectory] stringByAppendingPathComponent:name];
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
if ([fileManager fileExistsAtPath:filePath]) {
|
||||
NSData* data = [NSData dataWithContentsOfFile:filePath];
|
||||
NSString *fileType = [NSString stringWithUTF8String:linphone_content_get_type(content)];
|
||||
|
||||
// define a block , not called immediately. To avoid crash when saving photo before PHAuthorizationStatusNotDetermined.
|
||||
void (^block)(void)= ^ {
|
||||
if ([fileType isEqualToString:@"image"] ) {
|
||||
// we're finished, save the image and update the message
|
||||
UIImage *image = [UIImage imageWithData:data];
|
||||
if (!image) {
|
||||
[view showFileDownloadError];
|
||||
return;
|
||||
}
|
||||
__block PHObjectPlaceholder *placeHolder;
|
||||
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
|
||||
PHAssetCreationRequest *request = [PHAssetCreationRequest creationRequestForAssetFromImage:image];
|
||||
placeHolder = [request placeholderForCreatedAsset];
|
||||
} completionHandler:^(BOOL success, NSError *error) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (error) {
|
||||
LOGE(@"Cannot save image data downloaded [%@]", [error localizedDescription]);
|
||||
UIAlertController *errView = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Transfer error", nil)
|
||||
message:NSLocalizedString(@"Cannot write image to photo library",nil)
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"OK"
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action) {}];
|
||||
|
||||
[errView addAction:defaultAction];
|
||||
[PhoneMainView.instance presentViewController:errView animated:YES completion:nil];
|
||||
} else {
|
||||
LOGI(@"Image saved to [%@]", [placeHolder localIdentifier]);
|
||||
}
|
||||
});
|
||||
}];
|
||||
} else if([fileType isEqualToString:@"video"]) {
|
||||
// until image is properly saved, keep a reminder on it so that the
|
||||
// chat bubble is aware of the fact that image is being saved to device
|
||||
|
||||
__block PHObjectPlaceholder *placeHolder;
|
||||
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
|
||||
PHAssetCreationRequest *request = [PHAssetCreationRequest creationRequestForAssetFromVideoAtFileURL:[NSURL fileURLWithPath:filePath]];
|
||||
placeHolder = [request placeholderForCreatedAsset];
|
||||
} completionHandler:^(BOOL success, NSError * _Nullable error) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (error) {
|
||||
LOGE(@"Cannot save video data downloaded [%@]", [error localizedDescription]);
|
||||
UIAlertController *errView = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Transfer error", nil)
|
||||
message:NSLocalizedString(@"Cannot write video to photo library", nil)
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"OK"
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action) {}];
|
||||
|
||||
[errView addAction:defaultAction];
|
||||
[PhoneMainView.instance presentViewController:errView animated:YES completion:nil];
|
||||
} else {
|
||||
LOGI(@"video saved to [%@]", [placeHolder localIdentifier]);
|
||||
}
|
||||
});
|
||||
}];
|
||||
}
|
||||
};
|
||||
|
||||
// When you save an image or video to a photo library, make sure that it is allowed. Otherwise, there will be a backup error.
|
||||
if ([PHPhotoLibrary authorizationStatus] == PHAuthorizationStatusAuthorized) {
|
||||
block();
|
||||
} else {
|
||||
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if ([PHPhotoLibrary authorizationStatus] == PHAuthorizationStatusAuthorized) {
|
||||
block();
|
||||
} else {
|
||||
[[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Photo's permission", nil) message:NSLocalizedString(@"Photo not authorized", nil) delegate:nil cancelButtonTitle:nil otherButtonTitles:@"Continue", nil] show];
|
||||
}
|
||||
});
|
||||
}];
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
[sheet addCancelButtonWithTitle:NSLocalizedString(@"Cancel", nil) block:nil];
|
||||
[sheet showInView:PhoneMainView.instance.view];
|
||||
});
|
||||
}
|
||||
|
||||
- (IBAction)onFileClick:(id)sender {
|
||||
ChatConversationView *view = VIEW(ChatConversationView);
|
||||
NSString *name = [LinphoneManager getMessageAppDataForKey:@"localfile" inMessage:self.message];
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue