Prechádzať zdrojové kódy

New system for Video/Audio

Marino Faggiana 6 rokov pred
rodič
commit
987f541ede
36 zmenil súbory, kde vykonal 1459 pridanie a 252 odobranie
  1. 119 0
      Libraries external/KTVHTTPCache/KTVCocoaHTTPServer.framework/Headers/HTTPConnection.h
  2. 48 0
      Libraries external/KTVHTTPCache/KTVCocoaHTTPServer.framework/Headers/HTTPMessage.h
  3. 149 0
      Libraries external/KTVHTTPCache/KTVCocoaHTTPServer.framework/Headers/HTTPResponse.h
  4. 205 0
      Libraries external/KTVHTTPCache/KTVCocoaHTTPServer.framework/Headers/HTTPServer.h
  5. 17 0
      Libraries external/KTVHTTPCache/KTVCocoaHTTPServer.framework/Headers/KTVCocoaHTTPServer.h
  6. BIN
      Libraries external/KTVHTTPCache/KTVCocoaHTTPServer.framework/Info.plist
  7. BIN
      Libraries external/KTVHTTPCache/KTVCocoaHTTPServer.framework/KTVCocoaHTTPServer
  8. 5 0
      Libraries external/KTVHTTPCache/KTVCocoaHTTPServer.framework/Modules/module.modulemap
  9. 15 0
      Libraries external/KTVHTTPCache/KTVHTTPCache.framework/Headers/KTVHCCommon.h
  10. 24 0
      Libraries external/KTVHTTPCache/KTVHTTPCache.framework/Headers/KTVHCDataCacheItem.h
  11. 19 0
      Libraries external/KTVHTTPCache/KTVHTTPCache.framework/Headers/KTVHCDataCacheItemZone.h
  12. 47 0
      Libraries external/KTVHTTPCache/KTVHTTPCache.framework/Headers/KTVHCDataLoader.h
  13. 50 0
      Libraries external/KTVHTTPCache/KTVHTTPCache.framework/Headers/KTVHCDataReader.h
  14. 26 0
      Libraries external/KTVHTTPCache/KTVHTTPCache.framework/Headers/KTVHCDataRequest.h
  15. 30 0
      Libraries external/KTVHTTPCache/KTVHTTPCache.framework/Headers/KTVHCDataResponse.h
  16. 34 0
      Libraries external/KTVHTTPCache/KTVHTTPCache.framework/Headers/KTVHCRange.h
  17. 30 0
      Libraries external/KTVHTTPCache/KTVHTTPCache.framework/Headers/KTVHTTPCache.h
  18. 139 0
      Libraries external/KTVHTTPCache/KTVHTTPCache.framework/Headers/KTVHTTPCacheImp.h
  19. BIN
      Libraries external/KTVHTTPCache/KTVHTTPCache.framework/Info.plist
  20. BIN
      Libraries external/KTVHTTPCache/KTVHTTPCache.framework/KTVHTTPCache
  21. 5 0
      Libraries external/KTVHTTPCache/KTVHTTPCache.framework/Modules/module.modulemap
  22. 30 0
      Libraries external/KTVHTTPCache/KTVHTTPCache.h
  23. 28 0
      Nextcloud.xcodeproj/project.pbxproj
  24. 4 0
      iOSClient/AppDelegate.h
  25. 1 1
      iOSClient/AppDelegate.m
  26. 3 0
      iOSClient/CCGlobal.h
  27. 20 0
      iOSClient/Database/NCManageDatabase.swift
  28. 30 11
      iOSClient/Favorites/CCFavorites.m
  29. 5 6
      iOSClient/Main/CCDetail.h
  30. 308 207
      iOSClient/Main/CCDetail.m
  31. 35 18
      iOSClient/Main/CCMain.m
  32. 5 3
      iOSClient/Photos/CCPhotos.m
  33. 9 1
      iOSClient/Settings/Acknowledgements.rtf
  34. 3 0
      iOSClient/Settings/CCAdvanced.m
  35. BIN
      iOSClient/Supporting Files/pt-PT.lproj/Localizable.strings
  36. 16 5
      iOSClient/Text/NCText.swift

+ 119 - 0
Libraries external/KTVHTTPCache/KTVCocoaHTTPServer.framework/Headers/HTTPConnection.h

@@ -0,0 +1,119 @@
+#import <Foundation/Foundation.h>
+
+@class GCDAsyncSocket;
+@class HTTPMessage;
+@class HTTPServer;
+@class WebSocket;
+@protocol HTTPResponse;
+
+
+#define HTTPConnectionDidDieNotification  @"HTTPConnectionDidDie"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@interface HTTPConfig : NSObject
+{
+	HTTPServer __unsafe_unretained *server;
+	NSString __strong *documentRoot;
+	dispatch_queue_t queue;
+}
+
+- (id)initWithServer:(HTTPServer *)server documentRoot:(NSString *)documentRoot;
+- (id)initWithServer:(HTTPServer *)server documentRoot:(NSString *)documentRoot queue:(dispatch_queue_t)q;
+
+@property (nonatomic, unsafe_unretained, readonly) HTTPServer *server;
+@property (nonatomic, strong, readonly) NSString *documentRoot;
+@property (nonatomic, readonly) dispatch_queue_t queue;
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@interface HTTPConnection : NSObject
+{
+	dispatch_queue_t connectionQueue;
+	GCDAsyncSocket *asyncSocket;
+	HTTPConfig *config;
+	
+	BOOL started;
+	
+	HTTPMessage *request;
+	unsigned int numHeaderLines;
+	
+	BOOL sentResponseHeaders;
+	
+	NSString *nonce;
+	long lastNC;
+	
+	NSObject<HTTPResponse> *httpResponse;
+	
+	NSMutableArray *ranges;
+	NSMutableArray *ranges_headers;
+	NSString *ranges_boundry;
+	int rangeIndex;
+	
+	UInt64 requestContentLength;
+	UInt64 requestContentLengthReceived;
+	UInt64 requestChunkSize;
+	UInt64 requestChunkSizeReceived;
+  
+	NSMutableArray *responseDataSizes;
+}
+
+- (id)initWithAsyncSocket:(GCDAsyncSocket *)newSocket configuration:(HTTPConfig *)aConfig;
+
+- (void)start;
+- (void)stop;
+
+- (void)startConnection;
+
+- (BOOL)supportsMethod:(NSString *)method atPath:(NSString *)path;
+- (BOOL)expectsRequestBodyFromMethod:(NSString *)method atPath:(NSString *)path;
+
+- (BOOL)isSecureServer;
+- (NSArray *)sslIdentityAndCertificates;
+
+- (BOOL)isPasswordProtected:(NSString *)path;
+- (BOOL)useDigestAccessAuthentication;
+- (NSString *)realm;
+- (NSString *)passwordForUser:(NSString *)username;
+
+- (NSDictionary *)parseParams:(NSString *)query;
+- (NSDictionary *)parseGetParams;
+
+- (NSString *)requestURI;
+
+- (NSArray *)directoryIndexFileNames;
+- (NSString *)filePathForURI:(NSString *)path;
+- (NSString *)filePathForURI:(NSString *)path allowDirectory:(BOOL)allowDirectory;
+- (NSObject<HTTPResponse> *)httpResponseForMethod:(NSString *)method URI:(NSString *)path;
+- (WebSocket *)webSocketForURI:(NSString *)path;
+
+- (void)prepareForBodyWithSize:(UInt64)contentLength;
+- (void)processBodyData:(NSData *)postDataChunk;
+- (void)finishBody;
+
+- (void)handleVersionNotSupported:(NSString *)version;
+- (void)handleAuthenticationFailed;
+- (void)handleResourceNotFound;
+- (void)handleInvalidRequest:(NSData *)data;
+- (void)handleUnknownMethod:(NSString *)method;
+
+- (NSData *)preprocessResponse:(HTTPMessage *)response;
+- (NSData *)preprocessErrorResponse:(HTTPMessage *)response;
+
+- (void)finishResponse;
+
+- (BOOL)shouldDie;
+- (void)die;
+
+@end
+
+@interface HTTPConnection (AsynchronousHTTPResponse)
+- (void)responseHasAvailableData:(NSObject<HTTPResponse> *)sender;
+- (void)responseDidAbort:(NSObject<HTTPResponse> *)sender;
+@end

+ 48 - 0
Libraries external/KTVHTTPCache/KTVCocoaHTTPServer.framework/Headers/HTTPMessage.h

@@ -0,0 +1,48 @@
+/**
+ * The HTTPMessage class is a simple Objective-C wrapper around Apple's CFHTTPMessage class.
+**/
+
+#import <Foundation/Foundation.h>
+
+#if TARGET_OS_IPHONE
+  // Note: You may need to add the CFNetwork Framework to your project
+  #import <CFNetwork/CFNetwork.h>
+#endif
+
+#define HTTPVersion1_0  ((NSString *)kCFHTTPVersion1_0)
+#define HTTPVersion1_1  ((NSString *)kCFHTTPVersion1_1)
+
+
+@interface HTTPMessage : NSObject
+{
+	CFHTTPMessageRef message;
+}
+
+- (id)initEmptyRequest;
+
+- (id)initRequestWithMethod:(NSString *)method URL:(NSURL *)url version:(NSString *)version;
+
+- (id)initResponseWithStatusCode:(NSInteger)code description:(NSString *)description version:(NSString *)version;
+
+- (BOOL)appendData:(NSData *)data;
+
+- (BOOL)isHeaderComplete;
+
+- (NSString *)version;
+
+- (NSString *)method;
+- (NSURL *)url;
+
+- (NSInteger)statusCode;
+
+- (NSDictionary *)allHeaderFields;
+- (NSString *)headerField:(NSString *)headerField;
+
+- (void)setHeaderField:(NSString *)headerField value:(NSString *)headerFieldValue;
+
+- (NSData *)messageData;
+
+- (NSData *)body;
+- (void)setBody:(NSData *)body;
+
+@end

+ 149 - 0
Libraries external/KTVHTTPCache/KTVCocoaHTTPServer.framework/Headers/HTTPResponse.h

