mirror of
https://github.com/faroukbmiled/RyukGram.git
synced 2026-07-25 13:40:53 +02:00
modded scinsta with additional features and fixes for recent instagram version
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "../../modules/JGProgressHUD/JGProgressHUD.h"
|
||||
|
||||
#import "../InstagramHeaders.h"
|
||||
#import "../Utils.h"
|
||||
|
||||
#import "Manager.h"
|
||||
|
||||
@interface SCIDownloadPillView : UIView
|
||||
@property (nonatomic, strong) UIProgressView *progressRing;
|
||||
@property (nonatomic, strong) UILabel *textLabel;
|
||||
@property (nonatomic, strong) UILabel *subtitleLabel;
|
||||
@property (nonatomic, strong) UIButton *cancelButton;
|
||||
@property (nonatomic, copy) void (^onCancel)(void);
|
||||
|
||||
- (void)showInView:(UIView *)view;
|
||||
- (void)dismiss;
|
||||
- (void)dismissAfterDelay:(NSTimeInterval)delay;
|
||||
- (void)setProgress:(float)progress;
|
||||
- (void)setText:(NSString *)text;
|
||||
@end
|
||||
|
||||
@interface SCIDownloadDelegate : NSObject <SCIDownloadDelegateProtocol>
|
||||
|
||||
typedef NS_ENUM(NSUInteger, DownloadAction) {
|
||||
share,
|
||||
quickLook,
|
||||
saveToPhotos
|
||||
};
|
||||
@property (nonatomic, readonly) DownloadAction action;
|
||||
@property (nonatomic, readonly) BOOL showProgress;
|
||||
|
||||
@property (nonatomic, strong) SCIDownloadManager *downloadManager;
|
||||
@property (nonatomic, strong) SCIDownloadPillView *pill;
|
||||
|
||||
- (instancetype)initWithAction:(DownloadAction)action showProgress:(BOOL)showProgress;
|
||||
|
||||
- (void)downloadFileWithURL:(NSURL *)url fileExtension:(NSString *)fileExtension hudLabel:(NSString *)hudLabel;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,261 @@
|
||||
#import "Download.h"
|
||||
#import <Photos/Photos.h>
|
||||
|
||||
#pragma mark - SCIDownloadPillView
|
||||
|
||||
@implementation SCIDownloadPillView
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super initWithFrame:CGRectZero];
|
||||
if (self) {
|
||||
self.backgroundColor = [UIColor colorWithWhite:0.1 alpha:0.92];
|
||||
self.layer.cornerRadius = 20;
|
||||
self.clipsToBounds = YES;
|
||||
self.alpha = 0;
|
||||
|
||||
// Circular progress (using a small CAShapeLayer ring)
|
||||
_progressRing = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];
|
||||
_progressRing.progressTintColor = [UIColor systemBlueColor];
|
||||
_progressRing.trackTintColor = [UIColor colorWithWhite:0.3 alpha:1.0];
|
||||
_progressRing.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
_progressRing.layer.cornerRadius = 2;
|
||||
_progressRing.clipsToBounds = YES;
|
||||
[self addSubview:_progressRing];
|
||||
|
||||
// Text
|
||||
_textLabel = [[UILabel alloc] init];
|
||||
_textLabel.text = @"Downloading 0%";
|
||||
_textLabel.textColor = [UIColor whiteColor];
|
||||
_textLabel.font = [UIFont systemFontOfSize:13 weight:UIFontWeightSemibold];
|
||||
_textLabel.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
[self addSubview:_textLabel];
|
||||
|
||||
// Subtitle
|
||||
_subtitleLabel = [[UILabel alloc] init];
|
||||
_subtitleLabel.text = @"Tap to cancel";
|
||||
_subtitleLabel.textColor = [UIColor colorWithWhite:0.6 alpha:1.0];
|
||||
_subtitleLabel.font = [UIFont systemFontOfSize:10 weight:UIFontWeightRegular];
|
||||
_subtitleLabel.textAlignment = NSTextAlignmentCenter;
|
||||
_subtitleLabel.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
[self addSubview:_subtitleLabel];
|
||||
|
||||
// Tap gesture for cancel
|
||||
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap)];
|
||||
[self addGestureRecognizer:tap];
|
||||
|
||||
// Layout: [progress bar]
|
||||
// [text centered]
|
||||
// [subtitle centered]
|
||||
[NSLayoutConstraint activateConstraints:@[
|
||||
[_progressRing.topAnchor constraintEqualToAnchor:self.topAnchor constant:12],
|
||||
[_progressRing.leadingAnchor constraintEqualToAnchor:self.leadingAnchor constant:16],
|
||||
[_progressRing.trailingAnchor constraintEqualToAnchor:self.trailingAnchor constant:-16],
|
||||
[_progressRing.heightAnchor constraintEqualToConstant:4],
|
||||
|
||||
[_textLabel.topAnchor constraintEqualToAnchor:_progressRing.bottomAnchor constant:6],
|
||||
[_textLabel.centerXAnchor constraintEqualToAnchor:self.centerXAnchor],
|
||||
|
||||
[_subtitleLabel.topAnchor constraintEqualToAnchor:_textLabel.bottomAnchor constant:2],
|
||||
[_subtitleLabel.centerXAnchor constraintEqualToAnchor:self.centerXAnchor],
|
||||
[_subtitleLabel.bottomAnchor constraintEqualToAnchor:self.bottomAnchor constant:-10],
|
||||
]];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)handleTap {
|
||||
if (self.onCancel) self.onCancel();
|
||||
}
|
||||
|
||||
- (void)showInView:(UIView *)view {
|
||||
[self removeFromSuperview];
|
||||
self.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
[view addSubview:self];
|
||||
|
||||
[NSLayoutConstraint activateConstraints:@[
|
||||
[self.topAnchor constraintEqualToAnchor:view.safeAreaLayoutGuide.topAnchor constant:4],
|
||||
[self.centerXAnchor constraintEqualToAnchor:view.centerXAnchor],
|
||||
[self.widthAnchor constraintGreaterThanOrEqualToConstant:160],
|
||||
[self.widthAnchor constraintLessThanOrEqualToConstant:220],
|
||||
]];
|
||||
|
||||
[UIView animateWithDuration:0.25 animations:^{
|
||||
self.alpha = 1;
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)dismiss {
|
||||
[UIView animateWithDuration:0.2 animations:^{
|
||||
self.alpha = 0;
|
||||
} completion:^(BOOL finished) {
|
||||
[self removeFromSuperview];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)dismissAfterDelay:(NSTimeInterval)delay {
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
[self dismiss];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)setProgress:(float)progress {
|
||||
[self.progressRing setProgress:progress animated:YES];
|
||||
}
|
||||
|
||||
- (void)setText:(NSString *)text {
|
||||
self.textLabel.text = text;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#pragma mark - SCIDownloadDelegate
|
||||
|
||||
@implementation SCIDownloadDelegate
|
||||
|
||||
- (instancetype)initWithAction:(DownloadAction)action showProgress:(BOOL)showProgress {
|
||||
self = [super init];
|
||||
|
||||
if (self) {
|
||||
_action = action;
|
||||
_showProgress = showProgress;
|
||||
self.downloadManager = [[SCIDownloadManager alloc] initWithDelegate:self];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)downloadFileWithURL:(NSURL *)url fileExtension:(NSString *)fileExtension hudLabel:(NSString *)hudLabel {
|
||||
// Dismiss any existing pill
|
||||
[self.pill dismiss];
|
||||
|
||||
self.pill = [[SCIDownloadPillView alloc] init];
|
||||
|
||||
if (hudLabel) {
|
||||
[self.pill setText:hudLabel];
|
||||
}
|
||||
|
||||
if (!self.showProgress) {
|
||||
self.pill.progressRing.hidden = YES;
|
||||
self.pill.subtitleLabel.text = nil;
|
||||
}
|
||||
|
||||
__weak typeof(self) weakSelf = self;
|
||||
self.pill.onCancel = ^{
|
||||
[weakSelf.downloadManager cancelDownload];
|
||||
};
|
||||
|
||||
UIViewController *topVC = topMostController();
|
||||
UIView *hostView = topVC.view;
|
||||
if (!hostView) hostView = [UIApplication sharedApplication].keyWindow;
|
||||
if (!hostView) {
|
||||
NSLog(@"[SCInsta] Download: No valid view");
|
||||
return;
|
||||
}
|
||||
[self.pill showInView:hostView];
|
||||
|
||||
NSLog(@"[SCInsta] Download: Will start download for url \"%@\" with file extension: \".%@\"", url, fileExtension);
|
||||
[self.downloadManager downloadFileWithURL:url fileExtension:fileExtension];
|
||||
}
|
||||
|
||||
- (void)downloadDidStart {
|
||||
NSLog(@"[SCInsta] Download: Download started");
|
||||
}
|
||||
|
||||
- (void)downloadDidCancel {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self.pill setText:@"Cancelled"];
|
||||
self.pill.subtitleLabel.text = nil;
|
||||
self.pill.progressRing.hidden = YES;
|
||||
[self.pill dismissAfterDelay:0.8];
|
||||
});
|
||||
NSLog(@"[SCInsta] Download: Download cancelled");
|
||||
}
|
||||
|
||||
- (void)downloadDidProgress:(float)progress {
|
||||
if (self.showProgress) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self.pill setProgress:progress];
|
||||
[self.pill setText:[NSString stringWithFormat:@"Downloading %d%%", (int)(progress * 100)]];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (void)downloadDidFinishWithError:(NSError *)error {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (error && error.code != NSURLErrorCancelled) {
|
||||
NSLog(@"[SCInsta] Download: Download failed with error: \"%@\"", error);
|
||||
[self.pill setText:@"Download failed"];
|
||||
self.pill.subtitleLabel.text = nil;
|
||||
self.pill.progressRing.hidden = YES;
|
||||
[self.pill dismissAfterDelay:2.0];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)downloadDidFinishWithFileURL:(NSURL *)fileURL {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self.pill dismiss];
|
||||
|
||||
NSLog(@"[SCInsta] Download: Finished with url: \"%@\"", [fileURL absoluteString]);
|
||||
|
||||
switch (self.action) {
|
||||
case share:
|
||||
[SCIUtils showShareVC:fileURL];
|
||||
break;
|
||||
|
||||
case quickLook:
|
||||
[SCIUtils showQuickLookVC:@[fileURL]];
|
||||
break;
|
||||
|
||||
case saveToPhotos: {
|
||||
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
|
||||
if (status != PHAuthorizationStatusAuthorized) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[SCIUtils showErrorHUDWithDescription:@"Photo library access denied"];
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
|
||||
NSString *ext = [[fileURL pathExtension] lowercaseString];
|
||||
BOOL isVideo = [@[@"mp4", @"mov", @"m4v"] containsObject:ext];
|
||||
|
||||
if (isVideo) {
|
||||
PHAssetCreationRequest *req = [PHAssetCreationRequest creationRequestForAsset];
|
||||
PHAssetResourceCreationOptions *opts = [[PHAssetResourceCreationOptions alloc] init];
|
||||
opts.shouldMoveFile = YES;
|
||||
[req addResourceWithType:PHAssetResourceTypeVideo fileURL:fileURL options:opts];
|
||||
req.creationDate = [NSDate date];
|
||||
} else {
|
||||
PHAssetCreationRequest *req = [PHAssetCreationRequest creationRequestForAsset];
|
||||
PHAssetResourceCreationOptions *opts = [[PHAssetResourceCreationOptions alloc] init];
|
||||
opts.shouldMoveFile = YES;
|
||||
[req addResourceWithType:PHAssetResourceTypePhoto fileURL:fileURL options:opts];
|
||||
req.creationDate = [NSDate date];
|
||||
}
|
||||
} completionHandler:^(BOOL success, NSError *error) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (success) {
|
||||
SCIDownloadPillView *donePill = [[SCIDownloadPillView alloc] init];
|
||||
donePill.progressRing.hidden = YES;
|
||||
donePill.subtitleLabel.text = nil;
|
||||
[donePill setText:@"Saved to Photos"];
|
||||
UIView *hostView = topMostController().view;
|
||||
if (hostView) {
|
||||
[donePill showInView:hostView];
|
||||
[donePill dismissAfterDelay:1.5];
|
||||
}
|
||||
} else {
|
||||
[SCIUtils showErrorHUDWithDescription:@"Failed to save to Photos"];
|
||||
}
|
||||
});
|
||||
}];
|
||||
}];
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,32 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@protocol SCIDownloadDelegateProtocol <NSObject>
|
||||
|
||||
// Methods
|
||||
- (void)downloadDidStart;
|
||||
- (void)downloadDidCancel;
|
||||
- (void)downloadDidProgress:(float)progress;
|
||||
- (void)downloadDidFinishWithError:(NSError *)error;
|
||||
- (void)downloadDidFinishWithFileURL:(NSURL *)fileURL;
|
||||
|
||||
@end
|
||||
|
||||
@interface SCIDownloadManager : NSObject <NSURLSessionDownloadDelegate>
|
||||
|
||||
// Properties
|
||||
@property (nonatomic, weak) id<SCIDownloadDelegateProtocol> delegate;
|
||||
@property (nonatomic, strong) NSURLSession *session;
|
||||
@property (nonatomic, strong) NSURLSessionDownloadTask *task;
|
||||
@property (nonatomic, strong) NSString *fileExtension;
|
||||
|
||||
// Methods
|
||||
- (instancetype)initWithDelegate:(id<SCIDownloadDelegateProtocol>)downloadDelegate;
|
||||
|
||||
- (void)downloadFileWithURL:(NSURL *)url fileExtension:(NSString *)fileExtension;
|
||||
|
||||
- (void)cancelDownload;
|
||||
|
||||
- (NSURL *)moveFileToCacheDir:(NSURL *)oldPath;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,75 @@
|
||||
#import "Manager.h"
|
||||
|
||||
@implementation SCIDownloadManager
|
||||
|
||||
- (instancetype)initWithDelegate:(id<SCIDownloadDelegateProtocol>)downloadDelegate {
|
||||
self = [super init];
|
||||
|
||||
if (self) {
|
||||
self.delegate = downloadDelegate;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)downloadFileWithURL:(NSURL *)url fileExtension:(NSString *)fileExtension {
|
||||
// Properties
|
||||
self.session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:nil];
|
||||
self.task = [self.session downloadTaskWithURL:url];
|
||||
|
||||
// Default to jpg if no other reasonable length extension is provided
|
||||
self.fileExtension = [fileExtension length] >= 3 ? fileExtension : @"jpg";
|
||||
|
||||
[self.task resume];
|
||||
[self.delegate downloadDidStart];
|
||||
}
|
||||
|
||||
- (void)cancelDownload {
|
||||
[self.task cancel];
|
||||
[self.delegate downloadDidCancel];
|
||||
}
|
||||
|
||||
// URLSession methods
|
||||
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
|
||||
NSLog(@"Task wrote %lld bytes of %lld bytes", bytesWritten, totalBytesExpectedToWrite);
|
||||
|
||||
float progress = (float)totalBytesWritten / (float)totalBytesExpectedToWrite;
|
||||
|
||||
[self.delegate downloadDidProgress:progress];
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
|
||||
// Move downloaded file to cache directory
|
||||
NSURL *finalLocation = [self moveFileToCacheDir:location];
|
||||
|
||||
[self.delegate downloadDidFinishWithFileURL:finalLocation];
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
|
||||
NSLog(@"Task completed with error: %@", error);
|
||||
|
||||
[self.delegate downloadDidFinishWithError:error];
|
||||
}
|
||||
|
||||
// Rename downloaded file & move from documents dir -> cache dir
|
||||
- (NSURL *)moveFileToCacheDir:(NSURL *)oldPath {
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
|
||||
NSString *cacheDirectoryPath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
|
||||
NSURL *newPath = [[NSURL fileURLWithPath:cacheDirectoryPath] URLByAppendingPathComponent:[NSString stringWithFormat:@"%@.%@", NSUUID.UUID.UUIDString, self.fileExtension]];
|
||||
|
||||
NSLog(@"[SCInsta] Download Handler: Moving file from: %@ to: %@", oldPath.absoluteString, newPath.absoluteString);
|
||||
|
||||
// Move file to cache directory
|
||||
NSError *fileMoveError;
|
||||
[fileManager moveItemAtURL:oldPath toURL:newPath error:&fileMoveError];
|
||||
|
||||
if (fileMoveError) {
|
||||
NSLog(@"[SCInsta] Download Handler: Error while moving file: %@", oldPath.absoluteString);
|
||||
NSLog(@"[SCInsta] Download Handler: %@", fileMoveError);
|
||||
}
|
||||
|
||||
return newPath;
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user