@@ -0,0 +1,149 @@
+#import <Foundation/Foundation.h>
+
+
+@protocol HTTPResponse
+
+/**
+ * Returns the length of the data in bytes.
+ * If you don't know the length in advance, implement the isChunked method and have it return YES.
+**/
+- (UInt64)contentLength;
+
+/**
+ * The HTTP server supports range requests in order to allow things like
+ * file download resumption and optimized streaming on mobile devices.
+**/
+- (UInt64)offset;
+- (void)setOffset:(UInt64)offset;
+
+/**
+ * Returns the data for the response.
+ * You do not have to return data of the exact length that is given.
+ * You may optionally return data of a lesser length.
+ * However, you must never return data of a greater length than requested.
+ * Doing so could disrupt proper support for range requests.
+ * 
+ * To support asynchronous responses, read the discussion at the bottom of this header.
+**/
+- (NSData *)readDataOfLength:(NSUInteger)length;
+
+/**
+ * Should only return YES after the HTTPConnection has read all available data.
+ * That is, all data for the response has been returned to the HTTPConnection via the readDataOfLength method.
+**/
+- (BOOL)isDone;
+
+@optional
+
+/**
+ * If you need time to calculate any part of the HTTP response headers (status code or header fields),
+ * this method allows you to delay sending the headers so that you may asynchronously execute the calculations.
+ * Simply implement this method and return YES until you have everything you need concerning the headers.
+ * 
+ * This method ties into the asynchronous response architecture of the HTTPConnection.
+ * You should read the full discussion at the bottom of this header.
+ * 
+ * If you return YES from this method,
+ * the HTTPConnection will wait for you to invoke the responseHasAvailableData method.
+ * After you do, the HTTPConnection will again invoke this method to see if the response is ready to send the headers.
+ * 
+ * You should only delay sending the headers until you have everything you need concerning just the headers.
+ * Asynchronously generating the body of the response is not an excuse to delay sending the headers.
+ * Instead you should tie into the asynchronous response architecture, and use techniques such as the isChunked method.
+ * 
+ * Important: You should read the discussion at the bottom of this header.
+**/
+- (BOOL)delayResponseHeaders;
+
+/**
+ * Status code for response.
+ * Allows for responses such as redirect (301), etc.
+**/
+- (NSInteger)status;
+
+/**
+ * If you want to add any extra HTTP headers to the response,
+ * simply return them in a dictionary in this method.
+**/
+- (NSDictionary *)httpHeaders;
+
+/**
+ * If you don't know the content-length in advance,
+ * implement this method in your custom response class and return YES.
+ * 
+ * Important: You should read the discussion at the bottom of this header.
+**/
+- (BOOL)isChunked;
+
+/**
+ * This method is called from the HTTPConnection class when the connection is closed,
+ * or when the connection is finished with the response.
+ * If your response is asynchronous, you should implement this method so you know not to
+ * invoke any methods on the HTTPConnection after this method is called (as the connection may be deallocated).
+**/
+- (void)connectionDidClose;
+
+@end
+
+
+/**
+ * Important notice to those implementing custom asynchronous and/or chunked responses:
+ * 
+ * HTTPConnection supports asynchronous responses.  All you have to do in your custom response class is
+ * asynchronously generate the response, and invoke HTTPConnection's responseHasAvailableData method.
+ * You don't have to wait until you have all of the response ready to invoke this method.  For example, if you
+ * generate the response in incremental chunks, you could call responseHasAvailableData after generating
+ * each chunk.  Please see the HTTPAsyncFileResponse class for an example of how to do this.
+ * 
+ * The normal flow of events for an HTTPConnection while responding to a request is like this:
+ *  - Send http resopnse headers
+ *  - Get data from response via readDataOfLength method.
+ *  - Add data to asyncSocket's write queue.
+ *  - Wait for asyncSocket to notify it that the data has been sent.
+ *  - Get more data from response via readDataOfLength method.
+ *  - ... continue this cycle until the entire response has been sent.
+ * 
+ * With an asynchronous response, the flow is a little different.
+ * 
+ * First the HTTPResponse is given the opportunity to postpone sending the HTTP response headers.
+ * This allows the response to asynchronously execute any code needed to calculate a part of the header.
+ * An example might be the response needs to generate some custom header fields,
+ * or perhaps the response needs to look for a resource on network-attached storage.
+ * Since the network-attached storage may be slow, the response doesn't know whether to send a 200 or 404 yet.
+ * In situations such as this, the HTTPResponse simply implements the delayResponseHeaders method and returns YES.
+ * After returning YES from this method, the HTTPConnection will wait until the response invokes its
+ * responseHasAvailableData method. After this occurs, the HTTPConnection will again query the delayResponseHeaders
+ * method to see if the response is ready to send the headers.
+ * This cycle will continue until the delayResponseHeaders method returns NO.
+ * 
+ * You should only delay sending the response headers until you have everything you need concerning just the headers.
+ * Asynchronously generating the body of the response is not an excuse to delay sending the headers.
+ * 
+ * After the response headers have been sent, the HTTPConnection calls your readDataOfLength method.
+ * You may or may not have any available data at this point. If you don't, then simply return nil.
+ * You should later invoke HTTPConnection's responseHasAvailableData when you have data to send.
+ * 
+ * You don't have to keep track of when you return nil in the readDataOfLength method, or how many times you've invoked
+ * responseHasAvailableData. Just simply call responseHasAvailableData whenever you've generated new data, and
+ * return nil in your readDataOfLength whenever you don't have any available data in the requested range.
+ * HTTPConnection will automatically detect when it should be requesting new data and will act appropriately.
+ * 
+ * It's important that you also keep in mind that the HTTP server supports range requests.
+ * The setOffset method is mandatory, and should not be ignored.
+ * Make sure you take into account the offset within the readDataOfLength method.
+ * You should also be aware that the HTTPConnection automatically sorts any range requests.
+ * So if your setOffset method is called with a value of 100, then you can safely release bytes 0-99.
+ * 
+ * HTTPConnection can also help you keep your memory footprint small.
+ * Imagine you're dynamically generating a 10 MB response.  You probably don't want to load all this data into
+ * RAM, and sit around waiting for HTTPConnection to slowly send it out over the network.  All you need to do
+ * is pay attention to when HTTPConnection requests more data via readDataOfLength.  This is because HTTPConnection
+ * will never allow asyncSocket's write queue to get much bigger than READ_CHUNKSIZE bytes.  You should
+ * consider how you might be able to take advantage of this fact to generate your asynchronous response on demand,
+ * while at the same time keeping your memory footprint small, and your application lightning fast.
+ * 
+ * If you don't know the content-length in advanced, you should also implement the isChunked method.
+ * This means the response will not include a Content-Length header, and will instead use "Transfer-Encoding: chunked".
+ * There's a good chance that if your response is asynchronous and dynamic, it's also chunked.
+ * If your response is chunked, you don't need to worry about range requests.
+**/

+ 205 - 0
Libraries external/KTVHTTPCache/KTVCocoaHTTPServer.framework/Headers/HTTPServer.h

@@ -0,0 +1,205 @@
+#import <Foundation/Foundation.h>
+
+@class GCDAsyncSocket;
+@class WebSocket;
+
+#if TARGET_OS_IPHONE
+  #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 40000 // iPhone 4.0
+    #define IMPLEMENTED_PROTOCOLS <NSNetServiceDelegate>
+  #else
+    #define IMPLEMENTED_PROTOCOLS 
+  #endif
+#else
+  #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 // Mac OS X 10.6
+    #define IMPLEMENTED_PROTOCOLS <NSNetServiceDelegate>
+  #else
+    #define IMPLEMENTED_PROTOCOLS 
+  #endif
+#endif
+
+
+@interface HTTPServer : NSObject IMPLEMENTED_PROTOCOLS
+{
+	// Underlying asynchronous TCP/IP socket
+	GCDAsyncSocket *asyncSocket;
+	
+	// Dispatch queues
+	dispatch_queue_t serverQueue;
+	dispatch_queue_t connectionQueue;
+	void *IsOnServerQueueKey;
+	void *IsOnConnectionQueueKey;
+	
+	// HTTP server configuration
+	NSString *documentRoot;
+	Class connectionClass;
+	NSString *interface;
+	UInt16 port;
+	
+	// NSNetService and related variables
+	NSNetService *netService;
+	NSString *domain;
+	NSString *type;
+	NSString *name;
+	NSString *publishedName;
+	NSDictionary *txtRecordDictionary;
+	
+	// Connection management
+	NSMutableArray *connections;
+	NSMutableArray *webSockets;
+	NSLock *connectionsLock;
+	NSLock *webSocketsLock;
+	
+	BOOL isRunning;
+}
+
+/**
+ * Specifies the document root to serve files from.
+ * For example, if you set this to "/Users/<your_username>/Sites",
+ * then it will serve files out of the local Sites directory (including subdirectories).
+ * 
+ * The default value is nil.
+ * The default server configuration will not serve any files until this is set.
+ * 
+ * If you change the documentRoot while the server is running,
+ * the change will affect future incoming http connections.
+**/
+- (NSString *)documentRoot;
+- (void)setDocumentRoot:(NSString *)value;
+
+/**
+ * The connection class is the class used to handle incoming HTTP connections.
+ * 
+ * The default value is [HTTPConnection class].
+ * You can override HTTPConnection, and then set this to [MyHTTPConnection class].
+ * 
+ * If you change the connectionClass while the server is running,
+ * the change will affect future incoming http connections.
+**/
+- (Class)connectionClass;
+- (void)setConnectionClass:(Class)value;
+
+/**
+ * Set what interface you'd like the server to listen on.
+ * By default this is nil, which causes the server to listen on all available interfaces like en1, wifi etc.
+ * 
+ * The interface may be specified by name (e.g. "en1" or "lo0") or by IP address (e.g. "192.168.4.34").
+ * You may also use the special strings "localhost" or "loopback" to specify that
+ * the socket only accept connections from the local machine.
+**/
+- (NSString *)interface;
+- (void)setInterface:(NSString *)value;
+
+/**
+ * The port number to run the HTTP server on.
+ * 
+ * The default port number is zero, meaning the server will automatically use any available port.
+ * This is the recommended port value, as it avoids possible port conflicts with other applications.
+ * Technologies such as Bonjour can be used to allow other applications to automatically discover the port number.
+ * 
+ * Note: As is common on most OS's, you need root privledges to bind to port numbers below 1024.
+ * 
+ * You can change the port property while the server is running, but it won't affect the running server.
+ * To actually change the port the server is listening for connections on you'll need to restart the server.
+ * 
+ * The listeningPort method will always return the port number the running server is listening for connections on.
+ * If the server is not running this method returns 0.
+**/
+- (UInt16)port;
+- (UInt16)listeningPort;
+- (void)setPort:(UInt16)value;
+
+/**
+ * Bonjour domain for publishing the service.
+ * The default value is "local.".
+ * 
+ * Note: Bonjour publishing requires you set a type.
+ * 
+ * If you change the domain property after the bonjour service has already been published (server already started),
+ * you'll need to invoke the republishBonjour method to update the broadcasted bonjour service.
+**/
+- (NSString *)domain;
+- (void)setDomain:(NSString *)value;
+
+/**
+ * Bonjour name for publishing the service.
+ * The default value is "".
+ * 
+ * If using an empty string ("") for the service name when registering,
+ * the system will automatically use the "Computer Name".
+ * Using an empty string will also handle name conflicts
+ * by automatically appending a digit to the end of the name.
+ * 
+ * Note: Bonjour publishing requires you set a type.
+ * 
+ * If you change the name after the bonjour service has already been published (server already started),
+ * you'll need to invoke the republishBonjour method to update the broadcasted bonjour service.
+ * 
+ * The publishedName method will always return the actual name that was published via the bonjour service.
+ * If the service is not running this method returns nil.
+**/
+- (NSString *)name;
+- (NSString *)publishedName;
+- (void)setName:(NSString *)value;
+
+/**
+ * Bonjour type for publishing the service.
+ * The default value is nil.
+ * The service will not be published via bonjour unless the type is set.
+ * 
+ * If you wish to publish the service as a traditional HTTP server, you should set the type to be "_http._tcp.".
+ * 
+ * If you change the type after the bonjour service has already been published (server already started),
+ * you'll need to invoke the republishBonjour method to update the broadcasted bonjour service.
+**/
+- (NSString *)type;
+- (void)setType:(NSString *)value;
+
+/**
+ * Republishes the service via bonjour if the server is running.
+ * If the service was not previously published, this method will publish it (if the server is running).
+**/
+- (void)republishBonjour;
+
+/**
+ * 
+**/
+- (NSDictionary *)TXTRecordDictionary;
+- (void)setTXTRecordDictionary:(NSDictionary *)dict;
+
+/**
+ * Attempts to starts the server on the configured port, interface, etc.
+ * 
+ * If an error occurs, this method returns NO and sets the errPtr (if given).
+ * Otherwise returns YES on success.
+ * 
+ * Some examples of errors that might occur:
+ * - You specified the server listen on a port which is already in use by another application.
+ * - You specified the server listen on a port number below 1024, which requires root priviledges.
+ * 
+ * Code Example:
+ * 
+ * NSError *err = nil;
+ * if (![httpServer start:&err])
+ * {
+ *     NSLog(@"Error starting http server: %@", err);
+ * }
+**/
+- (BOOL)start:(NSError **)errPtr;
+
+/**
+ * Stops the server, preventing it from accepting any new connections.
+ * You may specify whether or not you want to close the existing client connections.
+ * 
+ * The default stop method (with no arguments) will close any existing connections. (It invokes [self stop:NO])
+**/
+- (void)stop;
+- (void)stop:(BOOL)keepExistingConnections;
+
+- (BOOL)isRunning;
+
+- (void)addWebSocket:(WebSocket *)ws;
+
+- (NSUInteger)numberOfHTTPConnections;
+- (NSUInteger)numberOfWebSocketConnections;
+
+@end

+ 17 - 0
Libraries external/KTVHTTPCache/KTVCocoaHTTPServer.framework/Headers/KTVCocoaHTTPServer.h

@@ -0,0 +1,17 @@
+//
+//  KTVCocoaHTTPServer.h
+//  KTVCocoaHTTPServer
+//
+//  Created by Single on 2017/8/10.
+//  Copyright © 2017年 Single. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+FOUNDATION_EXPORT double KTVCocoaHTTPServerVersionNumber;
+FOUNDATION_EXPORT const unsigned char KTVCocoaHTTPServerVersionString[];
+
+#import <KTVCocoaHTTPServer/HTTPServer.h>
+#import <KTVCocoaHTTPServer/HTTPConnection.h>
+#import <KTVCocoaHTTPServer/HTTPMessage.h>
+#import <KTVCocoaHTTPServer/HTTPResponse.h>

BIN
Libraries external/KTVHTTPCache/KTVCocoaHTTPServer.framework/Info.plist


BIN
Libraries external/KTVHTTPCache/KTVCocoaHTTPServer.framework/KTVCocoaHTTPServer


+ 5 - 0
Libraries external/KTVHTTPCache/KTVCocoaHTTPServer.framework/Modules/module.modulemap

@@ -0,0 +1,5 @@
+framework module KTVCocoaHTTPServer {
+    umbrella header "KTVCocoaHTTPServer.h"
+    export *
+    module * { export * }
+}

+ 15 - 0
Libraries external/KTVHTTPCache/KTVHTTPCache.framework/Headers/KTVHCCommon.h

@@ -0,0 +1,15 @@
+//
+//  KTVHCCommon.h
+//  KTVHTTPCache
+//
+//  Created by Single on 2017/8/17.
+//  Copyright © 2017年 Single. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+#if defined(__cplusplus)
+#define KTVHTTPCACHE_EXTERN extern "C"
+#else
+#define KTVHTTPCACHE_EXTERN extern
+#endif

+ 24 - 0
Libraries external/KTVHTTPCache/KTVHTTPCache.framework/Headers/KTVHCDataCacheItem.h

@@ -0,0 +1,24 @@
+//
+//  KTVHCDataCacheItem.h
+//  KTVHTTPCache
+//
+//  Created by Single on 2017/8/13.
+//  Copyright © 2017年 Single. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class KTVHCDataCacheItemZone;
+
+@interface KTVHCDataCacheItem : NSObject
+
++ (instancetype)new NS_UNAVAILABLE;
+- (instancetype)init NS_UNAVAILABLE;
+
+@property (nonatomic, copy, readonly) NSURL * URL;
+@property (nonatomic, assign, readonly) long long totalLength;
+@property (nonatomic, assign, readonly) long long cacheLength;
+@property (nonatomic, assign, readonly) long long vaildLength;
+@property (nonatomic, copy, readonly) NSArray <KTVHCDataCacheItemZone *> * zones;
+
+@end

+ 19 - 0
Libraries external/KTVHTTPCache/KTVHTTPCache.framework/Headers/KTVHCDataCacheItemZone.h

@@ -0,0 +1,19 @@
+//
+//  KTVHCDataCacheItemZone.h
+//  KTVHTTPCache
+//
+//  Created by Single on 2017/8/13.
+//  Copyright © 2017年 Single. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@interface KTVHCDataCacheItemZone : NSObject
+
++ (instancetype)new NS_UNAVAILABLE;
+- (instancetype)init NS_UNAVAILABLE;
+
+@property (nonatomic, assign, readonly) long long offset;
+@property (nonatomic, assign, readonly) long long length;
+
+@end

+ 47 - 0
Libraries external/KTVHTTPCache/KTVHTTPCache.framework/Headers/KTVHCDataLoader.h

@@ -0,0 +1,47 @@
+//
+//  KTVHCDataLoader.h
+//  KTVHTTPCache
+//
+//  Created by Single on 2018/6/7.
+//  Copyright © 2018 Single. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class KTVHCDataLoader;
+@class KTVHCDataRequest;
+@class KTVHCDataResponse;
+
+@protocol KTVHCDataLoaderDelegate <NSObject>
+
+- (void)loaderDidFinished:(KTVHCDataLoader *)loader;
+- (void)loader:(KTVHCDataLoader *)loader didFailed:(NSError *)error;
+- (void)loader:(KTVHCDataLoader *)loader didChangeProgress:(double)progress;
+
+@end
+
+@interface KTVHCDataLoader : NSObject
+
++ (instancetype)new NS_UNAVAILABLE;
+- (instancetype)init NS_UNAVAILABLE;
+
++ (instancetype)loaderWithRequest:(KTVHCDataRequest *)request;
+
+@property (nonatomic, weak) id <KTVHCDataLoaderDelegate> delegate;
+
+@property (nonatomic, strong) id object;
+
+@property (nonatomic, strong, readonly) KTVHCDataRequest * request;
+@property (nonatomic, strong, readonly) KTVHCDataResponse * response;
+
+@property (nonatomic, strong, readonly) NSError * error;
+
+@property (nonatomic, assign, readonly) BOOL didClosed;
+@property (nonatomic, assign, readonly) BOOL didFinished;
+
+@property (nonatomic, assign, readonly) double progress;
+
+- (void)prepare;
+- (void)close;
+
+@end

+ 50 - 0
Libraries external/KTVHTTPCache/KTVHTTPCache.framework/Headers/KTVHCDataReader.h

@@ -0,0 +1,50 @@
+//
+//  KTVHCDataReader.h
+//  KTVHTTPCache
+//
+//  Created by Single on 2017/8/11.
+//  Copyright © 2017年 Single. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class KTVHCDataReader;
+@class KTVHCDataRequest;
+@class KTVHCDataResponse;
+
+@protocol KTVHCDataReaderDelegate <NSObject>
+
+- (void)readerDidPrepared:(KTVHCDataReader *)reader;
+- (void)readerHasAvailableData:(KTVHCDataReader *)reader;
+- (void)reader:(KTVHCDataReader *)reader didFailed:(NSError *)error;
+
+@end
+
+@interface KTVHCDataReader : NSObject <NSLocking>
+
++ (instancetype)new NS_UNAVAILABLE;
+- (instancetype)init NS_UNAVAILABLE;
+
++ (instancetype)readerWithRequest:(KTVHCDataRequest *)request;
+
+@property (nonatomic, weak) id <KTVHCDataReaderDelegate> delegate;
+
+@property (nonatomic, strong) id object;
+
+@property (nonatomic, strong, readonly) KTVHCDataRequest * request;
+@property (nonatomic, strong, readonly) KTVHCDataResponse * response;
+
+@property (nonatomic, strong, readonly) NSError * error;
+
+@property (nonatomic, assign, readonly) BOOL didClosed;
+@property (nonatomic, assign, readonly) BOOL didPrepared;
+@property (nonatomic, assign, readonly) BOOL didFinished;
+
+@property (nonatomic, assign, readonly) long long readOffset;
+
+- (void)prepare;
+- (void)close;
+
+- (NSData *)readDataOfLength:(NSUInteger)length;
+
+@end

+ 26 - 0
Libraries external/KTVHTTPCache/KTVHTTPCache.framework/Headers/KTVHCDataRequest.h

@@ -0,0 +1,26 @@
+//
+//  KTVHCDataRequest.h
+//  KTVHTTPCache
+//
+//  Created by Single on 2017/8/11.
+//  Copyright © 2017年 Single. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "KTVHCRange.h"
+
+@interface KTVHCDataRequest : NSObject
+
++ (instancetype)new NS_UNAVAILABLE;
+- (instancetype)init NS_UNAVAILABLE;
+
+- (instancetype)initWithURL:(NSURL *)URL headers:(NSDictionary *)headers;
+
+@property (nonatomic, copy, readonly) NSURL * URL;
+@property (nonatomic, copy, readonly) NSDictionary * headers;
+@property (nonatomic, assign, readonly) KTVHCRange range;
+
+- (KTVHCDataRequest *)requestWithRange:(KTVHCRange)range;
+- (KTVHCDataRequest *)requestWithTotalLength:(long long)totalLength;
+
+@end

+ 30 - 0
Libraries external/KTVHTTPCache/KTVHTTPCache.framework/Headers/KTVHCDataResponse.h

@@ -0,0 +1,30 @@
+//
+//  KTVHCDataResponse.h
+//  KTVHTTPCache
+//
+//  Created by Single on 2017/8/24.
+//  Copyright © 2017年 Single. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "KTVHCRange.h"
+
+@interface KTVHCDataResponse : NSObject
+
++ (instancetype)new NS_UNAVAILABLE;
+- (instancetype)init NS_UNAVAILABLE;
+
+- (instancetype)initWithURL:(NSURL *)URL headers:(NSDictionary *)headers;
+
+@property (nonatomic, copy, readonly) NSURL * URL;
+@property (nonatomic, copy, readonly) NSDictionary * headers;
+@property (nonatomic, copy, readonly) NSDictionary * headersWithoutRangeAndLength;
+
+@property (nonatomic, copy, readonly) NSString * contentType;
+@property (nonatomic, assign, readonly) KTVHCRange range;
+@property (nonatomic, assign, readonly) long long totalLength;
+@property (nonatomic, assign, readonly) long long currentLength;
+
+- (KTVHCDataResponse *)responseWithRange:(KTVHCRange)range;
+
+@end

+ 34 - 0
Libraries external/KTVHTTPCache/KTVHTTPCache.framework/Headers/KTVHCRange.h

@@ -0,0 +1,34 @@
+//
+//  KTVHCRange.h
+//  KTVHTTPCache
+//
+//  Created by Single on 2018/5/20.
+//  Copyright © 2018年 Single. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+typedef struct KTVHCRange {
+    long long start;
+    long long end;
+} KTVHCRange;
+
+static const long long KTVHCNotFound = LONG_LONG_MAX;
+
+BOOL KTVHCRangeIsFull(KTVHCRange range);
+BOOL KTVHCRangeIsVaild(KTVHCRange range);
+BOOL KTVHCRangeIsInvaild(KTVHCRange range);
+BOOL KTVHCEqualRanges(KTVHCRange range1, KTVHCRange range2);
+long long KTVHCRangeGetLength(KTVHCRange range);
+NSString * KTVHCStringFromRange(KTVHCRange range);
+NSDictionary * KTVHCRangeFillToRequestHeaders(KTVHCRange range, NSDictionary * headers);
+NSDictionary * KTVHCRangeFillToResponseHeaders(KTVHCRange range, NSDictionary * headers, long long totalLength);
+
+KTVHCRange KTVHCMakeRange(long long start, long long end);
+KTVHCRange KTVHCRangeZero(void);
+KTVHCRange KTVHCRangeFull(void);
+KTVHCRange KTVHCRangeInvaild(void);
+KTVHCRange KTVHCRangeWithSeparateValue(NSString * value);
+KTVHCRange KTVHCRangeWithRequestHeaderValue(NSString * value);
+KTVHCRange KTVHCRangeWithResponseHeaderValue(NSString * value, long long * totalLength);
+KTVHCRange KTVHCRangeWithEnsureLength(KTVHCRange range, long long ensureLength);

+ 30 - 0
Libraries external/KTVHTTPCache/KTVHTTPCache.framework/Headers/KTVHTTPCache.h

@@ -0,0 +1,30 @@
+//
+//  KTVHTTPCache.h
+//  KTVHTTPCache
+//
+//  Created by Single on 2017/8/10.
+//  Copyright © 2017年 Single. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+FOUNDATION_EXPORT double KTVHTTPCacheVersionNumber;
+FOUNDATION_EXPORT const unsigned char KTVHTTPCacheVersionString[];
+
+#pragma mark - Interface
+
+#import <KTVHTTPCache/KTVHTTPCacheImp.h>
+
+#pragma mark - Data Storage
+
+#import <KTVHTTPCache/KTVHCDataReader.h>
+#import <KTVHTTPCache/KTVHCDataLoader.h>
+#import <KTVHTTPCache/KTVHCDataRequest.h>
+#import <KTVHTTPCache/KTVHCDataResponse.h>
+#import <KTVHTTPCache/KTVHCDataCacheItem.h>
+#import <KTVHTTPCache/KTVHCDataCacheItemZone.h>
+
+#pragma mark - Common
+
+#import <KTVHTTPCache/KTVHCRange.h>
+#import <KTVHTTPCache/KTVHCCommon.h>

+ 139 - 0
Libraries external/KTVHTTPCache/KTVHTTPCache.framework/Headers/KTVHTTPCacheImp.h

@@ -0,0 +1,139 @@
+//
+//  KTVHTTPCacheImp.h
+//  KTVHTTPCache
+//
+//  Created by Single on 2017/8/13.
+//  Copyright © 2017年 Single. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class KTVHCDataReader;
+@class KTVHCDataLoader;
+@class KTVHCDataRequest;
+@class KTVHCDataCacheItem;
+
+@interface KTVHTTPCache : NSObject
+
+#pragma mark - HTTP Server
+
+/**
+ *  Start & Stop HTTP Server.
+ */
++ (void)proxyStart:(NSError **)error;
++ (void)proxyStop;
+
++ (BOOL)proxyIsRunning;
+
+/**
+ *  Return the URL string for local server.
+ */
++ (NSURL *)proxyURLWithOriginalURL:(NSURL *)URL;
++ (NSString *)proxyURLStringWithOriginalURLString:(NSString *)URLString;
+
+#pragma mark - Data Storage
+
+/**
+ *  If the content of the URL is finish cached, return the file path for the content. Otherwise return nil.
+ */
++ (NSURL *)cacheCompleteFileURLIfExistedWithURL:(NSURL *)URL;
++ (NSString *)cacheCompleteFilePathIfExistedWithURLString:(NSString *)URLString;
+
+/**
+ *  Data Reader.
+ */
++ (KTVHCDataReader *)cacheReaderWithRequest:(KTVHCDataRequest *)request;
+
+/**
+ *  Data Loader.
+ */
++ (KTVHCDataLoader *)cacheLoaderWithRequest:(KTVHCDataRequest *)request;
+
+/**
+ *  Cache State.
+ */
++ (void)cacheSetMaxCacheLength:(long long)maxCacheLength;
++ (long long)cacheMaxCacheLength;
++ (long long)cacheTotalCacheLength;
+
+/**
+ *  Cache Item.
+ */
++ (KTVHCDataCacheItem *)cacheCacheItemWithURL:(NSURL *)URL;
++ (KTVHCDataCacheItem *)cacheCacheItemWithURLString:(NSString *)URLString;
++ (NSArray <KTVHCDataCacheItem *> *)cacheAllCacheItems;
+
+/**
+ *  Delete Cache.
+ */
++ (void)cacheDeleteCacheWithURL:(NSURL *)URL;
++ (void)cacheDeleteCacheWithURLString:(NSString *)URLString;
++ (void)cacheDeleteAllCaches;
+
+#pragma mark - Token
+
+/**
+ *  URL Filter.
+ *
+ *  High frequency call. Make it simple.
+ */
++ (void)tokenSetURLFilter:(NSURL * (^)(NSURL * URL))URLFilter;
+
+#pragma mark - Download
+
++ (void)downloadSetTimeoutInterval:(NSTimeInterval)timeoutInterval;
++ (NSTimeInterval)downloadTimeoutInterval;
+
+/**
+ *  Whitelist Header Fields.
+ */
++ (void)downloadSetWhitelistHeaderKeys:(NSArray <NSString *> *)whitelistHeaderKeys;
++ (NSArray <NSString *> *)downloadWhitelistHeaderKeys;
+
+/**
+ *  Additional Header Fields.
+ */
++ (void)downloadSetAdditionalHeaders:(NSDictionary <NSString *, NSString *> *)additionalHeaders;
++ (NSDictionary <NSString *, NSString *> *)downloadAdditionalHeaders;
+
+/**
+ *  Default values: 'video/x', 'audio/x', 'application/mp4', 'application/octet-stream', 'binary/octet-stream'
+ */
++ (void)downloadSetAcceptContentTypes:(NSArray <NSString *> *)acceptContentTypes;
++ (NSArray <NSString *> *)downloadAcceptContentTypes;
+
+/**
+ *  If the receive response's Content-Type not included in acceptContentTypes, this method will be called.
+ *  The return value of block to decide whether to continue to load resources. Otherwise the HTTP task will be rejected.
+ */
++ (void)downloadSetUnsupportContentTypeFilter:(BOOL(^)(NSURL * URL, NSString * contentType))contentTypeFilter;
+
+#pragma mark - Log
+
+/**
+ *  Console & Record.
+ */
++ (void)logAddLog:(NSString *)log;
+
+/**
+ *  DEBUG & RELEASE : Default is NO.
+ */
++ (void)logSetConsoleLogEnable:(BOOL)consoleLogEnable;
++ (BOOL)logConsoleLogEnable;
+
+/**
+ *  DEBUG & RELEASE : Default is NO.
+ */
++ (void)logSetRecordLogEnable:(BOOL)recordLogEnable;
++ (BOOL)logRecordLogEnable;
+
++ (NSString *)logRecordLogFilePath;      // nullable
++ (void)logDeleteRecordLog;
+
+/**
+ *  Error
+ */
++ (NSArray <NSError *> *)logAllErrors;
++ (NSError *)logLastError;
+
+@end

BIN
Libraries external/KTVHTTPCache/KTVHTTPCache.framework/Info.plist


BIN
Libraries external/KTVHTTPCache/KTVHTTPCache.framework/KTVHTTPCache


+ 5 - 0
Libraries external/KTVHTTPCache/KTVHTTPCache.framework/Modules/module.modulemap

@@ -0,0 +1,5 @@
+framework module KTVHTTPCache {
+    umbrella header "KTVHTTPCache.h"
+    export *
+    module * { export * }
+}

+ 30 - 0
Libraries external/KTVHTTPCache/KTVHTTPCache.h

@@ -0,0 +1,30 @@
+//
+//  KTVHTTPCache.h
+//  KTVHTTPCache
+//
+//  Created by Single on 2017/8/10.
+//  Copyright © 2017年 Single. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+FOUNDATION_EXPORT double KTVHTTPCacheVersionNumber;
+FOUNDATION_EXPORT const unsigned char KTVHTTPCacheVersionString[];
+
+#pragma mark - Interface
+
+#import <KTVHTTPCache/KTVHTTPCacheImp.h>
+
+#pragma mark - Data Storage
+
+#import <KTVHTTPCache/KTVHCDataReader.h>
+#import <KTVHTTPCache/KTVHCDataLoader.h>
+#import <KTVHTTPCache/KTVHCDataRequest.h>
+#import <KTVHTTPCache/KTVHCDataResponse.h>
+#import <KTVHTTPCache/KTVHCDataCacheItem.h>
+#import <KTVHTTPCache/KTVHCDataCacheItemZone.h>
+
+#pragma mark - Common
+
+#import <KTVHTTPCache/KTVHCRange.h>
+#import <KTVHTTPCache/KTVHCCommon.h>

+ 28 - 0
Nextcloud.xcodeproj/project.pbxproj

@@ -376,6 +376,12 @@
 		F780710A1EDAB65800EAFFF6 /* NSNotificationCenter+MainThread.m in Sources */ = {isa = PBXBuildFile; fileRef = F78071081EDAB65800EAFFF6 /* NSNotificationCenter+MainThread.m */; };
 		F78071211EDB135100EAFFF6 /* CCPhotos.m in Sources */ = {isa = PBXBuildFile; fileRef = F78071201EDB135100EAFFF6 /* CCPhotos.m */; };
 		F78295311F962EFA00A572F5 /* NCEndToEndEncryption.m in Sources */ = {isa = PBXBuildFile; fileRef = F70CAE391F8CF31A008125FD /* NCEndToEndEncryption.m */; };
+		F784FA28210F672E00256C26 /* KTVCocoaHTTPServer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F784FA26210F672E00256C26 /* KTVCocoaHTTPServer.framework */; };
+		F784FA29210F672E00256C26 /* KTVHTTPCache.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F784FA27210F672E00256C26 /* KTVHTTPCache.framework */; };
+		F784FA2E210F6FC000256C26 /* KTVCocoaHTTPServer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F784FA26210F672E00256C26 /* KTVCocoaHTTPServer.framework */; };
+		F784FA2F210F6FC000256C26 /* KTVCocoaHTTPServer.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = F784FA26210F672E00256C26 /* KTVCocoaHTTPServer.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+		F784FA30210F6FC500256C26 /* KTVHTTPCache.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F784FA27210F672E00256C26 /* KTVHTTPCache.framework */; };
+		F784FA31210F6FC500256C26 /* KTVHTTPCache.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = F784FA27210F672E00256C26 /* KTVHTTPCache.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
 		F78964AD1EBB576C00403E13 /* JDStatusBarNotification.m in Sources */ = {isa = PBXBuildFile; fileRef = F78964A81EBB576C00403E13 /* JDStatusBarNotification.m */; };
 		F78964AE1EBB576C00403E13 /* JDStatusBarStyle.m in Sources */ = {isa = PBXBuildFile; fileRef = F78964AA1EBB576C00403E13 /* JDStatusBarStyle.m */; };
 		F78964AF1EBB576C00403E13 /* JDStatusBarView.m in Sources */ = {isa = PBXBuildFile; fileRef = F78964AC1EBB576C00403E13 /* JDStatusBarView.m */; };
@@ -598,6 +604,8 @@
 			files = (
 				F7DFE25A1EBDC53200CF5202 /* RealmSwift.framework in Embed Frameworks */,
 				F7DFE2581EBDC52E00CF5202 /* Realm.framework in Embed Frameworks */,
+				F784FA2F210F6FC000256C26 /* KTVCocoaHTTPServer.framework in Embed Frameworks */,
+				F784FA31210F6FC500256C26 /* KTVHTTPCache.framework in Embed Frameworks */,
 			);
 			name = "Embed Frameworks";
 			runOnlyForDeploymentPostprocessing = 0;
@@ -1256,6 +1264,9 @@
 		F78071201EDB135100EAFFF6 /* CCPhotos.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCPhotos.m; sourceTree = "<group>"; };
 		F78316861C0CB3CA00C43975 /* CCShareUserOC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCShareUserOC.h; sourceTree = "<group>"; };
 		F78316871C0CB3CA00C43975 /* CCShareUserOC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCShareUserOC.m; sourceTree = "<group>"; };
+		F784FA25210F672E00256C26 /* KTVHTTPCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KTVHTTPCache.h; sourceTree = "<group>"; };
+		F784FA26210F672E00256C26 /* KTVCocoaHTTPServer.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = KTVCocoaHTTPServer.framework; sourceTree = "<group>"; };
+		F784FA27210F672E00256C26 /* KTVHTTPCache.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = KTVHTTPCache.framework; sourceTree = "<group>"; };
 		F78964A71EBB576C00403E13 /* JDStatusBarNotification.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JDStatusBarNotification.h; sourceTree = "<group>"; };
 		F78964A81EBB576C00403E13 /* JDStatusBarNotification.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JDStatusBarNotification.m; sourceTree = "<group>"; };
 		F78964A91EBB576C00403E13 /* JDStatusBarStyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JDStatusBarStyle.h; sourceTree = "<group>"; };
@@ -1704,6 +1715,7 @@
 				F75EDFBD1E8C112F00E6F369 /* libsqlite3.0.tbd in Frameworks */,
 				F72D0FFF210B6638009C96B7 /* FirebaseNanoPB.framework in Frameworks */,
 				F7DFE2541EBDC3A400CF5202 /* RealmSwift.framework in Frameworks */,
+				F784FA2E210F6FC000256C26 /* KTVCocoaHTTPServer.framework in Frameworks */,
 				F7A377161EB2364A002856D3 /* Crashlytics.framework in Frameworks */,
 				F7FC7D561DC1F93800BB2C6A /* libz.tbd in Frameworks */,
 				F7A3771A1EB2364A002856D3 /* Fabric.framework in Frameworks */,
@@ -1714,9 +1726,12 @@
 				F72D1000210B6638009C96B7 /* FirebaseInstanceID.framework in Frameworks */,
 				F7BB14961D5B62C000ECEE68 /* libcrypto.a in Frameworks */,
 				F7BB14971D5B62C000ECEE68 /* libssl.a in Frameworks */,
+				F784FA28210F672E00256C26 /* KTVCocoaHTTPServer.framework in Frameworks */,
+				F784FA30210F6FC500256C26 /* KTVHTTPCache.framework in Frameworks */,
 				F72D1001210B6638009C96B7 /* FirebaseCoreDiagnostics.framework in Frameworks */,
 				F72D0FFA210B6638009C96B7 /* FirebaseMessaging.framework in Frameworks */,
 				F72D0FFC210B6638009C96B7 /* FirebaseAnalytics.framework in Frameworks */,
+				F784FA29210F672E00256C26 /* KTVHTTPCache.framework in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -1874,6 +1889,7 @@
 				F7659A2A1DC0B72F004860C4 /* EARestrictedScrollView */,
 				F72D0FED210B6638009C96B7 /* Firebase */,
 				F78964A61EBB576C00403E13 /* JDStatusBarNotification */,
+				F784FA24210F672E00256C26 /* KTVHTTPCache */,
 				F7659A2F1DC0B737004860C4 /* iRate */,
 				F70F04821C889183008DAB36 /* MBProgressHUD */,
 				F7DC5FD31F00F98B00A903C7 /* MGSwipeTableCell */,
@@ -2528,6 +2544,16 @@
 			path = Photos;
 			sourceTree = "<group>";
 		};
+		F784FA24210F672E00256C26 /* KTVHTTPCache */ = {
+			isa = PBXGroup;
+			children = (
+				F784FA25210F672E00256C26 /* KTVHTTPCache.h */,
+				F784FA26210F672E00256C26 /* KTVCocoaHTTPServer.framework */,
+				F784FA27210F672E00256C26 /* KTVHTTPCache.framework */,
+			);
+			path = KTVHTTPCache;
+			sourceTree = "<group>";
+		};
 		F78964A61EBB576C00403E13 /* JDStatusBarNotification */ = {
 			isa = PBXGroup;
 			children = (
@@ -4705,6 +4731,7 @@
 					"$(PROJECT_DIR)\"/Libraries external/Firebase\"/**",
 					"$(PROJECT_DIR)\"/Libraries external/Fabric\"",
 					"$(PROJECT_DIR)\"/Libraries external/Realm\"",
+					"$(PROJECT_DIR)\"/Libraries external/KTVHTTPCache\"",
 				);
 				GCC_PRECOMPILE_PREFIX_HEADER = YES;
 				GCC_PREFIX_HEADER = iOSClient/CryptoCloud.pch;
@@ -4753,6 +4780,7 @@
 					"$(PROJECT_DIR)\"/Libraries external/Firebase\"/**",
 					"$(PROJECT_DIR)\"/Libraries external/Fabric\"",
 					"$(PROJECT_DIR)\"/Libraries external/Realm\"",
+					"$(PROJECT_DIR)\"/Libraries external/KTVHTTPCache\"",
 				);
 				GCC_PRECOMPILE_PREFIX_HEADER = YES;
 				GCC_PREFIX_HEADER = iOSClient/CryptoCloud.pch;

+ 4 - 0
iOSClient/AppDelegate.h

@@ -82,6 +82,10 @@
 // Passcode lockDirectory
 @property (nonatomic, strong) NSDate *sessionePasscodeLock;
 
+// Audio Video
+@property (nonatomic, strong) AVPlayer *player;
+@property (nonatomic, strong) AVPlayerViewController *playerController;
+
 // Remenu
 @property (nonatomic, strong) REMenu *reMainMenu;
 @property (nonatomic, strong) REMenuItem *selezionaItem;

+ 1 - 1
iOSClient/AppDelegate.m

@@ -189,7 +189,7 @@
     
     //AV Session
     [[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error:nil];
-    //[[AVAudioSession sharedInstance] setActive:YES error:nil];
+    // [[AVAudioSession sharedInstance] setActive:YES error:nil];
     [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
     
     // How to hide UINavigationBar 1px bottom line < iOS 11

+ 3 - 0
iOSClient/CCGlobal.h

@@ -171,6 +171,9 @@
 // Max Size Operation
 #define k_maxSizeOperationUpload                        524288000   // 500 MB
 
+// Max Cache Proxy Video
+#define k_maxHTTPCache                                  10737418240 // 10GB
+
 // Error
 #define k_CCErrorTaskNil                                -9999
 #define k_CCErrorTaskDownloadNotFound                   -9998

+ 20 - 0
iOSClient/Database/NCManageDatabase.swift

@@ -1906,6 +1906,10 @@ class NCManageDatabase: NSObject {
         }
     }
     
+    @objc func initNewMetadata(_ metadata: tableMetadata) -> tableMetadata {
+        return tableMetadata.init(value: metadata)
+    }
+    
     //MARK: -
     //MARK: Table Photos
     @objc func getTablePhotos(addMetadatasFromUpload: [tableMetadata]) -> [tableMetadata]? {
@@ -1934,6 +1938,22 @@ class NCManageDatabase: NSObject {
         }
     }
     
+    @objc func getTablePhoto(predicate: NSPredicate) -> tableMetadata? {
+        
+        guard self.getAccountActive() != nil else {
+            return nil
+        }
+        
+        let realm = try! Realm()
+        realm.refresh()
+        
+        guard let result = realm.objects(tablePhotos.self).filter(predicate).first else {
+            return nil
+        }
+        
+        return tableMetadata.init(value: result)
+    }
+    
     @objc func createTablePhotos(_ metadatas: [tableMetadata]) {
 
         guard let tableAccount = self.getAccountActive() else {

+ 30 - 11
iOSClient/Favorites/CCFavorites.m

@@ -724,14 +724,33 @@
                             
             } else {
             
-                self.metadata.session = k_download_session;
-                self.metadata.sessionError = @"";
-                self.metadata.sessionSelector = selectorLoadFileView;
-                self.metadata.status = k_metadataStatusWaitDownload;
+                tableDirectory *tableDirectory = [[NCManageDatabase sharedInstance] getTableDirectoryWithPredicate:[NSPredicate predicateWithFormat:@"account == %@ AND serverUrl == %@", appDelegate.activeAccount, serverUrl]];
                 
-                // Add Metadata for Download
-                tableMetadata *metadata = [[NCManageDatabase sharedInstance] addMetadata:self.metadata];
-                [[CCNetworking sharedNetworking] downloadFile:metadata taskStatus:k_taskStatusResume delegate:self];
+                if (tableDirectory.e2eEncrypted && ![CCUtility isEndToEndEnabled:appDelegate.activeAccount]) {
+                    
+                    [appDelegate messageNotification:@"_info_" description:@"_e2e_goto_settings_for_enable_" visible:YES delay:k_dismissAfterSecond type:TWMessageBarMessageTypeInfo errorCode:0];
+                    
+                } else {
+                    
+                    if (([self.metadata.typeFile isEqualToString: k_metadataTypeFile_video] || [self.metadata.typeFile isEqualToString: k_metadataTypeFile_audio]) && self.metadata.e2eEncrypted == NO) {
+                        
+                        if ([self shouldPerformSegue])
+                            [self performSegueWithIdentifier:@"segueDetail" sender:self];
+                        
+                    } else {
+                        
+                        self.metadata.session = k_download_session;
+                        self.metadata.sessionError = @"";
+                        self.metadata.sessionSelector = selectorLoadFileView;
+                        self.metadata.status = k_metadataStatusWaitDownload;
+                        
+                        // Add Metadata for Download
+                        tableMetadata *metadata = [[NCManageDatabase sharedInstance] addMetadata:self.metadata];
+                        [[CCNetworking sharedNetworking] downloadFile:metadata taskStatus:k_taskStatusResume delegate:self];
+                        
+                        [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath, nil] withRowAnimation:UITableViewRowAnimationAutomatic];
+                    }
+                }
             }
         }
     }
@@ -787,17 +806,17 @@
         _detailViewController = segue.destinationViewController;
     }
     
-    NSMutableArray *allRecordsDataSourceImagesVideos = [NSMutableArray new];
+    NSMutableArray *photoDataSource = [NSMutableArray new];
     
     for (NSString *fileID in sectionDataSource.allFileID) {
         tableMetadata *metadata = [sectionDataSource.allRecordsDataSource objectForKey:fileID];
-        if ([metadata.typeFile isEqualToString: k_metadataTypeFile_image] || [metadata.typeFile isEqualToString: k_metadataTypeFile_video] || [metadata.typeFile isEqualToString: k_metadataTypeFile_audio])
-            [allRecordsDataSourceImagesVideos addObject:metadata];
+        if ([metadata.typeFile isEqualToString: k_metadataTypeFile_image])
+            [photoDataSource addObject:metadata];
     }
     
     _detailViewController.metadataDetail = self.metadata;
     _detailViewController.dateFilterQuery = nil;
-    _detailViewController.dataSourceImagesVideos = allRecordsDataSourceImagesVideos;
+    _detailViewController.photoDataSource = photoDataSource;
     
     [_detailViewController setTitle:self.metadata.fileNameView];
 }

+ 5 - 6
iOSClient/Main/CCDetail.h

@@ -38,12 +38,14 @@
 @property (nonatomic, strong) tableMetadata *metadataDetail;
 @property (nonatomic, strong) NSDate *dateFilterQuery;
 
+// Toolbar
+@property (nonatomic, strong) UIToolbar *toolbar;
+
 // Document
 @property (nonatomic, strong) WKWebView *webView;
 
-// Photo-Video
-@property (nonatomic, strong) NSMutableArray *dataSourceImagesVideos;
-
+// Photo
+@property (nonatomic, strong) NSMutableArray *photoDataSource;
 @property (nonatomic, strong) MWPhotoBrowser *photoBrowser;
 @property (nonatomic, strong) NSMutableArray *photos;
 
@@ -56,10 +58,7 @@
 
 @property(nonatomic, weak) IBOutlet UIImageView *imageBackground;
 
-- (void)removeAllView;
-
 - (void)changeToDisplayMode;
-
 - (void)downloadPhotoBrowserSuccessFailure:(tableMetadata *)metadata selector:(NSString *)selector errorCode:(NSInteger)errorCode;
 
 @end

+ 308 - 207
iOSClient/Main/CCDetail.m

@@ -25,28 +25,31 @@
 #import "AppDelegate.h"
 #import "CCMain.h"
 #import "NCUchardet.h"
+#import <KTVHTTPCache/KTVHTTPCache.h>
+
 #import "NCBridgeSwift.h"
 
 #define TOOLBAR_HEIGHT 49.0f
 
 #define alertRequestPasswordPDF 1
 
-@interface CCDetail ()
+@interface CCDetail () <NCTextDelegate>
 {
     AppDelegate *appDelegate;
+        
+    UIBarButtonItem *buttonModifyTxt;
+    UIBarButtonItem *buttonAction;
+    UIBarButtonItem *buttonShare;
+    UIBarButtonItem *buttonDelete;
     
-    UIToolbar *_toolbar;
-    
-    UIBarButtonItem *_buttonModifyTxt;
-    UIBarButtonItem *_buttonAction;
-    UIBarButtonItem *_buttonShare;
-    UIBarButtonItem *_buttonDelete;
+    NSInteger indexNowVisible;
+    NSString *fileIDNowVisible;
     
-    NSInteger _indexNowVisible;
-    NSString *_fileIDNowVisible;
+    NSMutableOrderedSet *dataSourceDirectoryID;
+    NSString *fileNameExtension;
     
-    NSMutableOrderedSet *_dataSourceDirectoryID;
-    NSString *_fileNameExtension;
+    NSURL *videoURLProxy;
+    NSURL *videoURL;
 }
 @end
 
@@ -64,14 +67,13 @@
 
         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(triggerProgressTask:) name:@"NotificationProgressTask" object:nil];
         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changeTheming) name:@"changeTheming" object:nil];
-        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(backNavigationController) name:@"detailBack" object:nil];
 
         self.metadataDetail = [[tableMetadata alloc] init];
         self.photos = [[NSMutableArray alloc] init];
-        self.dataSourceImagesVideos = [[NSMutableArray alloc] init];
-        _dataSourceDirectoryID = [[NSMutableOrderedSet alloc] init];
-        _indexNowVisible = -1;
-        _fileIDNowVisible = nil;
+        self.photoDataSource = [NSMutableArray new];
+        dataSourceDirectoryID = [[NSMutableOrderedSet alloc] init];
+        indexNowVisible = -1;
+        fileIDNowVisible = nil;
 
         appDelegate.activeDetail = self;
     }
@@ -90,9 +92,21 @@
 
     self.imageBackground.image = [UIImage imageNamed:@"backgroundDetail"];
     
-    // Change bar bottom line shadow
+    // Proxy
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        [self setupHTTPCache];
+    });
+    
+    // Change bar bottom line shadow and remove title back button <"title"
     self.navigationController.navigationBar.shadowImage = [CCGraphics generateSinglePixelImageWithColor:[NCBrandColor sharedInstance].brand];
+    self.navigationController.navigationBar.topItem.title = @"";
     
+    // TabBar
+    self.tabBarController.tabBar.hidden = YES;
+    self.tabBarController.tabBar.translucent = YES;
+    
+    // Open View
     if ([self.metadataDetail.fileNameView length] > 0 || [self.metadataDetail.directoryID length] > 0 || [self.metadataDetail.fileID length] > 0) {
     
         // open view
@@ -100,77 +114,37 @@
     }
 }
 
-// Apparirà
 - (void)viewWillAppear:(BOOL)animated
 {
     [super viewWillAppear:animated];
     
-    if (self.splitViewController.isCollapsed) {
-        
-        self.tabBarController.tabBar.hidden = YES;
-        self.tabBarController.tabBar.translucent = YES;
-    }
-    
-    if (self.splitViewController.isCollapsed)
-        [appDelegate plusButtonVisibile:false];
+    self.tabBarController.tabBar.hidden = YES;
+    self.tabBarController.tabBar.translucent = YES;
 }
 
-// E' scomparso
 - (void)viewDidDisappear:(BOOL)animated
 {
     [super viewDidDisappear:animated];
     
-    // remove all
-    if (self.isMovingFromParentViewController)
-        [self removeAllView];    
-}
-
-- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
-{
-    [coordinator animateAlongsideTransition:nil completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
-        
-    }];
-    
-    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
+    // If AVPlayer in play -> Stop
+    if (appDelegate.player != nil && appDelegate.player.rate != 0) {
+        [appDelegate.player pause];
+    }
 }
 
-// remove all view
-- (void)removeAllView
+- (void)changeTheming
 {
-    // Document
-    if (_webView) {
-        [_webView removeFromSuperview];
-        _webView = nil;
-    }
-        
-    // PDF
-    if (_readerPDFViewController) {
-        [_readerPDFViewController.view removeFromSuperview];
-        _readerPDFViewController.delegate = nil;
-        _readerPDFViewController = nil;
-    }
-        
-    // Photo-Video-Audio
-    if (_photoBrowser) {
-        [_photos removeAllObjects];
-        _photoBrowser.delegate = nil;
-        _photoBrowser = nil;
-    }
+    [appDelegate changeTheming:self];
     
-    // ToolBar
-    if (_toolbar) {
-        [_toolbar removeFromSuperview];
-        _toolbar = nil;
+    if (self.toolbar) {
+        self.toolbar.barTintColor = [NCBrandColor sharedInstance].tabBar;
+        self.toolbar.tintColor = [NCBrandColor sharedInstance].brandElement;
     }
-    
-    // Title
-    self.title = nil;
 }
 
 - (void)backNavigationController
 {
-    [self removeAllView];
-    [self.navigationController popViewControllerAnimated:NO];
+    [self.navigationController popViewControllerAnimated:YES];
 }
 
 - (void)changeToDisplayMode
@@ -179,58 +153,6 @@
         [self.readerPDFViewController updateContentViews];
 }
 
-- (void)createToolbar
-{
-    CGFloat safeAreaBottom = 0;
-    NSString *serverUrl = [[NCManageDatabase sharedInstance] getServerUrl:_metadataDetail.directoryID];
-    if (!serverUrl)
-        return;
-    
-    if (@available(iOS 11, *)) {
-        safeAreaBottom = [UIApplication sharedApplication].delegate.window.safeAreaInsets.bottom;
-    }
-    
-    _toolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, self.view.bounds.size.height - TOOLBAR_HEIGHT - safeAreaBottom, self.view.bounds.size.width, TOOLBAR_HEIGHT)];
-    
-    UIBarButtonItem *flexible = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil];
-    UIBarButtonItem *fixedSpaceMini = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:self action:nil];
-    fixedSpaceMini.width = 25;
-    
-    _buttonModifyTxt = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"actionSheetModify"] style:UIBarButtonItemStylePlain target:self action:@selector(modifyTxtButtonPressed:)];
-    _buttonAction = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"openFile"] style:UIBarButtonItemStylePlain target:self action:@selector(actionButtonPressed:)];
-    _buttonShare  = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"share"] style:UIBarButtonItemStylePlain target:self action:@selector(shareButtonPressed:)];
-    _buttonDelete = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemTrash target:self action:@selector(deleteButtonPressed:)];
-    
-    if ([CCUtility isDocumentModifiableExtension:_fileNameExtension]) {
-        if ([CCUtility isFolderEncrypted:serverUrl account:appDelegate.activeAccount]) // E2EE
-            [_toolbar setItems:[NSArray arrayWithObjects: _buttonModifyTxt, flexible, _buttonDelete, fixedSpaceMini, _buttonAction,  nil]];
-        else
-            [_toolbar setItems:[NSArray arrayWithObjects: _buttonModifyTxt, flexible, _buttonDelete, fixedSpaceMini, _buttonShare, fixedSpaceMini, _buttonAction,  nil]];
-    } else {
-        if ([CCUtility isFolderEncrypted:serverUrl account:appDelegate.activeAccount]) // E2EE
-            [_toolbar setItems:[NSArray arrayWithObjects: flexible, _buttonDelete, fixedSpaceMini, _buttonAction,  nil]];
-        else
-            [_toolbar setItems:[NSArray arrayWithObjects: flexible, _buttonDelete, fixedSpaceMini, _buttonShare, fixedSpaceMini, _buttonAction,  nil]];
-    }
-    
-    [_toolbar setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin];
-    
-    _toolbar.barTintColor = [NCBrandColor sharedInstance].tabBar;
-    _toolbar.tintColor = [NCBrandColor sharedInstance].brandElement;
-
-    [self.view addSubview:_toolbar];
-}
-
-- (void)changeTheming
-{
-    [appDelegate changeTheming:self];
-    
-    if (_toolbar) {
-        _toolbar.barTintColor = [NCBrandColor sharedInstance].tabBar;
-        _toolbar.tintColor = [NCBrandColor sharedInstance].brandElement;
-    }    
-}
-
 #pragma --------------------------------------------------------------------------------------------
 #pragma mark ===== View File  =====
 #pragma --------------------------------------------------------------------------------------------
@@ -243,31 +165,90 @@
         [CCGraphics createNewImageFrom:self.metadataDetail.fileNameView fileID:self.metadataDetail.fileID extension:[self.metadataDetail.fileNameView pathExtension] size:@"m" imageForUpload:NO typeFile:self.metadataDetail.typeFile writeImage:YES optimizedFileName:[CCUtility getOptimizedPhoto]];
     }
     
-    if ([self.metadataDetail.typeFile isEqualToString: k_metadataTypeFile_image] || [self.metadataDetail.typeFile isEqualToString: k_metadataTypeFile_video] || [self.metadataDetail.typeFile isEqualToString: k_metadataTypeFile_audio]) {
+    // IMAGE
+    if ([self.metadataDetail.typeFile isEqualToString: k_metadataTypeFile_image]) {
+        
+        self.edgesForExtendedLayout = UIRectEdgeAll;
+        [self viewImage];
+    }
+    
+    // AUDIO VIDEO
+    if ([self.metadataDetail.typeFile isEqualToString: k_metadataTypeFile_video] || [self.metadataDetail.typeFile isEqualToString: k_metadataTypeFile_audio]) {
         
         self.edgesForExtendedLayout = UIRectEdgeAll;
-        [self viewImageVideoAudio];
+        [self createToolbar];
+        [self viewMedia];
+        [appDelegate aspectNavigationControllerBar:self.navigationController.navigationBar online:[appDelegate.reachability isReachable] hidden:NO];
     }
     
+    // DOCUMENT
     if ([self.metadataDetail.typeFile isEqualToString: k_metadataTypeFile_document]) {
         
-        _fileNameExtension = [[self.metadataDetail.fileNameView pathExtension] uppercaseString];
+        fileNameExtension = [[self.metadataDetail.fileNameView pathExtension] uppercaseString];
         
-        if ([_fileNameExtension isEqualToString:@"PDF"]) {
+        if ([fileNameExtension isEqualToString:@"PDF"]) {
             
             self.edgesForExtendedLayout = UIRectEdgeBottom;
-            [self viewPDF:@""];
             [self createToolbar];
+            [self viewPDF:@""];
             [appDelegate aspectNavigationControllerBar:self.navigationController.navigationBar online:[appDelegate.reachability isReachable] hidden:NO];
-
+            
         } else {
-
+            
             self.edgesForExtendedLayout = UIRectEdgeBottom;
-            [self viewDocument];
             [self createToolbar];
+            [self viewDocument];
             [appDelegate aspectNavigationControllerBar:self.navigationController.navigationBar online:[appDelegate.reachability isReachable] hidden:NO];
         }
     }
+    
+    self.title = _metadataDetail.fileNameView;
+}
+
+#pragma --------------------------------------------------------------------------------------------
+#pragma mark ===== Toolbar  =====
+#pragma --------------------------------------------------------------------------------------------
+
+- (void)createToolbar
+{
+    CGFloat safeAreaBottom = 0;
+    NSString *serverUrl = [[NCManageDatabase sharedInstance] getServerUrl:_metadataDetail.directoryID];
+    if (!serverUrl)
+        return;
+    
+    if (@available(iOS 11, *)) {
+        safeAreaBottom = [UIApplication sharedApplication].delegate.window.safeAreaInsets.bottom;
+    }
+    
+    self.toolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, self.view.bounds.size.height - TOOLBAR_HEIGHT - safeAreaBottom, self.view.bounds.size.width, TOOLBAR_HEIGHT)];
+    
+    UIBarButtonItem *flexible = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil];
+    UIBarButtonItem *fixedSpaceMini = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:self action:nil];
+    fixedSpaceMini.width = 25;
+    
+    buttonModifyTxt = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"actionSheetModify"] style:UIBarButtonItemStylePlain target:self action:@selector(modifyTxtButtonPressed:)];
+    buttonAction = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"openFile"] style:UIBarButtonItemStylePlain target:self action:@selector(actionButtonPressed:)];
+    buttonShare  = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"share"] style:UIBarButtonItemStylePlain target:self action:@selector(shareButtonPressed:)];
+    buttonDelete = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemTrash target:self action:@selector(deleteButtonPressed:)];
+    
+    if ([CCUtility isDocumentModifiableExtension:fileNameExtension]) {
+        if ([CCUtility isFolderEncrypted:serverUrl account:appDelegate.activeAccount]) // E2EE
+            [self.toolbar setItems:[NSArray arrayWithObjects: buttonModifyTxt, flexible, buttonDelete, fixedSpaceMini, buttonAction,  nil]];
+        else
+            [self.toolbar setItems:[NSArray arrayWithObjects: buttonModifyTxt, flexible, buttonDelete, fixedSpaceMini, buttonShare, fixedSpaceMini, buttonAction,  nil]];
+    } else {
+        if ([CCUtility isFolderEncrypted:serverUrl account:appDelegate.activeAccount]) // E2EE
+            [self.toolbar setItems:[NSArray arrayWithObjects: flexible, buttonDelete, fixedSpaceMini, buttonAction,  nil]];
+        else
+            [self.toolbar setItems:[NSArray arrayWithObjects: flexible, buttonDelete, fixedSpaceMini, buttonShare, fixedSpaceMini, buttonAction,  nil]];
+    }
+    
+    [self.toolbar setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin];
+    
+    self.toolbar.barTintColor = [NCBrandColor sharedInstance].tabBar;
+    self.toolbar.tintColor = [NCBrandColor sharedInstance].brandElement;
+
+    [self.view addSubview:self.toolbar];
 }
 
 #pragma --------------------------------------------------------------------------------------------
@@ -306,7 +287,7 @@
     [self.webView setBackgroundColor:[NCBrandColor sharedInstance].backgroundView];
     [self.webView setOpaque:NO];
     
-    if ( [_fileNameExtension isEqualToString:@"CSS"] || [_fileNameExtension isEqualToString:@"PY"] || [_fileNameExtension isEqualToString:@"XML"] || [_fileNameExtension isEqualToString:@"JS"] ) {
+    if ( [fileNameExtension isEqualToString:@"CSS"] || [fileNameExtension isEqualToString:@"PY"] || [fileNameExtension isEqualToString:@"XML"] || [fileNameExtension isEqualToString:@"JS"] ) {
         
         NSString *dataFile = [[NSString alloc] initWithData:[NSData dataWithContentsOfURL:url] encoding:NSASCIIStringEncoding];
         
@@ -316,7 +297,7 @@
             [self.webView  loadHTMLString:[NSString stringWithFormat:@"<div style='font-size:%@;font-family:%@;'><pre>%@",@"20",@"Sans-Serif",dataFile] baseURL:nil];
         }
         
-    } else if ([CCUtility isDocumentModifiableExtension:_fileNameExtension]) {
+    } else if ([CCUtility isDocumentModifiableExtension:fileNameExtension]) {
         
         NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
         NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:nil delegateQueue:nil];
@@ -341,21 +322,138 @@
     [self.view addSubview:self.webView];
 }
 
+#pragma --------------------------------------------------------------------------------------------
+#pragma mark ===== View Media =====
+#pragma --------------------------------------------------------------------------------------------
+
+- (void)viewMedia
+{
+    CGFloat safeAreaBottom = 0;
+    
+    if (@available(iOS 11, *)) {
+        safeAreaBottom = [UIApplication sharedApplication].delegate.window.safeAreaInsets.bottom;
+    }
+    
+    NSString *serverUrl = [[NCManageDatabase sharedInstance] getServerUrl:_metadataDetail.directoryID];
+    if (!serverUrl)
+        return;
+    
+    if ([CCUtility fileProviderStorageExists:self.metadataDetail.fileID fileName:self.metadataDetail.fileNameView]) {
+    
+        videoURL = [NSURL fileURLWithPath:[CCUtility getDirectoryProviderStorageFileID:self.metadataDetail.fileID fileName:self.metadataDetail.fileNameView]];
+        videoURLProxy = videoURL;
+        
+    } else {
+    
+        videoURL = [NSURL URLWithString:[[NSString stringWithFormat:@"%@/%@", serverUrl, _metadataDetail.fileName] stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]];
+        videoURLProxy = [KTVHTTPCache proxyURLWithOriginalURL:videoURL];
+
+        NSMutableDictionary *header = [NSMutableDictionary new];
+        NSData *authData = [[NSString stringWithFormat:@"%@:%@", appDelegate.activeUser, appDelegate.activePassword] dataUsingEncoding:NSUTF8StringEncoding];
+        NSString *authValue = [NSString stringWithFormat: @"Basic %@",[authData base64EncodedStringWithOptions:0]];
+        [header setValue:authValue forKey:@"Authorization"];
+        [header setValue:[CCUtility getUserAgent] forKey:@"User-Agent"];        
+        [KTVHTTPCache downloadSetAdditionalHeaders:header];
+        
+        // Disable Button Action (the file is in download via Proxy Server)
+        buttonAction.enabled = false;
+    }
+    
+    appDelegate.player = [AVPlayer playerWithURL:videoURLProxy];
+    appDelegate.playerController = [AVPlayerViewController new];
+
+    appDelegate.playerController.player = appDelegate.player;
+    appDelegate.playerController.view.frame = CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height - TOOLBAR_HEIGHT - safeAreaBottom);
+    appDelegate.playerController.allowsPictureInPicturePlayback = false;
+    [self addChildViewController:appDelegate.playerController];
+    [self.view addSubview:appDelegate.playerController.view];
+    [appDelegate.playerController didMoveToParentViewController:self];
+    
+    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(itemDidFinishPlaying:) name:AVPlayerItemDidPlayToEndTimeNotification object:[appDelegate.player currentItem]];
+    [appDelegate.player addObserver:self forKeyPath:@"rate" options:0 context:nil];
+
+    [appDelegate.player play];
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
+    if ([keyPath isEqualToString:@"rate"]) {
+        if ([appDelegate.player rate]) {
+            NSLog(@"start");
+        }
+        else {
+            NSLog(@"pause");
+        }
+        [self saveCacheToFileProvider];
+    }
+}
+
+- (void)itemDidFinishPlaying:(NSNotification *)notification
+{
+    AVPlayerItem *player = [notification object];
+    [player seekToTime:kCMTimeZero];    
+}
+
+- (void)saveCacheToFileProvider
+{
+    if (![CCUtility fileProviderStorageExists:self.metadataDetail.fileID fileName:self.metadataDetail.fileNameView]) {
+        NSURL *url = [KTVHTTPCache cacheCompleteFileURLIfExistedWithURL:videoURL];
+        if (url) {
+            
+            [CCUtility copyFileAtPath:[url path] toPath:[CCUtility getDirectoryProviderStorageFileID:self.metadataDetail.fileID fileName:self.metadataDetail.fileNameView]];
+            [[NCManageDatabase sharedInstance] addLocalFileWithMetadata:self.metadataDetail];
+            [KTVHTTPCache cacheDeleteCacheWithURL:videoURL];
+            
+            // reload Main
+            [appDelegate.activeMain reloadDatasource];
+            
+            // Enabled Button Action (the file is in local)
+            buttonAction.enabled = true;
+        }
+    }
+}
+
+- (void)setupHTTPCache
+{
+    [KTVHTTPCache cacheSetMaxCacheLength:k_maxHTTPCache];
+    
+#if TARGET_IPHONE_SIMULATOR
+    [KTVHTTPCache logSetConsoleLogEnable:YES];
+#endif
+    
+    NSError * error;
+    [KTVHTTPCache proxyStart:&error];
+    if (error) {
+        NSLog(@"Proxy Start Failure, %@", error);
+    } else {
+        NSLog(@"Proxy Start Success");
+    }
+    
+    [KTVHTTPCache tokenSetURLFilter:^NSURL * (NSURL * URL) {
+        NSLog(@"URL Filter reviced URL : %@", URL);
+        return URL;
+    }];
+    
+    [KTVHTTPCache downloadSetUnsupportContentTypeFilter:^BOOL(NSURL * URL, NSString * contentType) {
+        NSLog(@"Unsupport Content-Type Filter reviced URL : %@, %@", URL, contentType);
+        return NO;
+    }];
+}
+
 #pragma --------------------------------------------------------------------------------------------
 #pragma mark ===== View Image =====
 #pragma --------------------------------------------------------------------------------------------
 
-- (void)viewImageVideoAudio
+- (void)viewImage
 {
     self.photoBrowser = [[MWPhotoBrowser alloc] initWithDelegate:self];
-    _indexNowVisible = -1;
-    _fileIDNowVisible = nil;
+    indexNowVisible = -1;
+    fileIDNowVisible = nil;
     
     [self.photos removeAllObjects];
-    [_dataSourceDirectoryID removeAllObjects];
+    [dataSourceDirectoryID removeAllObjects];
     
     // if not images, exit
-    if ([self.dataSourceImagesVideos count] == 0)
+    if ([self.photoDataSource count] == 0)
         return;
     
     // test
@@ -364,7 +462,7 @@
         return;
     
     NSUInteger index = 0;
-    for (tableMetadata *metadata in self.dataSourceImagesVideos) {
+    for (tableMetadata *metadata in self.photoDataSource) {
         
         // start from here ?
         if (self.metadataDetail.fileID && [metadata.fileID isEqualToString:self.metadataDetail.fileID])
@@ -373,7 +471,7 @@
         [self.photos addObject:[MWPhoto photoWithImage:nil]];
         
         // add directory
-        [_dataSourceDirectoryID addObject:metadata.directoryID];
+        [dataSourceDirectoryID addObject:metadata.directoryID];
         index++;
     }
     
@@ -408,12 +506,12 @@
 
 - (NSUInteger)numberOfPhotosInPhotoBrowser:(MWPhotoBrowser *)photoBrowser
 {
-    return [self.dataSourceImagesVideos count];
+    return [self.photoDataSource count];
 }
 
 - (NSString *)photoBrowser:(MWPhotoBrowser *)photoBrowser titleForPhotoAtIndex:(NSUInteger)index
 {
-    tableMetadata *metadata = [self.dataSourceImagesVideos objectAtIndex:index];
+    tableMetadata *metadata = [self.photoDataSource objectAtIndex:index];
     
     NSString *titleDir = metadata.fileNameView;
     self.title = titleDir;
@@ -423,10 +521,10 @@
 
 - (void)photoBrowser:(MWPhotoBrowser *)photoBrowser didDisplayPhotoAtIndex:(NSUInteger)index
 {
-    tableMetadata *metadata = [self.dataSourceImagesVideos objectAtIndex:index];
+    tableMetadata *metadata = [self.photoDataSource objectAtIndex:index];
     
-    _indexNowVisible = index;
-    _fileIDNowVisible = metadata.fileID;
+    indexNowVisible = index;
+    fileIDNowVisible = metadata.fileID;
     
     photoBrowser.toolbar.hidden = NO;
     
@@ -451,7 +549,7 @@
     UIImage *image;
 //    UIImage *loadingGIF = [UIImage animatedImageWithAnimatedGIFURL:[[NSBundle mainBundle] URLForResource:@"loading" withExtension:@"gif"]];
 
-    tableMetadata *metadata = [self.dataSourceImagesVideos objectAtIndex:index];
+    tableMetadata *metadata = [self.photoDataSource objectAtIndex:index];
     
     if (index < self.photos.count) {
         
@@ -576,7 +674,7 @@
 
 - (void)photoBrowser:(MWPhotoBrowser *)photoBrowser actionButtonPressedForPhotoAtIndex:(NSUInteger)index
 {
-    tableMetadata *metadata = [self.dataSourceImagesVideos objectAtIndex:index];
+    tableMetadata *metadata = [self.photoDataSource objectAtIndex:index];
     if (metadata == nil) return;
 
     self.docController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:[CCUtility getDirectoryProviderStorageFileID:metadata.fileID fileName:metadata.fileNameView]]];
@@ -591,14 +689,14 @@
 
 - (void)photoBrowser:(MWPhotoBrowser *)photoBrowser shareButtonPressedForPhotoAtIndex:(NSUInteger)index
 {
-    tableMetadata *metadata = [self.dataSourceImagesVideos objectAtIndex:index];
+    tableMetadata *metadata = [self.photoDataSource objectAtIndex:index];
     
     [appDelegate.activeMain openWindowShare:metadata];
 }
 
 - (void)photoBrowser:(MWPhotoBrowser *)photoBrowser deleteButtonPressedForPhotoAtIndex:(NSUInteger)index deleteButton:(UIBarButtonItem *)deleteButton
 {
-    tableMetadata *metadata = [self.dataSourceImagesVideos objectAtIndex:index];
+    tableMetadata *metadata = [self.photoDataSource objectAtIndex:index];
     if (metadata == nil || [CCUtility fileProviderStorageExists:metadata.fileID fileName:metadata.fileNameView] == NO) {
         
         [appDelegate messageNotification:@"_info_" description:@"_file_not_found_" visible:YES delay:k_dismissAfterSecond type:TWMessageBarMessageTypeInfo errorCode:0];
@@ -629,12 +727,12 @@
 
 - (void)photoBrowserDidFinishPresentation:(MWPhotoBrowser *)photoBrowser
 {
-    [self removeAllView];
     [self.navigationController popViewControllerAnimated:YES];
 }
 
 - (void)triggerProgressTask:(NSNotification *)notification
 {
+    /*
     NSDictionary *dict = notification.userInfo;
     NSString *fileID = [dict valueForKey:@"fileID"];
     //NSString *serverUrl = [dict valueForKey:@"serverUrl"];
@@ -643,15 +741,20 @@
     //long long totalBytes = [[dict valueForKey:@"totalBytes"] longLongValue];
     //long long totalBytesExpected = [[dict valueForKey:@"totalBytesExpected"] longLongValue];
     
-//    if ([fileID isEqualToString:_fileIDNowVisible])
-//        [_hud progress:progress];
+    if ([fileID isEqualToString:_fileIDNowVisible])
+        [_hud progress:progress];
+    */
 }
 
 - (void)downloadPhotoBrowserSuccessFailure:(tableMetadata *)metadata selector:(NSString *)selector errorCode:(NSInteger)errorCode
 {
     // if a message for a directory of these
-    if (![metadata.fileID isEqualToString:_fileIDNowVisible])
+    if (![metadata.fileID isEqualToString:fileIDNowVisible])
         return;
+ 
+    // Title
+    self.navigationItem.titleView = nil;
+    self.title = metadata.fileNameView;
     
     if (errorCode == 0) {
         // verifico se esiste l'icona e se la posso creare
@@ -669,19 +772,18 @@
 
 - (void)downloadPhotoBrowser:(tableMetadata *)metadata
 {
-    NSString *serverUrl = [[NCManageDatabase sharedInstance] getServerUrl:metadata.directoryID];
+    tableMetadata *metadataForUpload = [[NCManageDatabase sharedInstance] initNewMetadata:metadata];
     
-    if (serverUrl) {
+    metadataForUpload.session = k_download_session;
+    metadataForUpload.sessionError = @"";
+    metadataForUpload.sessionSelector = selectorLoadViewImage;
+    metadataForUpload.status = k_metadataStatusWaitDownload;
         
-        metadata.session = k_download_session;
-        metadata.sessionError = @"";
-        metadata.sessionSelector = selectorLoadViewImage;
-        metadata.status = k_metadataStatusWaitDownload;
-        
-        // Add Metadata for Download
-        (void)[[NCManageDatabase sharedInstance] addMetadata:metadata];
-        [appDelegate performSelectorOnMainThread:@selector(loadAutoDownloadUpload) withObject:nil waitUntilDone:YES];
-    }
+    // Add Metadata for Download
+    (void)[[NCManageDatabase sharedInstance] addMetadata:metadataForUpload];
+    [appDelegate performSelectorOnMainThread:@selector(loadAutoDownloadUpload) withObject:nil waitUntilDone:YES];
+    
+    [CCGraphics addImageToTitle:NSLocalizedString(@"_...loading..._", nil) colorTitle:[NCBrandColor sharedInstance].brandText imageTitle:[CCGraphics changeThemingColorImage:[UIImage imageNamed:@"load"] multiplier:2 color:[NCBrandColor sharedInstance].brandText] navigationItem:self.navigationItem];
 }
 
 - (void)insertGeocoderLocation:(NSNotification *)notification
@@ -693,12 +795,12 @@
     //NSDate *date = [[notification.userInfo allValues] objectAtIndex:0];
  
     // test [Chrash V 1.14,15]
-    if (_indexNowVisible >= [self.photos count])
+    if (indexNowVisible >= [self.photos count])
         return;
     
-    if ([fileID isEqualToString:_fileIDNowVisible]) {
+    if ([fileID isEqualToString:fileIDNowVisible]) {
             
-        MWPhoto *photo = [self.photos objectAtIndex:_indexNowVisible];
+        MWPhoto *photo = [self.photos objectAtIndex:indexNowVisible];
             
         [self setLocationCaptionPhoto:photo fileID:fileID];
         
@@ -873,9 +975,9 @@
     }
     
     self.navigationController.navigationBarHidden = !self.navigationController.navigationBarHidden;
-    _toolbar.hidden = !_toolbar.isHidden;
+    self.toolbar.hidden = !self.toolbar.isHidden;
     
-    if (_toolbar.isHidden) {
+    if (self.toolbar.isHidden) {
         self.readerPDFViewController.view.frame = CGRectMake(0, safeAreaTop, self.view.bounds.size.width, self.view.bounds.size.height - safeAreaTop - safeAreaBottom);
     } else {
         self.readerPDFViewController.view.frame = CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height - TOOLBAR_HEIGHT - safeAreaBottom);
@@ -910,46 +1012,27 @@
             // reload Main
             [appDelegate.activeMain reloadDatasource];
             
-            // If removed document (web) or PDF close
-            if (_webView || _readerPDFViewController)
-                [self removeAllView];
+            // Not image
+            if ([self.metadataDetail.typeFile isEqualToString: k_metadataTypeFile_image] == NO) {
             
-            // if a message for a directory of these
-            if (![_dataSourceDirectoryID containsObject:metadata.directoryID])
-                return;
+                // exit
+                [self backNavigationController];
             
-            // if we are not in browserPhoto and it's removed photo/video in preview then "< Back"
-            if (!self.photoBrowser && [self.metadataDetail.fileID isEqualToString:metadata.fileID]) {
-                
-                NSArray *viewsToRemove = [self.view subviews];
-                for (id element in viewsToRemove) {
-                    
-                    if ([element isMemberOfClass:[UIView class]] || [element isMemberOfClass:[UIToolbar class]])
-                        [element removeFromSuperview];
-                }
-                
-                self.title = @"";
-                
-                [self.navigationController popViewControllerAnimated:YES];
-                
             } else {
                 
-                // only photoBrowser if exists
-                for (NSUInteger index=0; index < [self.dataSourceImagesVideos count] && _photoBrowser; index++ ) {
+                for (NSUInteger index=0; index < [self.photoDataSource count] && _photoBrowser; index++ ) {
                     
-                    tableMetadata *metadataTemp = [self.dataSourceImagesVideos objectAtIndex:index];
+                    tableMetadata *metadataTemp = [self.photoDataSource objectAtIndex:index];
                     
                     if ([metadata isInvalidated] || [metadataTemp.fileID isEqualToString:metadata.fileID]) {
                         
-                        [self.dataSourceImagesVideos removeObjectAtIndex:index];
+                        [self.photoDataSource removeObjectAtIndex:index];
                         [self.photos removeObjectAtIndex:index];
                         [self.photoBrowser reloadData];
                         
-                        // Title
-                        if ([self.dataSourceImagesVideos count] == 0) {
-                            
-                            self.title = @"";
-                            [self.navigationController popViewControllerAnimated:YES];
+                        // exit
+                        if ([self.photoDataSource count] == 0) {
+                            [self backNavigationController];
                         }
                     }
                 }
@@ -964,18 +1047,36 @@
 #pragma mark ===== ButtonPressed =====
 #pragma --------------------------------------------------------------------------------------------
 
+- (void)dismissTextView
+{
+    if (self.webView) {
+        
+        NSString *fileNamePath = [NSTemporaryDirectory() stringByAppendingString:self.metadataDetail.fileNameView];
+        
+        [[NSFileManager defaultManager] removeItemAtPath:fileNamePath error:nil];
+        [[NSFileManager defaultManager] linkItemAtPath:[CCUtility getDirectoryProviderStorageFileID:self.metadataDetail.fileID fileName:self.metadataDetail.fileNameView] toPath:fileNamePath error:nil];
+        
+        [self.webView reload];
+    }
+}
+
 - (void)modifyTxtButtonPressed:(UIBarButtonItem *)sender
 {
-    UINavigationController* navigationController = [[UIStoryboard storyboardWithName:@"NCText" bundle:nil] instantiateViewControllerWithIdentifier:@"NCText"];
-    
-    NCText *viewController = (NCText *)navigationController.topViewController;
-    
-    viewController.metadata = self.metadataDetail;
-    
-    navigationController.modalPresentationStyle = UIModalPresentationPageSheet;
-    navigationController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
-    
-    [self presentViewController:navigationController animated:YES completion:nil];
+    tableMetadata *metadata = [[NCManageDatabase sharedInstance] getMetadataWithPredicate:[NSPredicate predicateWithFormat:@"fileID == %@", self.metadataDetail.fileID]];
+    if (metadata) {
+        
+        UINavigationController* navigationController = [[UIStoryboard storyboardWithName:@"NCText" bundle:nil] instantiateViewControllerWithIdentifier:@"NCText"];
+        
+        NCText *viewController = (NCText *)navigationController.topViewController;
+        
+        viewController.metadata = metadata;
+        viewController.delegate = self;
+        
+        navigationController.modalPresentationStyle = UIModalPresentationPageSheet;
+        navigationController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
+        
+        [self presentViewController:navigationController animated:YES completion:nil];
+    }
 }
 
 - (void)actionButtonPressed:(UIBarButtonItem *)sender
@@ -1017,7 +1118,7 @@
                                                            [alertController dismissViewControllerAnimated:YES completion:nil];
                                                        }]];
     
-    alertController.popoverPresentationController.barButtonItem = _buttonDelete;
+    alertController.popoverPresentationController.barButtonItem = buttonDelete;
     
     if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
         [alertController.view layoutIfNeeded];

+ 35 - 18
iOSClient/Main/CCMain.m

@@ -374,8 +374,17 @@
         // Setting Theming
         [appDelegate settingThemingColorBrand];
         
-        // remove all of detail
-        [appDelegate.activeDetail removeAllView];
+        // Detail
+        // If AVPlayer in play -> Stop
+        if (appDelegate.player != nil && appDelegate.player.rate != 0) {
+            [appDelegate.player pause];
+        }
+        for (UIView *view in [appDelegate.activeDetail.view subviews]) {
+            if ([view isKindOfClass:[UIImageView class]] == NO) { // View Image Nextcloud
+                [view removeFromSuperview];
+            }
+        }
+        appDelegate.activeDetail.title = nil;
         
         // remove all Notification Messages
         [appDelegate.listOfNotifications removeAllObjects];
@@ -4383,17 +4392,24 @@
                 
             } else {
             
-                _metadata.session = k_download_session;
-                _metadata.sessionError = @"";
-                _metadata.sessionSelector = selectorLoadFileView;
-                _metadata.status = k_metadataStatusWaitDownload;
-                
-                // Add Metadata for Download
-                (void)[[NCManageDatabase sharedInstance] addMetadata:_metadata];
-                [appDelegate performSelectorOnMainThread:@selector(loadAutoDownloadUpload) withObject:nil waitUntilDone:YES];
-                            
-                NSIndexPath *indexPath = [sectionDataSource.fileIDIndexPath objectForKey:_metadata.fileID];
-                if (indexPath) [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath, nil] withRowAnimation:UITableViewRowAnimationAutomatic];
+                if (([_metadata.typeFile isEqualToString: k_metadataTypeFile_video] || [_metadata.typeFile isEqualToString: k_metadataTypeFile_audio]) && _metadataFolder.e2eEncrypted == NO) {
+                    
+                    if ([self shouldPerformSegue])
+                        [self performSegueWithIdentifier:@"segueDetail" sender:self];
+                    
+                } else {
+                   
+                    _metadata.session = k_download_session;
+                    _metadata.sessionError = @"";
+                    _metadata.sessionSelector = selectorLoadFileView;
+                    _metadata.status = k_metadataStatusWaitDownload;
+                    
+                    // Add Metadata for Download
+                    (void)[[NCManageDatabase sharedInstance] addMetadata:_metadata];
+                    [appDelegate performSelectorOnMainThread:@selector(loadAutoDownloadUpload) withObject:nil waitUntilDone:YES];
+                    
+                    [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath, nil] withRowAnimation:UITableViewRowAnimationAutomatic];
+                }
             }
         }
     }
@@ -4470,7 +4486,6 @@
 - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
 {
     id viewController = segue.destinationViewController;
-    NSMutableArray *allRecordsDataSourceImagesVideos = [NSMutableArray new];
     tableMetadata *metadata;
     
     if ([viewController isKindOfClass:[UINavigationController class]]) {
@@ -4483,10 +4498,12 @@
         _detailViewController = segue.destinationViewController;
     }
     
+    NSMutableArray *photoDataSource = [NSMutableArray new];
+    
     if ([sender isKindOfClass:[tableMetadata class]]) {
     
         metadata = sender;
-        [allRecordsDataSourceImagesVideos addObject:sender];
+        [photoDataSource addObject:sender];
         
     } else {
         
@@ -4494,13 +4511,13 @@
         
         for (NSString *fileID in sectionDataSource.allFileID) {
             tableMetadata *metadata = [sectionDataSource.allRecordsDataSource objectForKey:fileID];
-            if ([metadata.typeFile isEqualToString: k_metadataTypeFile_image] || [metadata.typeFile isEqualToString: k_metadataTypeFile_video] || [metadata.typeFile isEqualToString: k_metadataTypeFile_audio])
-                [allRecordsDataSourceImagesVideos addObject:metadata];
+            if ([metadata.typeFile isEqualToString: k_metadataTypeFile_image])
+                [photoDataSource addObject:metadata];
         }
     }
     
     _detailViewController.metadataDetail = metadata;
-    _detailViewController.dataSourceImagesVideos = allRecordsDataSourceImagesVideos;
+    _detailViewController.photoDataSource = photoDataSource;
     _detailViewController.dateFilterQuery = nil;
     
     [_detailViewController setTitle:metadata.fileNameView];

+ 5 - 3
iOSClient/Photos/CCPhotos.m

@@ -812,13 +812,15 @@
         self.detailViewController = segue.destinationViewController;
     }
     
-    NSMutableArray *allRecordsDataSourceImagesVideos = [NSMutableArray new];
+    NSMutableArray *photoDataSource = [NSMutableArray new];
+    
     for (NSString *fileID in sectionDataSource.allFileID) {
         tableMetadata *metadata = [sectionDataSource.allRecordsDataSource objectForKey:fileID];
-        [allRecordsDataSourceImagesVideos addObject:metadata];
+        if ([metadata.typeFile isEqualToString: k_metadataTypeFile_image])
+            [photoDataSource addObject:metadata];
     }
     
-    self.detailViewController.dataSourceImagesVideos = allRecordsDataSourceImagesVideos;
+    self.detailViewController.photoDataSource = photoDataSource;
     self.detailViewController.metadataDetail = self.metadata;
     self.detailViewController.dateFilterQuery = self.metadata.date;
     

+ 9 - 1
iOSClient/Settings/Acknowledgements.rtf

@@ -1,4 +1,4 @@
-{\rtf1\ansi\ansicpg1252\cocoartf1561\cocoasubrtf400
+{\rtf1\ansi\ansicpg1252\cocoartf1561\cocoasubrtf600
 {\fonttbl\f0\fswiss\fcharset0 Helvetica;}
 {\colortbl;\red255\green255\blue255;}
 {\*\expandedcolortbl;;}
@@ -238,4 +238,12 @@ Apache License, Version 2.0\
 Copyright (c) Pavel Katunin\
 ____________________________________________\
 \
+
+\b KTVHTTPCache
+\b0 \
+\
+The MIT License (MIT)\
+\
+Copyright (c) libobjc\
+____________________________________________\
 }

+ 3 - 0
iOSClient/Settings/CCAdvanced.m

@@ -24,6 +24,7 @@
 #import "CCAdvanced.h"
 #import "CCUtility.h"
 #import "AppDelegate.h"
+#import <KTVHTTPCache/KTVHTTPCache.h>
 #import "NCBridgeSwift.h"
 
 @interface CCAdvanced ()
@@ -368,6 +369,8 @@
         for (NSString *file in tmpDirectory)
             [[NSFileManager defaultManager] removeItemAtPath:[NSString stringWithFormat:@"%@%@", NSTemporaryDirectory(), file] error:NULL];
         
+        [KTVHTTPCache cacheDeleteAllCaches];
+        
         [self recalculateSize];
         
         // Clear Database

BIN
iOSClient/Supporting Files/pt-PT.lproj/Localizable.strings


+ 16 - 5
iOSClient/Text/NCText.swift

@@ -24,6 +24,10 @@
 
 import Foundation
 
+@objc protocol NCTextDelegate {
+    func dismissTextView()
+}
+
 class NCText: UIViewController, UITextViewDelegate {
 
     @IBOutlet weak var cancelButton: UIBarButtonItem!
@@ -35,6 +39,7 @@ class NCText: UIViewController, UITextViewDelegate {
     let appDelegate = UIApplication.shared.delegate as! AppDelegate
     
     @objc var metadata: tableMetadata?
+    @objc var delegate: NCTextDelegate?
     var loadText: String?
     
     override func viewDidLoad() {
@@ -124,7 +129,9 @@ class NCText: UIViewController, UITextViewDelegate {
             let alertController = UIAlertController(title: NSLocalizedString("_info_", comment: ""), message: NSLocalizedString("_save_exit_", comment: ""), preferredStyle: .alert)
             
             let actionYes = UIAlertAction(title: NSLocalizedString("_yes_", comment: ""), style: .default) { (action:UIAlertAction) in
-                self.dismiss(animated: true, completion: nil)
+                self.dismiss(animated: true, completion: {
+                    self.delegate?.dismissTextView()
+                })
             }
             
             let actionNo = UIAlertAction(title: NSLocalizedString("_no_", comment: ""), style: .cancel) { (action:UIAlertAction) in
@@ -138,7 +145,9 @@ class NCText: UIViewController, UITextViewDelegate {
             
         } else {
             
-            self.dismiss(animated: true, completion: nil)
+            self.dismiss(animated: true, completion: {
+                self.delegate?.dismissTextView()
+            })
         }
     }
     
@@ -164,8 +173,8 @@ class NCText: UIViewController, UITextViewDelegate {
 
                         _ = NCManageDatabase.sharedInstance.addMetadata(metadata)
                         self.appDelegate.perform(#selector(self.appDelegate.loadAutoDownloadUpload), on: Thread.main, with: nil, waitUntilDone: true)
-
-                        NotificationCenter.default.post(name: NSNotification.Name(rawValue: "detailBack"), object: nil)
+                        
+                        self.delegate?.dismissTextView()
                     })
 
                 } else {
@@ -173,7 +182,9 @@ class NCText: UIViewController, UITextViewDelegate {
                 }
                 
             } else {
-                self.dismiss(animated: true, completion: nil)
+                self.dismiss(animated: true, completion: {
+                    self.delegate?.dismissTextView()
+                })
             }
             
         } else {