Эх сурвалжийг харах

Merge pull request #1735 from nextcloud/RemoteCommandCenter

Remote command center
Marino Faggiana 3 жил өмнө
parent
commit
e5f01ce16d
34 өөрчлөгдсөн 1636 нэмэгдсэн , 1712 устгасан
  1. 23 23
      Nextcloud.xcodeproj/project.pbxproj
  2. 11 11
      Nextcloud.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
  3. 23 18
      Share/NCShareExtension.swift
  4. 0 3
      iOSClient/AppDelegate.swift
  5. 3 3
      iOSClient/Data/NCManageDatabase.swift
  6. 14 10
      iOSClient/Favorites/NCFavorite.swift
  7. 6 4
      iOSClient/FileViewInFolder/NCFileViewInFolder.swift
  8. 9 7
      iOSClient/Files/NCFiles.swift
  9. 1 10
      iOSClient/Login/NCLogin.swift
  10. 31 10
      iOSClient/Main/Collection Common/NCCollectionViewCommon.swift
  11. 30 34
      iOSClient/Main/NCFunctionCenter.swift
  12. 0 1
      iOSClient/Menu/NCViewer+Menu.swift
  13. 56 77
      iOSClient/Networking/NCNetworking.swift
  14. 86 89
      iOSClient/Networking/NCNetworkingChunkedUpload.swift
  15. 37 33
      iOSClient/Networking/NCOperationQueue.swift
  16. 121 146
      iOSClient/Networking/NCService.swift
  17. 9 7
      iOSClient/Offline/NCOffline.swift
  18. 4 4
      iOSClient/PushNotification/NCPushNotification.m
  19. 9 5
      iOSClient/Recent/NCRecent.swift
  20. 5 5
      iOSClient/Rename file/NCRenameFile.swift
  21. 1 0
      iOSClient/Security/NCEndToEndMetadata.swift
  22. 3 1
      iOSClient/Select/NCSelect.swift
  23. 2 2
      iOSClient/Settings/NCManageEndToEndEncryption.m
  24. 10 7
      iOSClient/Shares/NCShares.swift
  25. 5 3
      iOSClient/Trash/NCTrash.swift
  26. 2 3
      iOSClient/Utility/NCUtility.swift
  27. 1 1
      iOSClient/Viewer/NCViewer.swift
  28. 11 11
      iOSClient/Viewer/NCViewerMedia/NCPlayer/NCKTVHTTPCache.swift
  29. 26 72
      iOSClient/Viewer/NCViewerMedia/NCPlayer/NCPlayer.swift
  30. 111 206
      iOSClient/Viewer/NCViewerMedia/NCPlayer/NCPlayerToolBar.swift
  31. 393 453
      iOSClient/Viewer/NCViewerMedia/NCViewerMedia.swift
  32. 6 6
      iOSClient/Viewer/NCViewerMedia/NCViewerMediaPage.storyboard
  33. 587 0
      iOSClient/Viewer/NCViewerMedia/NCViewerMediaPage.swift
  34. 0 447
      iOSClient/Viewer/NCViewerMedia/NCViewerMediaZoom.swift

+ 23 - 23
Nextcloud.xcodeproj/project.pbxproj

@@ -42,9 +42,9 @@
 		F704B5E52430AA8000632F5F /* NCCreateFormUploadConflict.swift in Sources */ = {isa = PBXBuildFile; fileRef = F704B5E42430AA8000632F5F /* NCCreateFormUploadConflict.swift */; };
 		F704B5E72430C06700632F5F /* NCCreateFormUploadConflictCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F704B5E62430C06700632F5F /* NCCreateFormUploadConflictCell.xib */; };
 		F704B5E92430C0B800632F5F /* NCCreateFormUploadConflictCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F704B5E82430C0B800632F5F /* NCCreateFormUploadConflictCell.swift */; };
-		F70753EB2542A99800972D44 /* NCViewerMedia.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70753EA2542A99800972D44 /* NCViewerMedia.swift */; };
-		F70753F12542A9A200972D44 /* NCViewerMediaZoom.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70753F02542A9A200972D44 /* NCViewerMediaZoom.swift */; };
-		F70753F72542A9C000972D44 /* NCViewerMedia.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F70753F62542A9C000972D44 /* NCViewerMedia.storyboard */; };
+		F70753EB2542A99800972D44 /* NCViewerMediaPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70753EA2542A99800972D44 /* NCViewerMediaPage.swift */; };
+		F70753F12542A9A200972D44 /* NCViewerMedia.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70753F02542A9A200972D44 /* NCViewerMedia.swift */; };
+		F70753F72542A9C000972D44 /* NCViewerMediaPage.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F70753F62542A9C000972D44 /* NCViewerMediaPage.storyboard */; };
 		F707C26521A2DC5200F6181E /* NCStoreReview.swift in Sources */ = {isa = PBXBuildFile; fileRef = F707C26421A2DC5200F6181E /* NCStoreReview.swift */; };
 		F70968A424212C4E00ED60E5 /* NCLivePhoto.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70968A324212C4E00ED60E5 /* NCLivePhoto.swift */; };
 		F70A58BE24D0349500DED00D /* NCCapabilitiesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70A58BD24D0349500DED00D /* NCCapabilitiesViewController.swift */; };
@@ -442,9 +442,9 @@
 		F7063DEC2199E55F003F38DA /* SVGKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SVGKit.framework; path = Carthage/Build/iOS/SVGKit.framework; sourceTree = "<group>"; };
 		F7063DEE2199E568003F38DA /* CocoaLumberjack.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CocoaLumberjack.framework; path = Carthage/Build/iOS/CocoaLumberjack.framework; sourceTree = "<group>"; };
 		F7063DF02199E56E003F38DA /* CocoaLumberjackSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CocoaLumberjackSwift.framework; path = Carthage/Build/iOS/CocoaLumberjackSwift.framework; sourceTree = "<group>"; };
-		F70753EA2542A99800972D44 /* NCViewerMedia.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCViewerMedia.swift; sourceTree = "<group>"; };
-		F70753F02542A9A200972D44 /* NCViewerMediaZoom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCViewerMediaZoom.swift; sourceTree = "<group>"; };
-		F70753F62542A9C000972D44 /* NCViewerMedia.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = NCViewerMedia.storyboard; sourceTree = "<group>"; };
+		F70753EA2542A99800972D44 /* NCViewerMediaPage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCViewerMediaPage.swift; sourceTree = "<group>"; };
+		F70753F02542A9A200972D44 /* NCViewerMedia.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCViewerMedia.swift; sourceTree = "<group>"; };
+		F70753F62542A9C000972D44 /* NCViewerMediaPage.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = NCViewerMediaPage.storyboard; sourceTree = "<group>"; };
 		F707C26421A2DC5200F6181E /* NCStoreReview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCStoreReview.swift; sourceTree = "<group>"; };
 		F70968A324212C4E00ED60E5 /* NCLivePhoto.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCLivePhoto.swift; sourceTree = "<group>"; };
 		F70A07C8205285FB00DC1231 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/Localizable.strings"; sourceTree = "<group>"; };
@@ -1179,10 +1179,10 @@
 			isa = PBXGroup;
 			children = (
 				F79EDA9E26B004980007D134 /* NCPlayer */,
-				F70753F62542A9C000972D44 /* NCViewerMedia.storyboard */,
-				F70753EA2542A99800972D44 /* NCViewerMedia.swift */,
+				F70753F62542A9C000972D44 /* NCViewerMediaPage.storyboard */,
+				F70753EA2542A99800972D44 /* NCViewerMediaPage.swift */,
 				F718C24D254D507B00C5C256 /* NCViewerMediaDetailView.swift */,
-				F70753F02542A9A200972D44 /* NCViewerMediaZoom.swift */,
+				F70753F02542A9A200972D44 /* NCViewerMedia.swift */,
 			);
 			path = NCViewerMedia;
 			sourceTree = "<group>";
@@ -1958,7 +1958,7 @@
 				F7725A61251F33BB00D125E0 /* NCFiles.storyboard in Resources */,
 				F700510122DF63AC003A3356 /* NCShare.storyboard in Resources */,
 				F787704F22E7019900F287A9 /* NCShareLinkCell.xib in Resources */,
-				F70753F72542A9C000972D44 /* NCViewerMedia.storyboard in Resources */,
+				F70753F72542A9C000972D44 /* NCViewerMediaPage.storyboard in Resources */,
 				F70A58C024D0545100DED00D /* NCCapabilitiesViewController.storyboard in Resources */,
 				F749C10D23C4A5340027D966 /* NCIntro.storyboard in Resources */,
 				F7239877253D86D300257F49 /* NCEmptyView.xib in Resources */,
@@ -2136,7 +2136,7 @@
 				F72A47EC2487B06B005AD489 /* NCOperationQueue.swift in Sources */,
 				F769454622E9F1B0000A798A /* NCShareCommon.swift in Sources */,
 				F738E8421F90FFD100F95C8E /* NCManageEndToEndEncryption.m in Sources */,
-				F70753F12542A9A200972D44 /* NCViewerMediaZoom.swift in Sources */,
+				F70753F12542A9A200972D44 /* NCViewerMedia.swift in Sources */,
 				F7A80BCB252624C100C7CD01 /* NCFileViewInFolder.swift in Sources */,
 				F78A18B823CDE2B300F681F3 /* NCViewerRichWorkspace.swift in Sources */,
 				F77910AB25DD53C700CEDB9E /* NCSettingsBundleHelper.swift in Sources */,
@@ -2211,7 +2211,7 @@
 				F79A65C62191D95E00FF6DCC /* NCSelect.swift in Sources */,
 				F75D19E325EFE09000D74598 /* NCTrash+Menu.swift in Sources */,
 				F70CAE3A1F8CF31A008125FD /* NCEndToEndEncryption.m in Sources */,
-				F70753EB2542A99800972D44 /* NCViewerMedia.swift in Sources */,
+				F70753EB2542A99800972D44 /* NCViewerMediaPage.swift in Sources */,
 				F74C0436253F1CDC009762AB /* NCShares.swift in Sources */,
 				F7AE00F5230D5F9E007ACF8A /* NCLoginWeb.swift in Sources */,
 				F707C26521A2DC5200F6181E /* NCStoreReview.swift in Sources */,
@@ -2333,7 +2333,7 @@
 				CODE_SIGN_IDENTITY = "iPhone Developer";
 				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 9;
+				CURRENT_PROJECT_VERSION = 12;
 				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
 				DEVELOPMENT_TEAM = 6JLRKY9ZV7;
 				FRAMEWORK_SEARCH_PATHS = "$(inherited)";
@@ -2386,7 +2386,7 @@
 				CODE_SIGN_IDENTITY = "iPhone Developer";
 				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 9;
+				CURRENT_PROJECT_VERSION = 12;
 				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
 				DEVELOPMENT_TEAM = 6JLRKY9ZV7;
 				FRAMEWORK_SEARCH_PATHS = "$(inherited)";
@@ -2430,7 +2430,7 @@
 				CODE_SIGN_ENTITLEMENTS = iOSClient/Brand/Share.entitlements;
 				CODE_SIGN_IDENTITY = "iPhone Developer";
 				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
-				CURRENT_PROJECT_VERSION = 9;
+				CURRENT_PROJECT_VERSION = 12;
 				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
 				DEVELOPMENT_TEAM = 6JLRKY9ZV7;
 				FRAMEWORK_SEARCH_PATHS = "$(inherited)";
@@ -2475,7 +2475,7 @@
 				CODE_SIGN_ENTITLEMENTS = iOSClient/Brand/Share.entitlements;
 				CODE_SIGN_IDENTITY = "iPhone Developer";
 				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
-				CURRENT_PROJECT_VERSION = 9;
+				CURRENT_PROJECT_VERSION = 12;
 				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
 				DEVELOPMENT_TEAM = 6JLRKY9ZV7;
 				FRAMEWORK_SEARCH_PATHS = "$(inherited)";
@@ -2527,7 +2527,7 @@
 				CODE_SIGN_IDENTITY = "iPhone Developer";
 				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 9;
+				CURRENT_PROJECT_VERSION = 12;
 				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
 				DEVELOPMENT_TEAM = 6JLRKY9ZV7;
 				FRAMEWORK_SEARCH_PATHS = "$(inherited)";
@@ -2579,7 +2579,7 @@
 				CODE_SIGN_IDENTITY = "iPhone Developer";
 				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 9;
+				CURRENT_PROJECT_VERSION = 12;
 				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
 				DEVELOPMENT_TEAM = 6JLRKY9ZV7;
 				FRAMEWORK_SEARCH_PATHS = "$(inherited)";
@@ -2623,7 +2623,7 @@
 				CODE_SIGN_ENTITLEMENTS = iOSClient/Brand/iOSClient.entitlements;
 				CODE_SIGN_IDENTITY = "iPhone Developer";
 				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
-				CURRENT_PROJECT_VERSION = 9;
+				CURRENT_PROJECT_VERSION = 12;
 				DEVELOPMENT_TEAM = 6JLRKY9ZV7;
 				ENABLE_BITCODE = YES;
 				FRAMEWORK_SEARCH_PATHS = "$(inherited)";
@@ -2670,7 +2670,7 @@
 				CODE_SIGN_IDENTITY = "iPhone Developer";
 				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
 				COPY_PHASE_STRIP = NO;
-				CURRENT_PROJECT_VERSION = 9;
+				CURRENT_PROJECT_VERSION = 12;
 				DEVELOPMENT_TEAM = 6JLRKY9ZV7;
 				ENABLE_BITCODE = YES;
 				FRAMEWORK_SEARCH_PATHS = "$(inherited)";
@@ -2883,7 +2883,7 @@
 			repositoryURL = "https://github.com/firebase/firebase-ios-sdk";
 			requirement = {
 				kind = upToNextMajorVersion;
-				minimumVersion = 8.8.1;
+				minimumVersion = 8.9.0;
 			};
 		};
 		F72DA9B225F53E4E00B87DB1 /* XCRemoteSwiftPackageReference "SwiftRichString" */ = {
@@ -2931,7 +2931,7 @@
 			repositoryURL = "https://github.com/realm/realm-cocoa";
 			requirement = {
 				kind = exactVersion;
-				version = 10.17.0;
+				version = 10.18.0;
 			};
 		};
 		F786D58B253454BF00E3DD7B /* XCRemoteSwiftPackageReference "ios-communication-library" */ = {
@@ -2939,7 +2939,7 @@
 			repositoryURL = "https://github.com/nextcloud/ios-communication-library/";
 			requirement = {
 				kind = revision;
-				revision = 9b748dd71eff508307e6091f790b5a05be0509e4;
+				revision = 9fa3a3567f9c3092c73e3501cb055b1df40900bb;
 			};
 		};
 		F788ECC5263AAAF900ADC67F /* XCRemoteSwiftPackageReference "MarkdownKit" */ = {

+ 11 - 11
Nextcloud.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

@@ -42,8 +42,8 @@
         "repositoryURL": "https://github.com/firebase/firebase-ios-sdk",
         "state": {
           "branch": null,
-          "revision": "81b568348ab4e113dd8bcef5a83c5dd431741ae4",
-          "version": "8.8.1"
+          "revision": "2064534407804195d62f48531155ef41ddf1ce22",
+          "version": "8.9.0"
         }
       },
       {
@@ -60,8 +60,8 @@
         "repositoryURL": "https://github.com/google/GoogleAppMeasurement.git",
         "state": {
           "branch": null,
-          "revision": "06add56b27b88ae5180e92d4ee21a1199ee888a1",
-          "version": "8.8.0"
+          "revision": "1f13198b27eb2ac844953dad267bc25639128c41",
+          "version": "8.9.0"
         }
       },
       {
@@ -69,8 +69,8 @@
         "repositoryURL": "https://github.com/google/GoogleDataTransport.git",
         "state": {
           "branch": null,
-          "revision": "7fb27ea49414b9c5483503cd06baa821c8654d1e",
-          "version": "9.1.1"
+          "revision": "15ccdfd25ac55b9239b82809531ff26605e7556e",
+          "version": "9.1.2"
         }
       },
       {
@@ -78,8 +78,8 @@
         "repositoryURL": "https://github.com/google/GoogleUtilities.git",
         "state": {
           "branch": null,
-          "revision": "616fac2626b6b2d1424d79a6f786b4e2ed1cfb49",
-          "version": "7.5.2"
+          "revision": "797005ad8a1f0614063933e2fa010a5d13cb09d0",
+          "version": "7.6.0"
         }
       },
       {
@@ -105,7 +105,7 @@
         "repositoryURL": "https://github.com/nextcloud/ios-communication-library/",
         "state": {
           "branch": null,
-          "revision": "9b748dd71eff508307e6091f790b5a05be0509e4",
+          "revision": "9fa3a3567f9c3092c73e3501cb055b1df40900bb",
           "version": null
         }
       },
@@ -186,8 +186,8 @@
         "repositoryURL": "https://github.com/realm/realm-cocoa",
         "state": {
           "branch": null,
-          "revision": "7ca0ce1dd58553d5be1ec9cc7283b068c256979d",
-          "version": "10.17.0"
+          "revision": "9f43d0da902c55b493d6c8bb63203764caa8acbe",
+          "version": "10.18.0"
         }
       },
       {

+ 23 - 18
Share/NCShareExtension.swift

@@ -705,17 +705,19 @@ extension NCShareExtension {
         
         NCNetworking.shared.createFolder(fileName: fileName, serverUrl: serverUrl, account: activeAccount.account, urlBase: activeAccount.urlBase) { (errorCode, errorDescription) in
             
-            if errorCode == 0 {
-                
-                self.serverUrl = self.serverUrl + "/" + fileName
-                self.reloadDatasource(withLoadFolder: true)
-                self.setNavigationBar(navigationTitle: fileName)
-                
-            }  else {
-                
-                let alertController = UIAlertController(title: NSLocalizedString("_error_", comment: ""), message: errorDescription, preferredStyle: .alert)
-                alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))
-                self.present(alertController, animated: true)
+            DispatchQueue.main.async {
+                if errorCode == 0 {
+                    
+                    self.serverUrl = self.serverUrl + "/" + fileName
+                    self.reloadDatasource(withLoadFolder: true)
+                    self.setNavigationBar(navigationTitle: fileName)
+                    
+                }  else {
+                    
+                    let alertController = UIAlertController(title: NSLocalizedString("_error_", comment: ""), message: errorDescription, preferredStyle: .alert)
+                    alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))
+                    self.present(alertController, animated: true)
+                }
             }
         }
     }
@@ -726,14 +728,17 @@ extension NCShareExtension {
         collectionView.reloadData()
         
         NCNetworking.shared.readFolder(serverUrl: serverUrl, account: activeAccount.account) { (_, metadataFolder, _, _, _, _, errorCode, errorDescription) in
-            if errorCode != 0 {
-                let alertController = UIAlertController(title: NSLocalizedString("_error_", comment: ""), message: errorDescription, preferredStyle: .alert)
-                alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))
-                self.present(alertController, animated: true)
+            
+            DispatchQueue.main.async {
+                if errorCode != 0 {
+                    let alertController = UIAlertController(title: NSLocalizedString("_error_", comment: ""), message: errorDescription, preferredStyle: .alert)
+                    alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))
+                    self.present(alertController, animated: true)
+                }
+                self.networkInProgress = false
+                self.metadataFolder = metadataFolder
+                self.reloadDatasource(withLoadFolder: false)
             }
-            self.networkInProgress = false
-            self.metadataFolder = metadataFolder
-            self.reloadDatasource(withLoadFolder: false)
         }
     }
     

+ 0 - 3
iOSClient/AppDelegate.swift

@@ -314,9 +314,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         // close detail
         NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterMenuDetailClose)
         
-        // Video proxy
-        NCKTVHTTPCache.shared.restartProxy(user: user, password: password)
-
         // Registeration domain File Provider
         //FileProviderDomain *fileProviderDomain = [FileProviderDomain new];
         //[fileProviderDomain removeAllDomains];

+ 3 - 3
iOSClient/Data/NCManageDatabase.swift

@@ -2214,15 +2214,15 @@ class NCManageDatabase: NSObject {
                         // update
                         if result.status == NCGlobal.shared.metadataStatusNormal && (result.etag != metadata.etag || result.fileNameView != metadata.fileNameView || result.date != metadata.date || result.permissions != metadata.permissions || result.hasPreview != metadata.hasPreview || result.note != metadata.note) {
                             ocIdsUdate.append(metadata.ocId)
-                            realm.add(metadata, update: .all)
+                            realm.add(tableMetadata.init(value: metadata), update: .all)
                         } else if result.status == NCGlobal.shared.metadataStatusNormal && addCompareLivePhoto && result.livePhoto != metadata.livePhoto {
                             ocIdsUdate.append(metadata.ocId)
-                            realm.add(metadata, update: .all)
+                            realm.add(tableMetadata.init(value: metadata), update: .all)
                         }
                     } else {
                         // new
                         ocIdsUdate.append(metadata.ocId)
-                        realm.add(metadata, update: .all)
+                        realm.add(tableMetadata.init(value: metadata), update: .all)
                     }
                     
                     if metadata.directory && !ocIdsUdate.contains(metadata.ocId) {

+ 14 - 10
iOSClient/Favorites/NCFavorite.swift

@@ -82,9 +82,11 @@ class NCFavorite: NCCollectionViewCommon  {
                     NCContentPresenter.shared.messageNotification("_error_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode)
                 }
                 
-                self.refreshControl.endRefreshing()
-                self.isReloadDataSourceNetworkInProgress = false
-                self.reloadDataSource()
+                DispatchQueue.main.async {
+                    self.refreshControl.endRefreshing()
+                    self.isReloadDataSourceNetworkInProgress = false
+                    self.reloadDataSource()
+                }
             }
             
         } else {
@@ -100,13 +102,15 @@ class NCFavorite: NCCollectionViewCommon  {
                     }
                 }
                 
-                self.refreshControl.endRefreshing()
-                self.isReloadDataSourceNetworkInProgress = false
-                self.richWorkspaceText = tableDirectory?.richWorkspace
-                if metadatasUpdate?.count ?? 0 > 0 || metadatasDelete?.count ?? 0 > 0 || forced {
-                    self.reloadDataSource()
-                } else {
-                    self.collectionView?.reloadData()
+                DispatchQueue.main.async {
+                    self.refreshControl.endRefreshing()
+                    self.isReloadDataSourceNetworkInProgress = false
+                    self.richWorkspaceText = tableDirectory?.richWorkspace
+                    if metadatasUpdate?.count ?? 0 > 0 || metadatasDelete?.count ?? 0 > 0 || forced {
+                        self.reloadDataSource()
+                    } else {
+                        self.collectionView?.reloadData()
+                    }
                 }
             }
         }

+ 6 - 4
iOSClient/FileViewInFolder/NCFileViewInFolder.swift

@@ -143,10 +143,12 @@ class NCFileViewInFolder: NCCollectionViewCommon {
                 }
             }
             
-            self.refreshControl.endRefreshing()
-            self.isReloadDataSourceNetworkInProgress = false
-            self.richWorkspaceText = tableDirectory?.richWorkspace
-            self.reloadDataSource()
+            DispatchQueue.main.async {
+                self.refreshControl.endRefreshing()
+                self.isReloadDataSourceNetworkInProgress = false
+                self.richWorkspaceText = tableDirectory?.richWorkspace
+                self.reloadDataSource()
+            }
         }
     }
 }

+ 9 - 7
iOSClient/Files/NCFiles.swift

@@ -110,13 +110,15 @@ class NCFiles: NCCollectionViewCommon  {
                 }
             }
             
-            self.refreshControl.endRefreshing()
-            self.isReloadDataSourceNetworkInProgress = false
-            self.richWorkspaceText = tableDirectory?.richWorkspace
-            if metadatasUpdate?.count ?? 0 > 0 || metadatasDelete?.count ?? 0 > 0 || forced {
-                self.reloadDataSource()
-            } else {
-                self.collectionView?.reloadData()
+            DispatchQueue.main.async {
+                self.refreshControl.endRefreshing()
+                self.isReloadDataSourceNetworkInProgress = false
+                self.richWorkspaceText = tableDirectory?.richWorkspace
+                if metadatasUpdate?.count ?? 0 > 0 || metadatasDelete?.count ?? 0 > 0 || forced {
+                    self.reloadDataSource()
+                } else {
+                    self.collectionView?.reloadData()
+                }
             }
         }
     }

+ 1 - 10
iOSClient/Login/NCLogin.swift

@@ -119,9 +119,7 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
             // Cancel Button
             let navigationItemCancel = UIBarButtonItem.init(barButtonSystemItem: .stop, target: self, action: #selector(self.actionCancel))
             navigationItemCancel.tintColor = textColor
-            navigationItem.leftBarButtonItem = navigationItemCancel
-            
-            NotificationCenter.default.addObserver(self, selector: #selector(applicationDidEnterBackground), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterApplicationDidEnterBackground), object: nil)
+            navigationItem.leftBarButtonItem = navigationItemCancel            
         }
         
         self.navigationController?.navigationBar.setValue(true, forKey: "hidesShadow")
@@ -143,13 +141,6 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
         appDelegate.startTimerErrorNetworking()
     }
     
-    // MARK: - NotificationCenter
-
-    @objc func applicationDidEnterBackground() {
-        
-        dismiss(animated: false)
-    }
-    
     // MARK: - TextField
     
     func textFieldShouldReturn(_ textField: UITextField) -> Bool {

+ 31 - 10
iOSClient/Main/Collection Common/NCCollectionViewCommon.swift

@@ -537,7 +537,16 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
                 if let cell = collectionView?.cellForItem(at: IndexPath(row: index, section: 0)) {
                     if cell is NCListCell {
                         let cell = cell as! NCListCell
-                        if progressNumber.floatValue > 0 {
+                        if progressNumber.floatValue == 1 {
+                            cell.progressView?.isHidden = true
+                            cell.progressView?.progress = .zero
+                            cell.setButtonMore(named: NCGlobal.shared.buttonMoreMore, image: NCBrandColor.cacheImages.buttonMore)
+                            if let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
+                                cell.labelInfo.text = CCUtility.dateDiff(metadata.date as Date) + " · " + CCUtility.transformedSize(metadata.size)
+                            } else {
+                                cell.labelInfo.text = ""
+                            }
+                        } else if progressNumber.floatValue > 0 {
                             cell.progressView?.isHidden = false
                             cell.progressView?.progress = progressNumber.floatValue
                             cell.setButtonMore(named: NCGlobal.shared.buttonMoreStop, image: NCBrandColor.cacheImages.buttonStop)
@@ -549,7 +558,12 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
                         }
                     } else if cell is NCTransferCell {
                         let cell = cell as! NCTransferCell
-                        if progressNumber.floatValue > 0 {
+                        if progressNumber.floatValue == 1 {
+                            cell.progressView?.isHidden = true
+                            cell.progressView?.progress = .zero
+                            cell.buttonMore.isHidden = true
+                            cell.labelInfo.text = ""
+                        } else if progressNumber.floatValue > 0 {
                             cell.progressView?.isHidden = false
                             cell.progressView?.progress = progressNumber.floatValue
                             cell.setButtonMore(named: NCGlobal.shared.buttonMoreStop, image: NCBrandColor.cacheImages.buttonStop)
@@ -561,7 +575,11 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
                         }
                     } else if cell is NCGridCell {
                         let cell = cell as! NCGridCell
-                        if progressNumber.floatValue > 0 {
+                        if progressNumber.floatValue == 1 {
+                            cell.progressView.isHidden = true
+                            cell.progressView.progress = .zero
+                            cell.setButtonMore(named: NCGlobal.shared.buttonMoreMore, image: NCBrandColor.cacheImages.buttonMore)
+                        } else if progressNumber.floatValue > 0 {
                             cell.progressView.isHidden = false
                             cell.progressView.progress = progressNumber.floatValue
                             cell.setButtonMore(named: NCGlobal.shared.buttonMoreStop, image: NCBrandColor.cacheImages.buttonStop)
@@ -965,13 +983,15 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
             collectionView?.reloadData()
             
             NCNetworking.shared.searchFiles(urlBase: appDelegate.urlBase, user: appDelegate.user, literal: literalSearch!) { (account, metadatas, errorCode, errorDescription) in
-                if self.searchController?.isActive ?? false && errorCode == 0 {
-                    self.metadatasSource = metadatas!
-                }
                 
-                self.refreshControl.endRefreshing()
-                self.isReloadDataSourceNetworkInProgress = false
-                self.reloadDataSource()
+                DispatchQueue.main.async {
+                    if self.searchController?.isActive ?? false && errorCode == 0 {
+                        self.metadatasSource = metadatas!
+                    }
+                    self.refreshControl.endRefreshing()
+                    self.isReloadDataSourceNetworkInProgress = false
+                    self.reloadDataSource()
+                }
             }
         } else {
             self.refreshControl.endRefreshing()
@@ -1000,8 +1020,9 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
                             // E2EE
                             if let metadataFolder = metadataFolder {
                                 if metadataFolder.e2eEncrypted && CCUtility.isEnd(toEndEnabled: self.appDelegate.account) {
-                                    
+                                                                        
                                     NCCommunication.shared.getE2EEMetadata(fileId: metadataFolder.ocId, e2eToken: nil) { (account, e2eMetadata, errorCode, errorDescription) in
+                                        
                                         if errorCode == 0 && e2eMetadata != nil {
                                             
                                             if !NCEndToEndMetadata.shared.decoderMetadata(e2eMetadata!, privateKey: CCUtility.getEndToEndPrivateKey(account), serverUrl: self.serverUrl, account: account, urlBase: self.appDelegate.urlBase) {

+ 30 - 34
iOSClient/Main/NCFunctionCenter.swift

@@ -228,47 +228,43 @@ import Queuer
         
         NCUtility.shared.startActivityIndicator(backgroundView: nil, blurEffect: true)
         
-        DispatchQueue.global().async {
-            
-            var error: Int = 0
-            var items: [Any] = []
+        var error: Int = 0
+        var items: [Any] = []
 
-            for ocId in selectOcId {
-                if let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
-                    if metadata.directory {
-                        continue
-                    }
-                    if !CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView) {
-                        let semaphore = Semaphore()
-                        NCNetworking.shared.download(metadata: metadata, selector: "") { errorCode in
-                            error = errorCode
-                            semaphore.continue()
-                        }
-                        semaphore.wait()
-                    }
-                    if error != 0 {
-                        break
+        for ocId in selectOcId {
+            if let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
+                if metadata.directory {
+                    continue
+                }
+                if !CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView) {
+                    let semaphore = Semaphore()
+                    NCNetworking.shared.download(metadata: metadata, selector: "") { errorCode in
+                        error = errorCode
+                        semaphore.continue()
                     }
-                    let fileURL = URL(fileURLWithPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView))
-                    items.append(fileURL)
+                    semaphore.wait()
                 }
+                if error != 0 {
+                    break
+                }
+                let fileURL = URL(fileURLWithPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView))
+                items.append(fileURL)
             }
-            if error == 0 && items.count > 0 {
-                DispatchQueue.main.async {
+        }
+        if error == 0 && items.count > 0 {
+              
+            guard let mainTabBar = self.appDelegate.mainTabBar else { return }
                     
-                    guard let mainTabBar = self.appDelegate.mainTabBar else { return }
-                            
-                    let activityViewController = UIActivityViewController.init(activityItems: items, applicationActivities: nil)
+            let activityViewController = UIActivityViewController.init(activityItems: items, applicationActivities: nil)
 
-                    activityViewController.popoverPresentationController?.permittedArrowDirections = .any
-                    activityViewController.popoverPresentationController?.sourceView = mainTabBar
-                    activityViewController.popoverPresentationController?.sourceRect = mainTabBar.menuRect
-                    
-                    self.appDelegate.window?.rootViewController?.present(activityViewController, animated: true)
-                }
-            }
-            NCUtility.shared.stopActivityIndicator()
+            activityViewController.popoverPresentationController?.permittedArrowDirections = .any
+            activityViewController.popoverPresentationController?.sourceView = mainTabBar
+            activityViewController.popoverPresentationController?.sourceRect = mainTabBar.menuRect
+            
+            self.appDelegate.window?.rootViewController?.present(activityViewController, animated: true)
+            
         }
+        NCUtility.shared.stopActivityIndicator()
     }
         
     // MARK: - Save as scan

+ 0 - 1
iOSClient/Menu/NCViewer+Menu.swift

@@ -96,7 +96,6 @@ extension NCViewer {
                     icon: NCUtility.shared.loadImage(named: "tray.and.arrow.down"),
                     action: { menuAction in
                         if ((localFile == nil || !CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView)) && metadata.session == "") {
-                            
                             NCNetworking.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorLoadOffline) { (_) in }
                         } else {
                             NCManageDatabase.shared.setLocalFile(ocId: metadata.ocId, offline: !localFile!.offline)

+ 56 - 77
iOSClient/Networking/NCNetworking.swift

@@ -100,17 +100,6 @@ import Queuer
         _ = sessionManagerBackground
         _ = sessionManagerBackgroundWWan
         #endif
-        
-        // Notification
-        NotificationCenter.default.addObserver(self, selector: #selector(downloadStartFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterDownloadStartFile), object: nil)
-        NotificationCenter.default.addObserver(self, selector: #selector(downloadedFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterDownloadedFile), object: nil)
-        NotificationCenter.default.addObserver(self, selector: #selector(downloadCancelFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterDownloadCancelFile), object: nil)
-        
-        NotificationCenter.default.addObserver(self, selector: #selector(uploadStartFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterUploadStartFile), object: nil)
-        NotificationCenter.default.addObserver(self, selector: #selector(uploadedFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterUploadedFile), object: nil)
-        NotificationCenter.default.addObserver(self, selector: #selector(uploadCancelFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterUploadCancelFile), object: nil)
-        
-        NotificationCenter.default.addObserver(self, selector: #selector(triggerProgressTask(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterProgressTask), object:nil)
     }
     
     //MARK: - Communication Delegate
@@ -165,61 +154,6 @@ import Queuer
         #endif
     }
     
-    // MARK: - NotificationCenter
-    
-    @objc func downloadStartFile(_ notification: NSNotification) {
-    }
-    
-    @objc func downloadedFile(_ notification: NSNotification) {
-        
-        if let userInfo = notification.userInfo as NSDictionary?, let ocId = userInfo["ocId"] as? String {
-            #if !EXTENSION
-            (UIApplication.shared.delegate as! AppDelegate).listProgress[ocId] = nil
-            #endif
-        }
-    }
-    
-    @objc func downloadCancelFile(_ notification: NSNotification) {
-        
-        if let userInfo = notification.userInfo as NSDictionary?, let ocId = userInfo["ocId"] as? String {
-            #if !EXTENSION
-            (UIApplication.shared.delegate as! AppDelegate).listProgress[ocId] = nil
-            #endif
-        }
-    }
-    
-    @objc func uploadStartFile(_ notification: NSNotification) {
-    }
-    
-    @objc func uploadedFile(_ notification: NSNotification) {
-        
-        if let userInfo = notification.userInfo as NSDictionary?, let ocId = userInfo["ocId"] as? String {
-            #if !EXTENSION
-            (UIApplication.shared.delegate as! AppDelegate).listProgress[ocId] = nil
-            #endif
-        }
-    }
-    
-    @objc func uploadCancelFile(_ notification: NSNotification) {
-        
-        if let userInfo = notification.userInfo as NSDictionary?, let ocId = userInfo["ocId"] as? String {
-            #if !EXTENSION
-            (UIApplication.shared.delegate as! AppDelegate).listProgress[ocId] = nil
-            #endif
-        }
-    }
-    
-    @objc func triggerProgressTask(_ notification: NSNotification) {
-
-        if let userInfo = notification.userInfo as NSDictionary?, let progressNumber = userInfo["progress"] as? NSNumber, let totalBytes = userInfo["totalBytes"] as? Int64, let totalBytesExpected = userInfo["totalBytesExpected"] as? Int64, let ocId = userInfo["ocId"] as? String {
-             
-            #if !EXTENSION
-            let progressType = NCGlobal.progressType(progress: progressNumber.floatValue, totalBytes: totalBytes, totalBytesExpected: totalBytesExpected)
-            (UIApplication.shared.delegate as! AppDelegate).listProgress[ocId] = progressType
-            #endif
-        }
-    }
-    
     //MARK: - Pinning check
     
     private func checkTrustedChallenge(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge) -> Bool {
@@ -230,6 +164,14 @@ import Queuer
         let directoryCertificate = CCUtility.getDirectoryCerificates()!
         let directoryCertificateUrl = URL.init(fileURLWithPath: directoryCertificate)
         let host = challenge.protectionSpace.host
+        let hostPushNotification = URL(string: NCBrandOptions.shared.pushNotificationServerProxy)?.host
+        
+        //
+        // TRUSTED push notification proxy server
+        //
+        if host == hostPushNotification {
+            return true
+        }
         
         if let serverTrust: SecTrust = protectionSpace.serverTrust {
             
@@ -276,8 +218,7 @@ import Queuer
                     if !trusted && !trustedV2 {
                         #if !EXTENSION
                         DispatchQueue.main.async {
-                            let appDelegate = UIApplication.shared.delegate as! AppDelegate
-                            CCUtility.setCertificateError(appDelegate.account)
+                            CCUtility.setCertificateError((UIApplication.shared.delegate as! AppDelegate).account)
                         }
                         #endif
                     }
@@ -377,15 +318,20 @@ import Queuer
     
     @objc func cancelDownload(ocId: String, serverUrl:String, fileNameView: String) {
         
+        #if !EXTENSION
+        // removed progress ocid
+        DispatchQueue.main.async { (UIApplication.shared.delegate as! AppDelegate).listProgress[ocId] = nil }
+        #endif
+        
         guard let fileNameLocalPath = CCUtility.getDirectoryProviderStorageOcId(ocId, fileNameView: fileNameView) else { return }
         
         if let request = downloadRequest[fileNameLocalPath] {
             request.cancel()
         } else {
             if let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
+                
                 NCManageDatabase.shared.setMetadataSession(ocId: ocId, session: "", sessionError: "", sessionSelector: "", sessionTaskIdentifier: 0, status: NCGlobal.shared.metadataStatusNormal)
                 NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDownloadCancelFile, userInfo: ["ocId":metadata.ocId])
-                
             }
         }
     }
@@ -403,7 +349,7 @@ import Queuer
                 
         NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId, session: NCCommunicationCommon.shared.sessionIdentifierDownload, sessionError: "", sessionSelector: selector, sessionTaskIdentifier: 0, status: NCGlobal.shared.metadataStatusInDownload)
                     
-        NCCommunication.shared.download(serverUrlFileName: serverUrlFileName, fileNameLocalPath: fileNameLocalPath, requestHandler: { (request) in
+        NCCommunication.shared.download(serverUrlFileName: serverUrlFileName, fileNameLocalPath: fileNameLocalPath, queue: NCCommunicationCommon.shared.backgroundQueue, requestHandler: { (request) in
             
             self.downloadRequest[fileNameLocalPath] = request
             
@@ -414,6 +360,12 @@ import Queuer
             
         }, progressHandler: { (progress) in
             
+            #if !EXTENSION
+            // add progress ocid
+            let progressType = NCGlobal.progressType(progress: Float(progress.fractionCompleted), totalBytes: progress.totalUnitCount, totalBytesExpected: progress.completedUnitCount)
+            DispatchQueue.main.async { (UIApplication.shared.delegate as! AppDelegate).listProgress[metadata.ocId] = progressType }
+            #endif
+            
             NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterProgressTask, object: nil, userInfo: ["account":metadata.account, "ocId":metadata.ocId, "fileName":metadata.fileName, "serverUrl":metadata.serverUrl, "status":NSNumber(value: NCGlobal.shared.metadataStatusInDownload), "progress":NSNumber(value: progress.fractionCompleted), "totalBytes":NSNumber(value: progress.totalUnitCount), "totalBytesExpected":NSNumber(value: progress.completedUnitCount)])
             
         }) { (account, etag, date, length, allHeaderFields, error, errorCode, errorDescription) in
@@ -447,6 +399,10 @@ import Queuer
             }
             
             self.downloadRequest[fileNameLocalPath] = nil
+            #if !EXTENSION
+            DispatchQueue.main.async { (UIApplication.shared.delegate as! AppDelegate).listProgress[metadata.ocId] = nil }
+            #endif
+            
             if error?.isExplicitlyCancelledError ?? false {
                 NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDownloadCancelFile, userInfo: ["ocId":metadata.ocId])
             } else {
@@ -482,9 +438,9 @@ import Queuer
             metadata = tableMetadata.init(value: metadata)
            
             if metadata.e2eEncrypted {
-                #if !EXTENSION_FILE_PROVIDER_EXTENSION
+#if !EXTENSION_FILE_PROVIDER_EXTENSION
                 NCNetworkingE2EE.shared.upload(metadata: tableMetadata.init(value: metadata), start: { start() }, completion: completion)
-                #endif
+#endif
             } else if metadata.chunk {
                 uploadChunkedFile(metadata: metadata, start: { start() }, completion: completion)
             } else if metadata.session == NCCommunicationCommon.shared.sessionIdentifierUpload {
@@ -543,6 +499,12 @@ import Queuer
             
         }, progressHandler: { (progress) in
             
+            #if !EXTENSION
+            // add progress ocid
+            let progressType = NCGlobal.progressType(progress: Float(progress.fractionCompleted), totalBytes: progress.totalUnitCount, totalBytesExpected: progress.completedUnitCount)
+            DispatchQueue.main.async { (UIApplication.shared.delegate as! AppDelegate).listProgress[metadata.ocId] = progressType }
+            #endif
+            
             NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterProgressTask, userInfo: ["account":metadata.account, "ocId":metadata.ocId, "fileName":metadata.fileName, "serverUrl":metadata.serverUrl, "status":NSNumber(value: NCGlobal.shared.metadataStatusInUpload), "progress":NSNumber(value: progress.fractionCompleted), "totalBytes":NSNumber(value: progress.totalUnitCount), "totalBytesExpected":NSNumber(value: progress.completedUnitCount)])
             
         }) { (account, ocId, etag, date, size, allHeaderFields, error, errorCode, errorDescription) in
@@ -679,6 +641,10 @@ import Queuer
                 NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadedFile, userInfo: ["ocId":metadata.ocId, "ocIdTemp":ocIdTemp, "errorCode":errorCode, "errorDescription":""])
             }
             
+            #if !EXTENSION
+            DispatchQueue.main.async { (UIApplication.shared.delegate as! AppDelegate).listProgress[metadata.ocId] = nil }
+            #endif
+            
             // Delete
             self.uploadMetadataInBackground[fileName+serverUrl] = nil
         }
@@ -698,6 +664,13 @@ import Queuer
         }
         
         if let metadata = metadata {
+            
+            #if !EXTENSION
+            // add progress ocid
+            let progressType = NCGlobal.progressType(progress: progress, totalBytes: totalBytes, totalBytesExpected: totalBytesExpected)
+            DispatchQueue.main.async { (UIApplication.shared.delegate as! AppDelegate).listProgress[metadata.ocId] = progressType }
+            #endif
+            
             NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterProgressTask, userInfo: ["account":metadata.account, "ocId":metadata.ocId, "fileName":metadata.fileName, "serverUrl":serverUrl, "status":NSNumber(value: NCGlobal.shared.metadataStatusInUpload), "progress":NSNumber(value: progress), "totalBytes":NSNumber(value: totalBytes), "totalBytesExpected":NSNumber(value: totalBytesExpected)])
         }
     }
@@ -725,6 +698,11 @@ import Queuer
         
         let metadata = tableMetadata.init(value: metadata)
 
+        #if !EXTENSION
+        // removed progress ocid
+        DispatchQueue.main.async { (UIApplication.shared.delegate as! AppDelegate).listProgress[metadata.ocId] = nil }
+        #endif
+        
         if metadata.session.count == 0 {
             NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))
             return completion()
@@ -830,7 +808,7 @@ import Queuer
             }
             
             if metadata.status == NCGlobal.shared.metadataStatusDownloading && metadata.session == NCCommunicationCommon.shared.sessionIdentifierDownload {
-                NCNetworking.shared.cancelDownload(ocId: metadata.ocId, serverUrl: metadata.serverUrl, fileNameView: metadata.fileNameView)
+                cancelDownload(ocId: metadata.ocId, serverUrl: metadata.serverUrl, fileNameView: metadata.fileNameView)
             }
         }
         
@@ -843,7 +821,7 @@ import Queuer
     
     @objc func readFolder(serverUrl: String, account: String, completion: @escaping (_ account: String, _ metadataFolder: tableMetadata?, _ metadatas: [tableMetadata]?, _ metadatasUpdate: [tableMetadata]?, _ metadatasLocalUpdate: [tableMetadata]?, _ metadatasDelete: [tableMetadata]?, _ errorCode: Int, _ errorDescription: String)->()) {
         
-        NCCommunication.shared.readFileOrFolder(serverUrlFileName: serverUrl, depth: "1", showHiddenFiles: CCUtility.getShowHiddenFiles()) { (account, files, responseData, errorCode, errorDescription) in
+        NCCommunication.shared.readFileOrFolder(serverUrlFileName: serverUrl, depth: "1", showHiddenFiles: CCUtility.getShowHiddenFiles(), queue: NCCommunicationCommon.shared.backgroundQueue) { (account, files, responseData, errorCode, errorDescription) in
             
             if errorCode == 0  {
                               
@@ -877,7 +855,7 @@ import Queuer
     
     @objc func readFile(serverUrlFileName: String, account: String, completion: @escaping (_ account: String, _ metadata: tableMetadata?, _ errorCode: Int, _ errorDescription: String)->()) {
         
-        NCCommunication.shared.readFileOrFolder(serverUrlFileName: serverUrlFileName, depth: "0", showHiddenFiles: CCUtility.getShowHiddenFiles()) { (account, files, responseData, errorCode, errorDescription) in
+        NCCommunication.shared.readFileOrFolder(serverUrlFileName: serverUrlFileName, depth: "0", showHiddenFiles: CCUtility.getShowHiddenFiles(), queue: NCCommunicationCommon.shared.backgroundQueue) { (account, files, responseData, errorCode, errorDescription) in
 
             if errorCode == 0 && files.count == 1 {
                 
@@ -898,7 +876,7 @@ import Queuer
     
     @objc func searchFiles(urlBase: String, user: String, literal: String, completion: @escaping (_ account: String, _ metadatas: [tableMetadata]?, _ errorCode: Int, _ errorDescription: String)->()) {
         
-        NCCommunication.shared.searchLiteral(serverUrl: urlBase, depth: "infinity", literal: literal, showHiddenFiles: CCUtility.getShowHiddenFiles()) { (account, files, errorCode, errorDescription) in
+        NCCommunication.shared.searchLiteral(serverUrl: urlBase, depth: "infinity", literal: literal, showHiddenFiles: CCUtility.getShowHiddenFiles(), queue: NCCommunicationCommon.shared.backgroundQueue) { (account, files, errorCode, errorDescription) in
             
             if errorCode == 0  {
                 
@@ -1155,7 +1133,8 @@ import Queuer
     }
     
     @objc func listingFavoritescompletion(selector: String, completion: @escaping (_ account: String, _ metadatas: [tableMetadata]?, _ errorCode: Int, _ errorDescription: String)->()) {
-        NCCommunication.shared.listingFavorites(showHiddenFiles: CCUtility.getShowHiddenFiles()) { (account, files, errorCode, errorDescription) in
+        NCCommunication.shared.listingFavorites(showHiddenFiles: CCUtility.getShowHiddenFiles(), queue: NCCommunicationCommon.shared.backgroundQueue) { (account, files, errorCode, errorDescription) in
+            
             if errorCode == 0 {
                 NCManageDatabase.shared.convertNCCommunicationFilesToMetadatas(files, useMetadataFolder: false, account: account) { (_, _, metadatas) in
                     NCManageDatabase.shared.updateMetadatasFavorite(account: account, metadatas: metadatas)

+ 86 - 89
iOSClient/Networking/NCNetworkingChunkedUpload.swift

@@ -65,113 +65,110 @@ extension NCNetworking {
                     
                 NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadStartFile, userInfo: ["ocId": metadata.ocId])
                 
-                DispatchQueue.global(qos: .background).async {
+                for fileName in filesNames {
                         
-                    for fileName in filesNames {
-                            
-                        let serverUrlFileName = chunkFolderPath + "/" + fileName
-                        let fileNameChunkLocalPath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: fileName)!
-                        
-                        var size: Int64?
-                        if let tableChunk = NCManageDatabase.shared.getChunk(account: metadata.account, fileName: fileName) {
-                            size = tableChunk.size - NCUtilityFileSystem.shared.getFileSize(filePath: fileNameChunkLocalPath)
-                        }
+                    let serverUrlFileName = chunkFolderPath + "/" + fileName
+                    let fileNameChunkLocalPath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: fileName)!
+                    
+                    var size: Int64?
+                    if let tableChunk = NCManageDatabase.shared.getChunk(account: metadata.account, fileName: fileName) {
+                        size = tableChunk.size - NCUtilityFileSystem.shared.getFileSize(filePath: fileNameChunkLocalPath)
+                    }
+                                            
+                    let semaphore = Semaphore()
                                                 
-                        let semaphore = Semaphore()
-                                                    
-                        NCCommunication.shared.upload(serverUrlFileName: serverUrlFileName, fileNameLocalPath: fileNameChunkLocalPath, requestHandler: { (request) in
-                                
-                            self.uploadRequest[fileNameLocalPath] = request
+                    NCCommunication.shared.upload(serverUrlFileName: serverUrlFileName, fileNameLocalPath: fileNameChunkLocalPath, requestHandler: { (request) in
                             
-                        }, taskHandler: { (task) in
-                            
-                            NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId, sessionError: "", sessionTaskIdentifier: task.taskIdentifier, status: NCGlobal.shared.metadataStatusUploading)
-                            
-                            NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadStartFile, userInfo: ["ocId":metadata.ocId])
-                            
-                            NCCommunicationCommon.shared.writeLog("Upload chunk: " + fileName)
-                           
-                        }, progressHandler: { (progress) in
+                        self.uploadRequest[fileNameLocalPath] = request
+                        
+                    }, taskHandler: { (task) in
+                        
+                        NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId, sessionError: "", sessionTaskIdentifier: task.taskIdentifier, status: NCGlobal.shared.metadataStatusUploading)
+                        
+                        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadStartFile, userInfo: ["ocId":metadata.ocId])
+                        
+                        NCCommunicationCommon.shared.writeLog("Upload chunk: " + fileName)
+                       
+                    }, progressHandler: { (progress) in
+                        
+                        if let size = size {
                             
-                            if let size = size {
+                            let totalBytesExpected = size + progress.completedUnitCount
+                            let totalBytes = metadata.size
+                            let fractionCompleted = Float(totalBytesExpected) / Float(totalBytes)
                                 
-                                let totalBytesExpected = size + progress.completedUnitCount
-                                let totalBytes = metadata.size
-                                let fractionCompleted = Float(totalBytesExpected) / Float(totalBytes)
-                                    
-                                NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterProgressTask, object: nil, userInfo: ["account":metadata.account, "ocId":metadata.ocId, "fileName":metadata.fileName, "serverUrl":metadata.serverUrl, "status":NSNumber(value: NCGlobal.shared.metadataStatusInUpload), "progress":NSNumber(value: fractionCompleted), "totalBytes":NSNumber(value: totalBytes), "totalBytesExpected":NSNumber(value: totalBytesExpected)])
-                            }
-                            
-                        }) { (_, _, _, _, _, _, _, errorCode, errorDescription) in
-                               
-                            self.uploadRequest[fileNameLocalPath] = nil
-                            uploadErrorCode = errorCode
-                            uploadErrorDescription = errorDescription
-                            semaphore.continue()
-                        }
-                            
-                        semaphore.wait()
-                            
-                        if uploadErrorCode == 0 {
-                            NCManageDatabase.shared.deleteChunk(account: metadata.account, ocId: metadata.ocId, fileName: fileName)
-                        } else {
-                            break
+                            NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterProgressTask, object: nil, userInfo: ["account":metadata.account, "ocId":metadata.ocId, "fileName":metadata.fileName, "serverUrl":metadata.serverUrl, "status":NSNumber(value: NCGlobal.shared.metadataStatusInUpload), "progress":NSNumber(value: fractionCompleted), "totalBytes":NSNumber(value: totalBytes), "totalBytesExpected":NSNumber(value: totalBytesExpected)])
                         }
+                        
+                    }) { (_, _, _, _, _, _, _, errorCode, errorDescription) in
+                           
+                        self.uploadRequest[fileNameLocalPath] = nil
+                        uploadErrorCode = errorCode
+                        uploadErrorDescription = errorDescription
+                        semaphore.continue()
                     }
                         
+                    semaphore.wait()
+                        
                     if uploadErrorCode == 0 {
-                            
-                        // Assembling the chunks
-                        let serverUrlFileNameSource = chunkFolderPath + "/.file"
-                        let pathServerUrl = CCUtility.returnPathfromServerUrl(metadata.serverUrl, urlBase: metadata.urlBase, account: metadata.account)!
-                        let serverUrlFileNameDestination = metadata.urlBase + "/" + NCUtilityFileSystem.shared.getWebDAV(account: metadata.account) + "/files/" + metadata.userId + pathServerUrl + "/" + metadata.fileName
+                        NCManageDatabase.shared.deleteChunk(account: metadata.account, ocId: metadata.ocId, fileName: fileName)
+                    } else {
+                        break
+                    }
+                }
+                    
+                if uploadErrorCode == 0 {
                         
-                        var addCustomHeaders: [String:String] = [:]
-                        let creationDate = "\(metadata.creationDate.timeIntervalSince1970)"
-                        let modificationDate = "\(metadata.date.timeIntervalSince1970)"
-                            
-                        addCustomHeaders["X-OC-CTime"] = creationDate
-                        addCustomHeaders["X-OC-MTime"] = modificationDate
+                    // Assembling the chunks
+                    let serverUrlFileNameSource = chunkFolderPath + "/.file"
+                    let pathServerUrl = CCUtility.returnPathfromServerUrl(metadata.serverUrl, urlBase: metadata.urlBase, account: metadata.account)!
+                    let serverUrlFileNameDestination = metadata.urlBase + "/" + NCUtilityFileSystem.shared.getWebDAV(account: metadata.account) + "/files/" + metadata.userId + pathServerUrl + "/" + metadata.fileName
+                    
+                    var addCustomHeaders: [String:String] = [:]
+                    let creationDate = "\(metadata.creationDate.timeIntervalSince1970)"
+                    let modificationDate = "\(metadata.date.timeIntervalSince1970)"
+                        
+                    addCustomHeaders["X-OC-CTime"] = creationDate
+                    addCustomHeaders["X-OC-MTime"] = modificationDate
 
-                        NCCommunication.shared.moveFileOrFolder(serverUrlFileNameSource: serverUrlFileNameSource, serverUrlFileNameDestination: serverUrlFileNameDestination, overwrite: true, addCustomHeaders: addCustomHeaders) { (_, errorCode, errorDescription) in
-                                       
-                            NCCommunicationCommon.shared.writeLog("Assembling chunk with error code: \(errorCode)")
-                            
-                            if errorCode == 0 {
-                                                                
-                                let serverUrl = metadata.serverUrl
+                    NCCommunication.shared.moveFileOrFolder(serverUrlFileNameSource: serverUrlFileNameSource, serverUrlFileNameDestination: serverUrlFileNameDestination, overwrite: true, addCustomHeaders: addCustomHeaders) { (_, errorCode, errorDescription) in
+                                   
+                        NCCommunicationCommon.shared.writeLog("Assembling chunk with error code: \(errorCode)")
+                        
+                        if errorCode == 0 {
+                                                            
+                            let serverUrl = metadata.serverUrl
 
-                                NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))
-                                NCManageDatabase.shared.deleteChunks(account: metadata.account, ocId: metadata.ocId)
-                                NCUtilityFileSystem.shared.deleteFile(filePath: directoryProviderStorageOcId)
+                            NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))
+                            NCManageDatabase.shared.deleteChunks(account: metadata.account, ocId: metadata.ocId)
+                            NCUtilityFileSystem.shared.deleteFile(filePath: directoryProviderStorageOcId)
 
-                                self.readFile(serverUrlFileName: serverUrlFileNameDestination, account: metadata.account) { (_, metadata, _, _) in
-                                        
-                                    if errorCode == 0, let metadata = metadata {
-                                        
-                                        NCManageDatabase.shared.addMetadata(metadata)
-                                        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource, userInfo: ["serverUrl":serverUrl])
-                                        
-                                    } else {
-                                        
-                                        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSourceNetworkForced, userInfo: ["serverUrl": serverUrl])
-                                    }
+                            self.readFile(serverUrlFileName: serverUrlFileNameDestination, account: metadata.account) { (_, metadata, _, _) in
+                                    
+                                if errorCode == 0, let metadata = metadata {
                                     
-                                    completion(errorCode, errorDescription)
+                                    NCManageDatabase.shared.addMetadata(metadata)
+                                    NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource, userInfo: ["serverUrl":serverUrl])
+                                    
+                                } else {
+                                    
+                                    NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSourceNetworkForced, userInfo: ["serverUrl": serverUrl])
                                 }
                                 
-                            } else {
-                                
-                                self.uploadChunkFileError(metadata: metadata, chunkFolderPath: chunkFolderPath, directoryProviderStorageOcId: directoryProviderStorageOcId, errorCode: errorCode, errorDescription: errorDescription)
                                 completion(errorCode, errorDescription)
                             }
+                            
+                        } else {
+                            
+                            self.uploadChunkFileError(metadata: metadata, chunkFolderPath: chunkFolderPath, directoryProviderStorageOcId: directoryProviderStorageOcId, errorCode: errorCode, errorDescription: errorDescription)
+                            completion(errorCode, errorDescription)
                         }
-                                                        
-                    } else {
-                                                            
-                        self.uploadChunkFileError(metadata: metadata, chunkFolderPath: chunkFolderPath, directoryProviderStorageOcId: directoryProviderStorageOcId, errorCode: uploadErrorCode, errorDescription: uploadErrorDescription)
-                        completion(errorCode, errorDescription)
                     }
+                                                    
+                } else {
+                                                        
+                    self.uploadChunkFileError(metadata: metadata, chunkFolderPath: chunkFolderPath, directoryProviderStorageOcId: directoryProviderStorageOcId, errorCode: uploadErrorCode, errorDescription: uploadErrorDescription)
+                    completion(errorCode, errorDescription)
                 }
                 
             } else {
@@ -184,12 +181,12 @@ extension NCNetworking {
     
     private func createChunkedFolder(chunkFolderPath: String, account: String, completion: @escaping (_ errorCode: Int, _ errorDescription: String)->()) {
         
-        NCCommunication.shared.readFileOrFolder(serverUrlFileName: chunkFolderPath, depth: "0", showHiddenFiles: CCUtility.getShowHiddenFiles()) { (_, _, _, errorCode, errorDescription) in
+        NCCommunication.shared.readFileOrFolder(serverUrlFileName: chunkFolderPath, depth: "0", showHiddenFiles: CCUtility.getShowHiddenFiles(), queue: NCCommunicationCommon.shared.backgroundQueue) { (_, _, _, errorCode, errorDescription) in
         
             if errorCode == 0 {
                 completion(0, "")
             } else if errorCode == NCGlobal.shared.errorResourceNotFound {
-                NCCommunication.shared.createFolder(chunkFolderPath) { (_, _, _, errorCode, errorDescription) in
+                NCCommunication.shared.createFolder(chunkFolderPath, queue: NCCommunicationCommon.shared.backgroundQueue) { (_, _, _, errorCode, errorDescription) in
                     completion(errorCode, errorDescription)
                 }
             } else {

+ 37 - 33
iOSClient/Networking/NCOperationQueue.swift

@@ -317,7 +317,7 @@ class NCOperationSynchronization: ConcurrentOperation {
 
                     if (errorCode == 0) && (directory?.etag != files.first?.etag || self.selector == NCGlobal.shared.selectorDownloadAllFile) {
                         
-                        NCCommunication.shared.readFileOrFolder(serverUrlFileName: serverUrl, depth: "1", showHiddenFiles: CCUtility.getShowHiddenFiles()) { (account, files, responseData, errorCode, errorDescription) in
+                        NCCommunication.shared.readFileOrFolder(serverUrlFileName: serverUrl, depth: "1", showHiddenFiles: CCUtility.getShowHiddenFiles(), queue: NCCommunicationCommon.shared.backgroundQueue) { (account, files, responseData, errorCode, errorDescription) in
                             
                             if errorCode == 0 {
                             
@@ -421,26 +421,28 @@ class NCOperationDownloadThumbnail: ConcurrentOperation {
             if FileManager.default.fileExists(atPath: fileNameIconLocalPath) && FileManager.default.fileExists(atPath: fileNamePreviewLocalPath) {
                 etagResource = metadata.etagResource
             }
-            NCCommunication.shared.downloadPreview(fileNamePathOrFileId: fileNamePath, fileNamePreviewLocalPath: fileNamePreviewLocalPath , widthPreview: NCGlobal.shared.sizePreview, heightPreview: NCGlobal.shared.sizePreview, fileNameIconLocalPath: fileNameIconLocalPath, sizeIcon: NCGlobal.shared.sizeIcon, etag: etagResource) { (account, imagePreview, imageIcon, imageOriginal, etag, errorCode, errorDescription) in
+            NCCommunication.shared.downloadPreview(fileNamePathOrFileId: fileNamePath, fileNamePreviewLocalPath: fileNamePreviewLocalPath , widthPreview: NCGlobal.shared.sizePreview, heightPreview: NCGlobal.shared.sizePreview, fileNameIconLocalPath: fileNameIconLocalPath, sizeIcon: NCGlobal.shared.sizeIcon, etag: etagResource, queue: NCCommunicationCommon.shared.backgroundQueue) { (account, imagePreview, imageIcon, imageOriginal, etag, errorCode, errorDescription) in
                 
                 if errorCode == 0 && imageIcon != nil {
                     NCManageDatabase.shared.setMetadataEtagResource(ocId: self.metadata.ocId, etagResource: etag)
-                    if self.metadata.ocId == self.cell?.fileObjectId {
-                        if let filePreviewImageView = self.cell?.filePreviewImageView  {
-                            UIView.transition(with: filePreviewImageView,
-                                duration: 0.75,
-                                options: .transitionCrossDissolve,
-                                animations: { filePreviewImageView.image = imageIcon! },
-                                completion: nil)
-                        }
-                    } else {
-                        if self.view is UICollectionView {
-                            (self.view as? UICollectionView)?.reloadData()
-                        } else if self.view is UITableView{
-                            (self.view as? UITableView)?.reloadData()
+                    DispatchQueue.main.async {
+                        if self.metadata.ocId == self.cell?.fileObjectId {
+                            if let filePreviewImageView = self.cell?.filePreviewImageView  {
+                                UIView.transition(with: filePreviewImageView,
+                                    duration: 0.75,
+                                    options: .transitionCrossDissolve,
+                                    animations: { filePreviewImageView.image = imageIcon! },
+                                    completion: nil)
+                            }
+                        } else {
+                            if self.view is UICollectionView {
+                                (self.view as? UICollectionView)?.reloadData()
+                            } else if self.view is UITableView{
+                                (self.view as? UITableView)?.reloadData()
+                            }
                         }
+                        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDownloadedThumbnail, userInfo: ["ocId": self.metadata.ocId])
                     }
-                    NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDownloadedThumbnail, userInfo: ["ocId": self.metadata.ocId])
                 }
                 self.finish()
             }
@@ -474,29 +476,31 @@ class NCOperationDownloadAvatar: ConcurrentOperation {
         if isCancelled {
             self.finish()
         } else {
-            NCCommunication.shared.downloadAvatar(user: user, fileNameLocalPath: fileNameLocalPath, sizeImage: NCGlobal.shared.avatarSize, avatarSizeRounded: NCGlobal.shared.avatarSizeRounded, etag: self.etag) { (account, imageAvatar, imageOriginal, etag, errorCode, errorMessage) in
+            NCCommunication.shared.downloadAvatar(user: user, fileNameLocalPath: fileNameLocalPath, sizeImage: NCGlobal.shared.avatarSize, avatarSizeRounded: NCGlobal.shared.avatarSizeRounded, etag: self.etag, queue: NCCommunicationCommon.shared.backgroundQueue) { (account, imageAvatar, imageOriginal, etag, errorCode, errorMessage) in
                 
                 if errorCode == 0, let imageAvatar = imageAvatar, let etag = etag {
                     
                     NCManageDatabase.shared.addAvatar(fileName: self.fileName, etag: etag)
-                    
-                    if self.user == self.cell.fileUser {
-                        if let avatarImageView = self.cell?.fileAvatarImageView  {
-                            UIView.transition(with: avatarImageView, duration: 0.75, options: .transitionCrossDissolve) {
-                                avatarImageView.image = imageAvatar
-                            } completion: { _ in
-                                if self.view is UICollectionView {
-                                    (self.view as? UICollectionView)?.reloadData()
-                                } else if self.view is UITableView{
-                                    (self.view as? UITableView)?.reloadData()
+
+                    DispatchQueue.main.async {
+                        if self.user == self.cell.fileUser {
+                            if let avatarImageView = self.cell?.fileAvatarImageView  {
+                                UIView.transition(with: avatarImageView, duration: 0.75, options: .transitionCrossDissolve) {
+                                    avatarImageView.image = imageAvatar
+                                } completion: { _ in
+                                    if self.view is UICollectionView {
+                                        (self.view as? UICollectionView)?.reloadData()
+                                    } else if self.view is UITableView{
+                                        (self.view as? UITableView)?.reloadData()
+                                    }
                                 }
                             }
-                        }
-                    } else {
-                        if self.view is UICollectionView {
-                            (self.view as? UICollectionView)?.reloadData()
-                        } else if self.view is UITableView{
-                            (self.view as? UITableView)?.reloadData()
+                        } else {
+                            if self.view is UICollectionView {
+                                (self.view as? UICollectionView)?.reloadData()
+                            } else if self.view is UITableView{
+                                (self.view as? UITableView)?.reloadData()
+                            }
                         }
                     }
                     

+ 121 - 146
iOSClient/Networking/NCService.swift

@@ -83,57 +83,54 @@ class NCService: NSObject {
         
         if appDelegate.account == "" { return }
         
-        NCCommunication.shared.getUserProfile() { (account, userProfile, errorCode, errorDescription) in
+        NCCommunication.shared.getUserProfile(queue: NCCommunicationCommon.shared.backgroundQueue) { (account, userProfile, errorCode, errorDescription) in
                
             if errorCode == 0 && account == self.appDelegate.account {
                   
-                DispatchQueue.global().async {
-                
-                    // Update User (+ userProfile.id) & active account & account network
-                    guard let tableAccount = NCManageDatabase.shared.setAccountUserProfile(userProfile!) else {
-                        NCContentPresenter.shared.messageNotification("Account", description: "Internal error : account not found on DB",  delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: NCGlobal.shared.errorInternalError)
-                        return
-                    }
+                // Update User (+ userProfile.id) & active account & account network
+                guard let tableAccount = NCManageDatabase.shared.setAccountUserProfile(userProfile!) else {
+                    NCContentPresenter.shared.messageNotification("Account", description: "Internal error : account not found on DB",  delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: NCGlobal.shared.errorInternalError)
+                    return
+                }
+            
+                let user = tableAccount.user
+                let url = tableAccount.urlBase
                 
-                    let user = tableAccount.user
-                    let url = tableAccount.urlBase
-                    
-                    self.appDelegate.settingAccount(tableAccount.account, urlBase: tableAccount.urlBase, user: tableAccount.user, userId: tableAccount.userId, password: CCUtility.getPassword(tableAccount.account))
-                       
-                    // Synchronize favorite                    
-                    NCNetworking.shared.listingFavoritescompletion(selector: NCGlobal.shared.selectorReadFile) { (_, _, _, _) in }
+                self.appDelegate.settingAccount(tableAccount.account, urlBase: tableAccount.urlBase, user: tableAccount.user, userId: tableAccount.userId, password: CCUtility.getPassword(tableAccount.account))
+                   
+                // Synchronize favorite
+                NCNetworking.shared.listingFavoritescompletion(selector: NCGlobal.shared.selectorReadFile) { (_, _, _, _) in }
+            
+                // Synchronize Offline
+                self.synchronizeOffline(account: tableAccount.account)
                 
-                    // Synchronize Offline
-                    self.synchronizeOffline(account: tableAccount.account)
-                    
-                    // Get Avatar
-                    let fileName = String(CCUtility.getUserUrlBase(user, urlBase: url)) + "-" + self.appDelegate.user + ".png"
-                    let fileNameLocalPath = String(CCUtility.getDirectoryUserData()) + "/" + fileName
-                    let etag = NCManageDatabase.shared.getTableAvatar(fileName: fileName)?.etag
+                // Get Avatar
+                let fileName = String(CCUtility.getUserUrlBase(user, urlBase: url)) + "-" + self.appDelegate.user + ".png"
+                let fileNameLocalPath = String(CCUtility.getDirectoryUserData()) + "/" + fileName
+                let etag = NCManageDatabase.shared.getTableAvatar(fileName: fileName)?.etag
 
-                    NCCommunication.shared.downloadAvatar(user: tableAccount.userId, fileNameLocalPath: fileNameLocalPath, sizeImage: NCGlobal.shared.avatarSize, avatarSizeRounded: NCGlobal.shared.avatarSizeRounded, etag: etag) { (account, image, imageOriginal, etag, errorCode, errorMessage) in
+                NCCommunication.shared.downloadAvatar(user: tableAccount.userId, fileNameLocalPath: fileNameLocalPath, sizeImage: NCGlobal.shared.avatarSize, avatarSizeRounded: NCGlobal.shared.avatarSizeRounded, etag: etag, queue: NCCommunicationCommon.shared.backgroundQueue) { (account, image, imageOriginal, etag, errorCode, errorMessage) in
 
-                        if let etag = etag, errorCode == 0, let imageOriginal = imageOriginal {
-                            
-                            do {
-                                if let pngData = imageOriginal.pngData() {
-                                    let fileName = String(CCUtility.getUserUrlBase(user, urlBase: url)) + "-" + self.appDelegate.user + "-original.png"
-                                    let fileNameLocalPath = String(CCUtility.getDirectoryUserData()) + "/" + fileName
-                                    let url = URL.init(fileURLWithPath: fileNameLocalPath)
-                                    try pngData.write(to: url)
-                                }
-                            } catch {}
-                            
-                            NCManageDatabase.shared.addAvatar(fileName: fileName, etag: etag)
-                            NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadAvatar, userInfo: nil)
-                            
-                        } else if errorCode == NCGlobal.shared.errorNotModified {
-                            
-                            NCManageDatabase.shared.setAvatarLoaded(fileName: fileName)
-                        }
+                    if let etag = etag, errorCode == 0, let imageOriginal = imageOriginal {
+                        
+                        do {
+                            if let pngData = imageOriginal.pngData() {
+                                let fileName = String(CCUtility.getUserUrlBase(user, urlBase: url)) + "-" + self.appDelegate.user + "-original.png"
+                                let fileNameLocalPath = String(CCUtility.getDirectoryUserData()) + "/" + fileName
+                                let url = URL.init(fileURLWithPath: fileNameLocalPath)
+                                try pngData.write(to: url)
+                            }
+                        } catch {}
+                        
+                        NCManageDatabase.shared.addAvatar(fileName: fileName, etag: etag)
+                        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadAvatar, userInfo: nil)
+                        
+                    } else if errorCode == NCGlobal.shared.errorNotModified {
+                        
+                        NCManageDatabase.shared.setAvatarLoaded(fileName: fileName)
                     }
-                    self.requestServerCapabilities()
                 }
+                self.requestServerCapabilities()
                 
             } else {
                 
@@ -146,19 +143,14 @@ class NCService: NSObject {
     
     private func requestServerStatus() {
         
-        NCCommunication.shared.getServerStatus(serverUrl: appDelegate.urlBase) { (serverProductName, serverVersion, versionMajor, versionMinor, versionMicro, extendedSupport, errorCode, errorMessage) in
+        NCCommunication.shared.getServerStatus(serverUrl: appDelegate.urlBase, queue: NCCommunicationCommon.shared.backgroundQueue) { (serverProductName, serverVersion, versionMajor, versionMinor, versionMicro, extendedSupport, errorCode, errorMessage) in
                         
-            if errorCode == 0 {
-                
-                DispatchQueue.global().async {
-                    
-                    if extendedSupport == false {
-                        if serverProductName == "owncloud" {
-                            NCContentPresenter.shared.messageNotification("_warning_", description: "_warning_owncloud_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.info, errorCode: NCGlobal.shared.errorInternalError)
-                        } else if versionMajor <=  NCGlobal.shared.nextcloud_unsupported_version {
-                            NCContentPresenter.shared.messageNotification("_warning_", description: "_warning_unsupported_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.info, errorCode: NCGlobal.shared.errorInternalError)
-                        }
-                    }
+            if errorCode == 0 && extendedSupport == false {
+                                
+                if serverProductName == "owncloud" {
+                    NCContentPresenter.shared.messageNotification("_warning_", description: "_warning_owncloud_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.info, errorCode: NCGlobal.shared.errorInternalError)
+                } else if versionMajor <=  NCGlobal.shared.nextcloud_unsupported_version {
+                    NCContentPresenter.shared.messageNotification("_warning_", description: "_warning_unsupported_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.info, errorCode: NCGlobal.shared.errorInternalError)
                 }
             }
         }
@@ -168,119 +160,102 @@ class NCService: NSObject {
         
         if appDelegate.account == "" { return }
         
-        NCCommunication.shared.getCapabilities() { (account, data, errorCode, errorDescription) in
+        NCCommunication.shared.getCapabilities(queue: NCCommunicationCommon.shared.backgroundQueue) { (account, data, errorCode, errorDescription) in
             
             if errorCode == 0 && data != nil {
                 
-                DispatchQueue.global().async {
-                
-                    NCManageDatabase.shared.addCapabilitiesJSon(data!, account: account)
+                NCManageDatabase.shared.addCapabilitiesJSon(data!, account: account)
+            
+                let serverVersionMajor = NCManageDatabase.shared.getCapabilitiesServerInt(account: account, elements: NCElementsJSON.shared.capabilitiesVersionMajor)
                 
-                    let serverVersionMajor = NCManageDatabase.shared.getCapabilitiesServerInt(account: account, elements: NCElementsJSON.shared.capabilitiesVersionMajor)
-                    
-                    // Setup communication
-                    if serverVersionMajor > 0 {
-                        NCCommunicationCommon.shared.setup(nextcloudVersion: serverVersionMajor)
-                    }
-                    NCCommunicationCommon.shared.setup(webDav: NCUtilityFileSystem.shared.getWebDAV(account: account))
-                    
-                    // Theming
-                    NCBrandColor.shared.settingThemingColor(account: account)
+                // Setup communication
+                if serverVersionMajor > 0 {
+                    NCCommunicationCommon.shared.setup(nextcloudVersion: serverVersionMajor)
+                }
+                NCCommunicationCommon.shared.setup(webDav: NCUtilityFileSystem.shared.getWebDAV(account: account))
                 
-                    // File Sharing
-                    let isFilesSharingEnabled = NCManageDatabase.shared.getCapabilitiesServerBool(account: account, elements: NCElementsJSON.shared.capabilitiesFileSharingApiEnabled, exists: false)
-                    if isFilesSharingEnabled {
-                        NCCommunication.shared.readShares { (account, shares, errorCode, ErrorDescription) in
-                            if errorCode == 0 {
-                                
-                                NCManageDatabase.shared.deleteTableShare(account: account)
-                                if shares != nil {
-                                    NCManageDatabase.shared.addShare(urlBase: self.appDelegate.urlBase, account: account, shares: shares!)
-                                }
-                                self.appDelegate.shares = NCManageDatabase.shared.getTableShares(account: account)
-                               
-                            } else {
-                                NCContentPresenter.shared.messageNotification("_share_", description: ErrorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode)
+                // Theming
+                NCBrandColor.shared.settingThemingColor(account: account)
+            
+                // File Sharing
+                let isFilesSharingEnabled = NCManageDatabase.shared.getCapabilitiesServerBool(account: account, elements: NCElementsJSON.shared.capabilitiesFileSharingApiEnabled, exists: false)
+                if isFilesSharingEnabled {
+                    NCCommunication.shared.readShares(queue: NCCommunicationCommon.shared.backgroundQueue) { (account, shares, errorCode, ErrorDescription) in
+                        if errorCode == 0 {
+                            NCManageDatabase.shared.deleteTableShare(account: account)
+                            if shares != nil {
+                                NCManageDatabase.shared.addShare(urlBase: self.appDelegate.urlBase, account: account, shares: shares!)
                             }
+                            self.appDelegate.shares = NCManageDatabase.shared.getTableShares(account: account)
+                        } else {
+                            NCContentPresenter.shared.messageNotification("_share_", description: ErrorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode)
                         }
                     }
-                    
-                    let comments = NCManageDatabase.shared.getCapabilitiesServerBool(account: account, elements: NCElementsJSON.shared.capabilitiesFilesComments, exists: false)
-                    let activity = NCManageDatabase.shared.getCapabilitiesServerArray(account: account, elements: NCElementsJSON.shared.capabilitiesActivity)
-                    
-                    if !isFilesSharingEnabled && !comments && activity == nil {
-                        self.appDelegate.disableSharesView = true
-                    } else {
-                        self.appDelegate.disableSharesView = false
-                    }
+                }
                 
-                    // Text direct editor detail
-                    if serverVersionMajor >= NCGlobal.shared.nextcloudVersion18 {
-                        NCCommunication.shared.NCTextObtainEditorDetails() { (account, editors, creators, errorCode, errorMessage) in
-                            if errorCode == 0 && account == self.appDelegate.account {
-                                
-                                DispatchQueue.global().async {
-                                
-                                    NCManageDatabase.shared.addDirectEditing(account: account, editors: editors, creators: creators)
-                                }
-                            }
+                let comments = NCManageDatabase.shared.getCapabilitiesServerBool(account: account, elements: NCElementsJSON.shared.capabilitiesFilesComments, exists: false)
+                let activity = NCManageDatabase.shared.getCapabilitiesServerArray(account: account, elements: NCElementsJSON.shared.capabilitiesActivity)
+                
+                if !isFilesSharingEnabled && !comments && activity == nil {
+                    self.appDelegate.disableSharesView = true
+                } else {
+                    self.appDelegate.disableSharesView = false
+                }
+            
+                // Text direct editor detail
+                if serverVersionMajor >= NCGlobal.shared.nextcloudVersion18 {
+                    NCCommunication.shared.NCTextObtainEditorDetails(queue: NCCommunicationCommon.shared.backgroundQueue) { (account, editors, creators, errorCode, errorMessage) in
+                        if errorCode == 0 && account == self.appDelegate.account {
+                            NCManageDatabase.shared.addDirectEditing(account: account, editors: editors, creators: creators)
                         }
                     }
-                    
-                    // External file Server
-                    let isExternalSitesServerEnabled = NCManageDatabase.shared.getCapabilitiesServerBool(account: account, elements: NCElementsJSON.shared.capabilitiesExternalSitesExists, exists: true)
-                    if (isExternalSitesServerEnabled) {
-                        NCCommunication.shared.getExternalSite() { (account, externalSites, errorCode, errorDescription) in
-                            if errorCode == 0 && account == self.appDelegate.account {
-                                
-                                DispatchQueue.global().async {
-                                
-                                    NCManageDatabase.shared.deleteExternalSites(account: account)
-                                    for externalSite in externalSites {
-                                        NCManageDatabase.shared.addExternalSites(externalSite, account: account)
-                                    }
-                                }
+                }
+                
+                // External file Server
+                let isExternalSitesServerEnabled = NCManageDatabase.shared.getCapabilitiesServerBool(account: account, elements: NCElementsJSON.shared.capabilitiesExternalSitesExists, exists: true)
+                if (isExternalSitesServerEnabled) {
+                    NCCommunication.shared.getExternalSite(queue: NCCommunicationCommon.shared.backgroundQueue) { (account, externalSites, errorCode, errorDescription) in
+                        if errorCode == 0 && account == self.appDelegate.account {
+                            NCManageDatabase.shared.deleteExternalSites(account: account)
+                            for externalSite in externalSites {
+                                NCManageDatabase.shared.addExternalSites(externalSite, account: account)
                             }
                         }
-                        
-                    } else {
-                        NCManageDatabase.shared.deleteExternalSites(account: account)
                     }
                     
-                    // User Status
-                    let userStatus = NCManageDatabase.shared.getCapabilitiesServerBool(account: account, elements: NCElementsJSON.shared.capabilitiesUserStatusEnabled, exists: false)
-                    if userStatus {
-                        NCCommunication.shared.getUserStatus { (account, clearAt, icon, message, messageId, messageIsPredefined, status, statusIsUserDefined, userId, errorCode, errorDescription) in
-                            if errorCode == 0 && account == self.appDelegate.account && userId == self.appDelegate.userId {
-                                
-                                DispatchQueue.global().async {
-                                
-                                    NCManageDatabase.shared.setAccountUserStatus(userStatusClearAt: clearAt, userStatusIcon: icon, userStatusMessage: message, userStatusMessageId: messageId, userStatusMessageIsPredefined: messageIsPredefined, userStatusStatus: status, userStatusStatusIsUserDefined: statusIsUserDefined, account: account)
-                                }
-                            }
+                } else {
+                    NCManageDatabase.shared.deleteExternalSites(account: account)
+                }
+                
+                // User Status
+                let userStatus = NCManageDatabase.shared.getCapabilitiesServerBool(account: account, elements: NCElementsJSON.shared.capabilitiesUserStatusEnabled, exists: false)
+                if userStatus {
+                    NCCommunication.shared.getUserStatus(queue: NCCommunicationCommon.shared.backgroundQueue) { (account, clearAt, icon, message, messageId, messageIsPredefined, status, statusIsUserDefined, userId, errorCode, errorDescription) in
+                        if errorCode == 0 && account == self.appDelegate.account && userId == self.appDelegate.userId {
+                            NCManageDatabase.shared.setAccountUserStatus(userStatusClearAt: clearAt, userStatusIcon: icon, userStatusMessage: message, userStatusMessageId: messageId, userStatusMessageIsPredefined: messageIsPredefined, userStatusStatus: status, userStatusStatusIsUserDefined: statusIsUserDefined, account: account)
                         }
                     }
+                }
 
-                    // Added UTI for Collabora
-                    if let richdocumentsMimetypes = NCManageDatabase.shared.getCapabilitiesServerArray(account: account, elements: NCElementsJSON.shared.capabilitiesRichdocumentsMimetypes) {
-                        for mimeType in richdocumentsMimetypes {
-                            NCCommunicationCommon.shared.addInternalTypeIdentifier(typeIdentifier: mimeType, classFile:  NCCommunicationCommon.typeClassFile.document.rawValue, editor: NCGlobal.shared.editorCollabora, iconName: NCCommunicationCommon.typeIconFile.document.rawValue, name: "document")
-                        }
+                // Added UTI for Collabora
+                if let richdocumentsMimetypes = NCManageDatabase.shared.getCapabilitiesServerArray(account: account, elements: NCElementsJSON.shared.capabilitiesRichdocumentsMimetypes) {
+                    for mimeType in richdocumentsMimetypes {
+                        NCCommunicationCommon.shared.addInternalTypeIdentifier(typeIdentifier: mimeType, classFile:  NCCommunicationCommon.typeClassFile.document.rawValue, editor: NCGlobal.shared.editorCollabora, iconName: NCCommunicationCommon.typeIconFile.document.rawValue, name: "document")
                     }
-                    
-                    // Added UTI for ONLYOFFICE & Text
-                    if let directEditingCreators = NCManageDatabase.shared.getDirectEditingCreators(account: account) {
-                        for directEditing in directEditingCreators {
-                            NCCommunicationCommon.shared.addInternalTypeIdentifier(typeIdentifier: directEditing.mimetype, classFile:  NCCommunicationCommon.typeClassFile.document.rawValue, editor: directEditing.editor, iconName: NCCommunicationCommon.typeIconFile.document.rawValue, name: "document")
-                        }
+                }
+                
+                // Added UTI for ONLYOFFICE & Text
+                if let directEditingCreators = NCManageDatabase.shared.getDirectEditingCreators(account: account) {
+                    for directEditing in directEditingCreators {
+                        NCCommunicationCommon.shared.addInternalTypeIdentifier(typeIdentifier: directEditing.mimetype, classFile:  NCCommunicationCommon.typeClassFile.document.rawValue, editor: directEditing.editor, iconName: NCCommunicationCommon.typeIconFile.document.rawValue, name: "document")
                     }
-
-                    //                    Handwerkcloud
-                    //                    let isHandwerkcloudEnabled = NCManageDatabase.shared.getCapabilitiesServerBool(account: account, elements: NCElementsJSON.shared.capabilitiesHWCEnabled, exists: false)
-                    //                    if (isHandwerkcloudEnabled) {
-                    //                        self.requestHC()
-                    //                    }
                 }
+
+                //                    Handwerkcloud
+                //                    let isHandwerkcloudEnabled = NCManageDatabase.shared.getCapabilitiesServerBool(account: account, elements: NCElementsJSON.shared.capabilitiesHWCEnabled, exists: false)
+                //                    if (isHandwerkcloudEnabled) {
+                //                        self.requestHC()
+                //                    }
                 
             } else if errorCode != 0 {
                 

+ 9 - 7
iOSClient/Offline/NCOffline.swift

@@ -108,13 +108,15 @@ class NCOffline: NCCollectionViewCommon  {
                     }
                 }
                 
-                self.refreshControl.endRefreshing()
-                self.isReloadDataSourceNetworkInProgress = false
-                self.richWorkspaceText = tableDirectory?.richWorkspace
-                if metadatasUpdate?.count ?? 0 > 0 || metadatasDelete?.count ?? 0 > 0 || forced {
-                    self.reloadDataSource()
-                } else {
-                    self.collectionView?.reloadData()
+                DispatchQueue.main.async {
+                    self.refreshControl.endRefreshing()
+                    self.isReloadDataSourceNetworkInProgress = false
+                    self.richWorkspaceText = tableDirectory?.richWorkspace
+                    if metadatasUpdate?.count ?? 0 > 0 || metadatasDelete?.count ?? 0 > 0 || forced {
+                        self.reloadDataSource()
+                    } else {
+                        self.collectionView?.reloadData()
+                    }
                 }
             }
         }

+ 4 - 4
iOSClient/PushNotification/NCPushNotification.m

@@ -106,10 +106,10 @@
     NSString *pushDevicePublicKey = [[NSString alloc] initWithData:pushPublicKey encoding:NSUTF8StringEncoding];
     NSString *proxyServerPath = [NCBrandOptions shared].pushNotificationServerProxy;
     
-    [[NCCommunication shared] subscribingPushNotificationWithServerUrl:urlBase account:account user:user password:[CCUtility getPassword:account] pushTokenHash:pushTokenHash devicePublicKey:pushDevicePublicKey proxyServerUrl:proxyServerPath customUserAgent:nil addCustomHeaders:nil completionHandler:^(NSString *account, NSString *deviceIdentifier, NSString *signature, NSString *publicKey, NSInteger errorCode, NSString *errorDescription) {
+    [[NCCommunication shared] subscribingPushNotificationWithServerUrl:urlBase account:account user:user password:[CCUtility getPassword:account] pushTokenHash:pushTokenHash devicePublicKey:pushDevicePublicKey proxyServerUrl:proxyServerPath customUserAgent:nil addCustomHeaders:nil queue:dispatch_get_main_queue() completionHandler:^(NSString *account, NSString *deviceIdentifier, NSString *signature, NSString *publicKey, NSInteger errorCode, NSString *errorDescription) {
         if (errorCode == 0) {
             NSString *userAgent = [NSString stringWithFormat:@"%@  (Strict VoIP)", [CCUtility getUserAgent]];
-            [[NCCommunication shared] subscribingPushProxyWithProxyServerUrl:proxyServerPath pushToken:self.pushKitToken deviceIdentifier:deviceIdentifier signature:signature publicKey:publicKey userAgent:userAgent completionHandler:^(NSInteger errorCode, NSString *errorDescription) {
+            [[NCCommunication shared] subscribingPushProxyWithProxyServerUrl:proxyServerPath pushToken:self.pushKitToken deviceIdentifier:deviceIdentifier signature:signature publicKey:publicKey userAgent:userAgent queue:dispatch_get_main_queue() completionHandler:^(NSInteger errorCode, NSString *errorDescription) {
                 if (errorCode == 0) {
                     
                     [[NCCommunicationCommon shared] writeLog:@"Subscribed to Push Notification server & proxy successfully"];
@@ -132,11 +132,11 @@
     NSString *signature = [CCUtility getPushNotificationDeviceIdentifierSignature:account];
     NSString *publicKey = [CCUtility getPushNotificationSubscribingPublicKey:account];
 
-    [[NCCommunication shared] unsubscribingPushNotificationWithServerUrl:urlBase account:account user:user password:[CCUtility getPassword:account] customUserAgent:nil addCustomHeaders:nil completionHandler:^(NSString *account, NSInteger errorCode, NSString *errorDescription) {
+    [[NCCommunication shared] unsubscribingPushNotificationWithServerUrl:urlBase account:account user:user password:[CCUtility getPassword:account] customUserAgent:nil addCustomHeaders:nil queue:dispatch_get_main_queue() completionHandler:^(NSString *account, NSInteger errorCode, NSString *errorDescription) {
         if (errorCode == 0) {
             NSString *userAgent = [NSString stringWithFormat:@"%@  (Strict VoIP)", [CCUtility getUserAgent]];
             NSString *proxyServerPath = [NCBrandOptions shared].pushNotificationServerProxy;
-            [[NCCommunication shared] unsubscribingPushProxyWithProxyServerUrl:proxyServerPath deviceIdentifier:deviceIdentifier signature:signature publicKey:publicKey userAgent:userAgent completionHandler:^(NSInteger errorCode, NSString *errorDescription) {
+            [[NCCommunication shared] unsubscribingPushProxyWithProxyServerUrl:proxyServerPath deviceIdentifier:deviceIdentifier signature:signature publicKey:publicKey userAgent:userAgent queue:dispatch_get_main_queue() completionHandler:^(NSInteger errorCode, NSString *errorDescription) {
                 if (errorCode == 0) {
                 
                     [[NCCommunicationCommon shared] writeLog:@"Unsubscribed to Push Notification server & proxy successfully."];

+ 9 - 5
iOSClient/Recent/NCRecent.swift

@@ -141,10 +141,7 @@ class NCRecent: NCCollectionViewCommon  {
         isReloadDataSourceNetworkInProgress = true
         collectionView?.reloadData()
         
-        NCCommunication.shared.searchBodyRequest(serverUrl: appDelegate.urlBase, requestBody: requestBody, showHiddenFiles: CCUtility.getShowHiddenFiles()) { (account, files, errorCode, errorDescription) in
-            
-            self.refreshControl.endRefreshing()
-            self.isReloadDataSourceNetworkInProgress = false
+        NCCommunication.shared.searchBodyRequest(serverUrl: appDelegate.urlBase, requestBody: requestBody, showHiddenFiles: CCUtility.getShowHiddenFiles(), queue: NCCommunicationCommon.shared.backgroundQueue) { (account, files, errorCode, errorDescription) in
             
             if errorCode == 0 {
                 NCManageDatabase.shared.convertNCCommunicationFilesToMetadatas(files, useMetadataFolder: false, account: account) { (metadataFolder, metadatasFolder, metadatas) in
@@ -160,7 +157,14 @@ class NCRecent: NCCollectionViewCommon  {
                     self.reloadDataSource()
                 }
             } else {
-                self.collectionView?.reloadData()
+                DispatchQueue.main.async {
+                    self.collectionView?.reloadData()
+                }
+            }
+            
+            DispatchQueue.main.async {
+                self.refreshControl.endRefreshing()
+                self.isReloadDataSourceNetworkInProgress = false
             }
         }
     }

+ 5 - 5
iOSClient/Rename file/NCRenameFile.swift

@@ -66,11 +66,11 @@ class NCRenameFile: UIViewController, UITextFieldDelegate {
                 titleLabel.text = NSLocalizedString("_rename_file_", comment: "")
             }
             
-            fileNameWithoutExt.text = metadata.fileNameWithoutExt
+            fileNameWithoutExt.text = (metadata.fileNameView as NSString).deletingPathExtension
             fileNameWithoutExt.delegate = self
             fileNameWithoutExt.becomeFirstResponder()
             
-            ext.text = metadata.ext
+            ext.text = (metadata.fileNameView as NSString).pathExtension
             ext.delegate = self
             if disableChangeExt {
                 ext.isEnabled = false
@@ -162,7 +162,7 @@ class NCRenameFile: UIViewController, UITextFieldDelegate {
         if let metadata = self.metadata {
         
             if fileNameWithoutExt.text == nil || fileNameWithoutExt.text?.count == 0 {
-                self.fileNameWithoutExt.text = metadata.fileNameWithoutExt
+                self.fileNameWithoutExt.text = (metadata.fileNameView as NSString).deletingPathExtension
                 return
             } else {
                 fileNameWithoutExtNew = fileNameWithoutExt.text!
@@ -176,7 +176,7 @@ class NCRenameFile: UIViewController, UITextFieldDelegate {
             } else {
                 
                 if ext.text == nil || ext.text?.count == 0 {
-                    self.ext.text = metadata.ext
+                    self.ext.text = (metadata.fileNameView as NSString).pathExtension
                     return
                 } else {
                     extNew = ext.text!
@@ -196,7 +196,7 @@ class NCRenameFile: UIViewController, UITextFieldDelegate {
                     
                     title = NSLocalizedString("_keep_", comment: "") + " ." + metadata.ext
                     alertController.addAction(UIAlertAction(title: title, style: .default, handler: { action in
-                        self.ext.text = metadata.ext
+                        self.ext.text = (metadata.fileNameView as NSString).pathExtension
                     }))
                     
                     self.present(alertController, animated: true)

+ 1 - 0
iOSClient/Security/NCEndToEndMetadata.swift

@@ -217,6 +217,7 @@ class NCEndToEndMetadata : NSObject  {
                         
                         // Update metadata on tableMetadata
                         metadata.fileNameView = encryptedFileAttributes.filename
+                        metadata.fileNameWithoutExt = (encryptedFileAttributes.filename as NSString).deletingPathExtension
                         
                         let results = NCCommunicationCommon.shared.getInternalType(fileName: encryptedFileAttributes.filename, mimeType: metadata.contentType, directory: metadata.directory)
                         

+ 3 - 1
iOSClient/Select/NCSelect.swift

@@ -702,7 +702,9 @@ extension NCSelect {
         let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", activeAccount.account,serverUrl))
         richWorkspaceText = directory?.richWorkspace
         
-        collectionView.reloadData()
+        DispatchQueue.main.async {
+            self.collectionView.reloadData()
+        }
     }
     
     func createFolder(with fileName: String) {

+ 2 - 2
iOSClient/Settings/NCManageEndToEndEncryption.m

@@ -371,7 +371,7 @@
 {
     [self deselectFormRow:sender];
     
-    [[NCCommunication shared] deleteE2EECertificateWithCustomUserAgent:nil addCustomHeaders:nil completionHandler:^(NSString *account, NSInteger errorCode, NSString *errorDescription) {
+    [[NCCommunication shared] deleteE2EECertificateWithCustomUserAgent:nil addCustomHeaders:nil queue:dispatch_get_main_queue() completionHandler:^(NSString *account, NSInteger errorCode, NSString *errorDescription) {
        if (errorCode == 0 && [account isEqualToString:appDelegate.account]) {
             [[NCContentPresenter shared] messageNotification:@"E2E delete certificate" description:@"Success" delay:[[NCGlobal shared] dismissAfterSecond] type:messageTypeSuccess errorCode:NCGlobal.shared.errorInternalError forced:true];
         } else {
@@ -384,7 +384,7 @@
 {
     [self deselectFormRow:sender];
     
-    [[NCCommunication shared] deleteE2EEPrivateKeyWithCustomUserAgent:nil addCustomHeaders:nil completionHandler:^(NSString *account, NSInteger errorCode, NSString *errorDescription) {
+    [[NCCommunication shared] deleteE2EEPrivateKeyWithCustomUserAgent:nil addCustomHeaders:nil queue:dispatch_get_main_queue() completionHandler:^(NSString *account, NSInteger errorCode, NSString *errorDescription) {
         if (errorCode == 0 && [account isEqualToString:appDelegate.account]) {
             [[NCContentPresenter shared] messageNotification:@"E2E delete privateKey" description:@"Success" delay:[[NCGlobal shared] dismissAfterSecond] type:messageTypeSuccess errorCode:NCGlobal.shared.errorInternalError forced:true];
         } else {

+ 10 - 7
iOSClient/Shares/NCShares.swift

@@ -74,11 +74,13 @@ class NCShares: NCCollectionViewCommon  {
         collectionView?.reloadData()
                     
         // Shares network
-        NCCommunication.shared.readShares { (account, shares, errorCode, ErrorDescription) in
-                
-            self.refreshControl.endRefreshing()
-            self.isReloadDataSourceNetworkInProgress = false
+        NCCommunication.shared.readShares(queue: NCCommunicationCommon.shared.backgroundQueue) { (account, shares, errorCode, ErrorDescription) in
                 
+            DispatchQueue.main.async {
+                self.refreshControl.endRefreshing()
+                self.isReloadDataSourceNetworkInProgress = false
+            }
+            
             if errorCode == 0 {
                     
                 NCManageDatabase.shared.deleteTableShare(account: account)
@@ -86,13 +88,14 @@ class NCShares: NCCollectionViewCommon  {
                     NCManageDatabase.shared.addShare(urlBase: self.appDelegate.urlBase, account: account, shares: shares!)
                 }
                 self.appDelegate.shares = NCManageDatabase.shared.getTableShares(account: account)
-                    
                 self.reloadDataSource()
                     
             } else {
                     
-                self.collectionView?.reloadData()
-                NCContentPresenter.shared.messageNotification("_share_", description: ErrorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode)
+                DispatchQueue.main.async {
+                    self.collectionView?.reloadData()
+                    NCContentPresenter.shared.messageNotification("_share_", description: ErrorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode)
+                }
             }
         }
     }

+ 5 - 3
iOSClient/Trash/NCTrash.swift

@@ -416,8 +416,7 @@ extension NCTrash {
     
     @objc func loadListingTrash() {
         
-        NCCommunication.shared.listingTrash(showHiddenFiles: false) { (account, items, errorCode, errorDescription) in
-            self.refreshControl.endRefreshing()
+        NCCommunication.shared.listingTrash(showHiddenFiles: false, queue: NCCommunicationCommon.shared.backgroundQueue) { (account, items, errorCode, errorDescription) in
          
             if errorCode == 0 && account == self.appDelegate.account {
                 NCManageDatabase.shared.deleteTrash(filePath: self.trashPath, account: self.appDelegate.account)
@@ -428,7 +427,10 @@ extension NCTrash {
                 print("[LOG] It has been changed user during networking process, error.")
             }
             
-            self.reloadDataSource()
+            DispatchQueue.main.async {
+                self.refreshControl.endRefreshing()
+                self.reloadDataSource()
+            }
         }
     }
     

+ 2 - 3
iOSClient/Utility/NCUtility.swift

@@ -570,11 +570,10 @@ class NCUtility: NSObject {
             stopActivityIndicator()
         }
         
-        self.activityIndicator = UIActivityIndicatorView(style: style)
-        guard let activityIndicator = self.activityIndicator else { return }
-        
         DispatchQueue.main.async {
             
+            self.activityIndicator = UIActivityIndicatorView(style: style)
+            guard let activityIndicator = self.activityIndicator else { return }
             if self.viewBackgroundActivityIndicator != nil { return }
             
             activityIndicator.color = NCBrandColor.shared.label

+ 1 - 1
iOSClient/Viewer/NCViewer.swift

@@ -48,7 +48,7 @@ class NCViewer: NSObject {
             
             if let navigationController = viewController.navigationController {
                             
-                let viewerMediaPageContainer:NCViewerMedia = UIStoryboard(name: "NCViewerMedia", bundle: nil).instantiateInitialViewController() as! NCViewerMedia
+                let viewerMediaPageContainer:NCViewerMediaPage = UIStoryboard(name: "NCViewerMediaPage", bundle: nil).instantiateInitialViewController() as! NCViewerMediaPage
                 var index = 0
                 for medatasImage in metadatas {
                     if medatasImage.ocId == metadata.ocId {

+ 11 - 11
iOSClient/Viewer/NCViewerMedia/NCPlayer/NCKTVHTTPCache.swift

@@ -45,7 +45,16 @@ class NCKTVHTTPCache: NSObject {
         }
     }
     
-    func startProxy(user: String, password: String) {
+    func restartProxy(user: String, password: String) {
+                
+        if KTVHTTPCache.proxyIsRunning() {
+            KTVHTTPCache.proxyStop()
+        }
+        
+        startProxy(user: user, password: password)
+    }
+    
+    private func startProxy(user: String, password: String) {
         
         guard let authData = (user + ":" + password).data(using: .utf8) else { return }
         
@@ -61,22 +70,13 @@ class NCKTVHTTPCache: NSObject {
         }        
     }
     
-    func stopProxy() {
+    private func stopProxy() {
         
         if KTVHTTPCache.proxyIsRunning() {
             KTVHTTPCache.proxyStop()
         }
     }
     
-    func restartProxy(user: String, password: String) {
-                
-        if KTVHTTPCache.proxyIsRunning() {
-            KTVHTTPCache.proxyStop()
-        }
-        
-        startProxy(user: user, password: password)
-    }
-    
     func getProxyURL(stringURL: String) -> URL {
         
         return KTVHTTPCache.proxyURL(withOriginalURL: URL(string: stringURL))

+ 26 - 72
iOSClient/Viewer/NCViewerMedia/NCPlayer/NCPlayer.swift

@@ -25,12 +25,8 @@ import Foundation
 import NCCommunication
 import UIKit
 import AVFoundation
-import AVKit
 import MediaPlayer
 
-/// The Set of custom player controllers currently using or transitioning out of PiP
-private var activeNCPlayer = Set<NCPlayer>()
-
 class NCPlayer: NSObject {
    
     private let appDelegate = UIApplication.shared.delegate as! AppDelegate
@@ -41,16 +37,17 @@ class NCPlayer: NSObject {
 
     public var player: AVPlayer?
     public var durationTime: CMTime = .zero
-    public var metadata: tableMetadata?
+    public var metadata: tableMetadata
     public var videoLayer: AVPlayerLayer?
-    public var pictureInPictureController: AVPictureInPictureController?
     
     // MARK: - View Life Cycle
 
     init(url: URL, autoPlay: Bool, imageVideoContainer: imageVideoContainerView, playerToolBar: NCPlayerToolBar?, metadata: tableMetadata, detailView: NCViewerMediaDetailView?) {
-        super.init()
         
         self.metadata = metadata
+
+        super.init()
+        
         self.detailView = detailView
 
         do {
@@ -64,9 +61,6 @@ class NCPlayer: NSObject {
         print("Play URL: \(url)")
         player = AVPlayer(url: url)
         
-        self.durationTime = self.player?.currentItem?.asset.duration ?? .zero
-        NCManageDatabase.shared.addVideoTime(metadata: metadata, time: nil, durationTime: self.durationTime)
-
         if metadata.livePhoto {
             player?.isMuted = false
         } else if metadata.classFile == NCCommunicationCommon.typeClassFile.audio.rawValue {
@@ -85,6 +79,9 @@ class NCPlayer: NSObject {
             case .loaded:
                 DispatchQueue.main.async {
                     
+                    self.durationTime = self.player?.currentItem?.asset.duration ?? .zero
+                    NCManageDatabase.shared.addVideoTime(metadata: metadata, time: nil, durationTime: self.durationTime)
+
                     self.activateObserver(playerToolBar: playerToolBar)
                     
                     self.videoLayer = AVPlayerLayer(player: self.player)
@@ -96,20 +93,10 @@ class NCPlayer: NSObject {
                         imageVideoContainer.layer.addSublayer(self.videoLayer!)
                         imageVideoContainer.playerLayer = self.videoLayer
                         imageVideoContainer.metadata = self.metadata
-                        if !metadata.livePhoto {
-                            DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
-                                imageVideoContainer.image = imageVideoContainer.image?.image(alpha: 0)
-                            }
-                        }
-                        
-                        // PIP
-                        if let playerLayer = self.videoLayer, CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView), metadata.livePhoto == false {
-                            self.pictureInPictureController = AVPictureInPictureController(playerLayer: playerLayer)
-                            self.pictureInPictureController?.delegate = self
-                        }
+                        imageVideoContainer.image = imageVideoContainer.image?.image(alpha: 0)
                     }
                     
-                    self.playerToolBar?.setBarPlayer(ncplayer: self, metadata: metadata, image: imageVideoContainer.image)
+                    self.playerToolBar?.setBarPlayer(ncplayer: self, metadata: metadata)
                     self.generatorImagePreview()
                     if !(detailView?.isShow() ?? false) {
                         NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterShowPlayerToolBar, userInfo: ["ocId":metadata.ocId, "enableTimerAutoHide": false])
@@ -142,28 +129,29 @@ class NCPlayer: NSObject {
 
     deinit {
         print("deinit NCPlayer")
+        
+        deactivateObserver()
     }
     
     func activateObserver(playerToolBar: NCPlayerToolBar?) {
-        guard let metadata = self.metadata else { return }
         
         self.playerToolBar = playerToolBar
         
         // At end go back to start & show toolbar
         observerAVPlayerItemDidPlayToEndTime = NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: player?.currentItem, queue: .main) { (notification) in
             if let item = notification.object as? AVPlayerItem, let currentItem = self.player?.currentItem, item == currentItem {
-                NCKTVHTTPCache.shared.saveCache(metadata: metadata)
+                NCKTVHTTPCache.shared.saveCache(metadata: self.metadata)
                 self.videoSeek(time: .zero)
                 if !(self.detailView?.isShow() ?? false) {
-                    NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterShowPlayerToolBar, userInfo: ["ocId":metadata.ocId, "enableTimerAutoHide": false])
+                    NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterShowPlayerToolBar, userInfo: ["ocId":self.metadata.ocId, "enableTimerAutoHide": false])
                 }
-                self.playerToolBar?.updateToolBar(commandCenter: true)
+                self.playerToolBar?.updateToolBar()
             }
         }
         
         // Evey 1 second update toolbar
         observerAVPlayertTime = player?.addPeriodicTimeObserver(forInterval: CMTimeMakeWithSeconds(1, preferredTimescale: 1), queue: .main, using: { (CMTime) in
-            if self.player?.currentItem?.status == .readyToPlay, let playerToolBar = self.playerToolBar, playerToolBar.isShow() {
+            if self.player?.currentItem?.status == .readyToPlay {
                 self.playerToolBar?.updateToolBar()
             }
         })
@@ -173,20 +161,14 @@ class NCPlayer: NSObject {
         NotificationCenter.default.addObserver(self, selector: #selector(applicationDidBecomeActive(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterApplicationDidBecomeActive), object: nil)
     }
     
-    func deactivateObserver(livePhoto: Bool) {
+    func deactivateObserver() {
         
         if isPlay() {
             playerPause()
         }
         
-        self.playerToolBar?.disableCommandCenter()
         self.playerToolBar = nil
         
-        if livePhoto {
-            self.videoLayer?.removeFromSuperlayer()
-            self.videoLayer = nil
-        }
-        
         if let observerAVPlayerItemDidPlayToEndTime = self.observerAVPlayerItemDidPlayToEndTime {
             NotificationCenter.default.removeObserver(observerAVPlayerItemDidPlayToEndTime)
         }
@@ -206,9 +188,12 @@ class NCPlayer: NSObject {
 
     @objc func applicationDidEnterBackground(_ notification: NSNotification) {
         
-        if metadata?.classFile == NCCommunicationCommon.typeClassFile.video.rawValue {
-            if let pictureInPictureController = pictureInPictureController, pictureInPictureController.isPictureInPictureActive { return }
-            playerPause()
+        guard let playerToolBar = self.playerToolBar else { return }
+        
+        if metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue {
+            if !playerToolBar.isPictureInPictureActive() {
+                playerPause()
+            }
         }
     }
     
@@ -224,15 +209,6 @@ class NCPlayer: NSObject {
         if player?.rate == 1 { return true } else { return false }
     }
     
-    func isPictureInPictureActive() -> Bool {
-        
-        if let pictureInPictureController = pictureInPictureController, pictureInPictureController.isPictureInPictureActive {
-            return true
-        } else {
-            return false
-        }
-    }
-    
     func playerPlay() {
                 
         player?.play()
@@ -244,8 +220,8 @@ class NCPlayer: NSObject {
         player?.pause()
         self.playerToolBar?.updateToolBar()
         
-        if let pictureInPictureController = pictureInPictureController, pictureInPictureController.isPictureInPictureActive {
-            pictureInPictureController.stopPictureInPicture()
+        if let playerToolBar = self.playerToolBar, playerToolBar.isPictureInPictureActive() {
+            playerToolBar.pictureInPictureController?.stopPictureInPicture()
         }
     }
     
@@ -256,7 +232,7 @@ class NCPlayer: NSObject {
     }
     
     func saveTime(_ time: CMTime) {
-        guard let metadata = self.metadata else { return }
+
         if metadata.classFile == NCCommunicationCommon.typeClassFile.audio.rawValue { return }
 
         NCManageDatabase.shared.addVideoTime(metadata: metadata, time: time, durationTime: nil)
@@ -271,8 +247,8 @@ class NCPlayer: NSObject {
     }
     
     @objc func generatorImagePreview() {
+        
         guard let time = player?.currentTime() else { return }
-        guard let metadata = self.metadata else { return }
         if metadata.livePhoto { return }
         if metadata.classFile == NCCommunicationCommon.typeClassFile.audio.rawValue { return }
 
@@ -310,27 +286,5 @@ class NCPlayer: NSObject {
     }
 }
 
-extension NCPlayer: AVPictureInPictureControllerDelegate {
-    
-    func pictureInPictureControllerWillStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
-        
-        activeNCPlayer.insert(self)
-    }
-    
-    func pictureInPictureControllerDidStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
-        // nothing
-    }
-    
-    func pictureInPictureControllerWillStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
-        //nothing
-    }
-    
-    func pictureInPictureControllerDidStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
-        guard let metadata = self.metadata else { return }
 
-        if !isPlay() {
-            NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterShowPlayerToolBar, userInfo: ["ocId":metadata.ocId, "enableTimerAutoHide": false])
-        }
-    }
-}
 

+ 111 - 206
iOSClient/Viewer/NCViewerMedia/NCPlayer/NCPlayerToolBar.swift

@@ -48,13 +48,13 @@ class NCPlayerToolBar: UIView {
         
     private let appDelegate = UIApplication.shared.delegate as! AppDelegate
     private var ncplayer: NCPlayer?
+    private var metadata: tableMetadata?
     private var wasInPlay: Bool = false
     private var playbackSliderEvent: sliderEventType = .ended
     private var timerAutoHide: Timer?
-    private var metadata: tableMetadata?
-    private var image: UIImage?
     
-    weak var viewerMedia: NCViewerMedia?
+    var pictureInPictureController: AVPictureInPictureController?
+    weak var viewerMediaPage: NCViewerMediaPage?
 
     // MARK: - View Life Cycle
 
@@ -110,6 +110,10 @@ class NCPlayerToolBar: UIView {
         NotificationCenter.default.addObserver(self, selector: #selector(handleRouteChange), name: AVAudioSession.routeChangeNotification, object: nil)
     }
     
+    required init?(coder aDecoder: NSCoder) {
+        super.init(coder: aDecoder)
+    }
+    
     deinit {
         print("deinit NCPlayerToolBar")
                 
@@ -119,11 +123,10 @@ class NCPlayerToolBar: UIView {
     
     // MARK: -
 
-    func setBarPlayer(ncplayer: NCPlayer, metadata: tableMetadata, image: UIImage?) {
+    func setBarPlayer(ncplayer: NCPlayer, metadata: tableMetadata) {
                         
         self.ncplayer = ncplayer
         self.metadata = metadata
-        self.image = image
                         
         playbackSlider.value = 0
         playbackSlider.minimumValue = 0
@@ -133,61 +136,36 @@ class NCPlayerToolBar: UIView {
         labelCurrentTime.text = NCUtility.shared.stringFromTime(.zero)
         labelLeftTime.text = "-" + NCUtility.shared.stringFromTime(ncplayer.durationTime)
         
-        updateToolBar(commandCenter: true)
+        updateToolBar()
     }
     
-    public func updateToolBar(timeSeek: CMTime? = nil, commandCenter: Bool = false) {
-        guard let metadata = self.metadata else { return }
-        guard let ncplayer = self.ncplayer else { return }
-        var time: CMTime = .zero
-        
-        let imageNameBackward = "gobackward.10"
-        let imageNameForward = "goforward.10"
-        
-        /*
-        if metadata.classFile == NCCommunicationCommon.typeClassFile.audio.rawValue {
-            imageNameBackward = "backward"
-            imageNameForward = "forward"
-        }
-        */
-        
-        // COMMAND CENTER
-        if commandCenter && CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView) {
-            enableCommandCenter()
-        }
+    public func updateToolBar() {
         
+        guard let ncplayer = self.ncplayer else { return }
+                
         // MUTE
-        if CCUtility.getAudioMute() {
-            muteButton.setImage(NCUtility.shared.loadImage(named: "audioOff", color: .white), for: .normal)
-        } else {
-            muteButton.setImage(NCUtility.shared.loadImage(named: "audioOn", color: .white), for: .normal)
+        if let muteButton = muteButton {
+            if CCUtility.getAudioMute() {
+                muteButton.setImage(NCUtility.shared.loadImage(named: "audioOff", color: .white), for: .normal)
+            } else {
+                muteButton.setImage(NCUtility.shared.loadImage(named: "audioOn", color: .white), for: .normal)
+            }
+            muteButton.isEnabled = true
         }
-        muteButton.isEnabled = true
         
         // PIP
-        if metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue && CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView) {
-            pipButton.setImage(NCUtility.shared.loadImage(named: "pip.enter", color: .white), for: .normal)
-            pipButton.isEnabled = true
-            if let playerLayer = ncplayer.videoLayer, ncplayer.pictureInPictureController == nil {
-                ncplayer.pictureInPictureController = AVPictureInPictureController(playerLayer: playerLayer)
-                ncplayer.pictureInPictureController?.delegate = ncplayer
-            }
-        } else {
-            pipButton.setImage(NCUtility.shared.loadImage(named: "pip.enter", color: .gray), for: .normal)
-            pipButton.isEnabled = false
-            if ncplayer.pictureInPictureController != nil {
-                ncplayer.pictureInPictureController = nil
-                ncplayer.pictureInPictureController?.delegate = nil
+        if let pipButton = pipButton {
+            if metadata?.classFile == NCCommunicationCommon.typeClassFile.video.rawValue && AVPictureInPictureController.isPictureInPictureSupported() {
+                pipButton.setImage(NCUtility.shared.loadImage(named: "pip.enter", color: .white), for: .normal)
+                pipButton.isEnabled = true
+            } else {
+                pipButton.setImage(NCUtility.shared.loadImage(named: "pip.enter", color: .gray), for: .normal)
+                pipButton.isEnabled = false
             }
         }
         
         // SLIDER TIME (START - END)
-        if timeSeek != nil {
-            time = timeSeek!
-        } else {
-            time = (ncplayer.player?.currentTime() ?? .zero).convertScale(1000, method: .default)
-            
-        }
+        let time = (ncplayer.player?.currentTime() ?? .zero).convertScale(1000, method: .default)
         playbackSlider.value = Float(time.seconds)
         MPNowPlayingInfoCenter.default().nowPlayingInfo?[MPNowPlayingInfoPropertyElapsedPlaybackTime] = time.seconds
         playbackSlider.isEnabled = true
@@ -196,9 +174,9 @@ class NCPlayerToolBar: UIView {
         
         // BACK
         if #available(iOS 13.0, *) {
-            backButton.setImage(NCUtility.shared.loadImage(named: imageNameBackward, color: .white), for: .normal)
+            backButton.setImage(NCUtility.shared.loadImage(named: "gobackward.10", color: .white), for: .normal)
         } else {
-            backButton.setImage(NCUtility.shared.loadImage(named: imageNameBackward, color: .white, size: 30), for: .normal)
+            backButton.setImage(NCUtility.shared.loadImage(named: "gobackward.10", color: .white, size: 30), for: .normal)
         }
         backButton.isEnabled = true
                  
@@ -218,131 +196,13 @@ class NCPlayerToolBar: UIView {
         
         // FORWARD
         if #available(iOS 13.0, *) {
-            forwardButton.setImage(NCUtility.shared.loadImage(named: imageNameForward, color: .white), for: .normal)
+            forwardButton.setImage(NCUtility.shared.loadImage(named: "goforward.10", color: .white), for: .normal)
         } else {
-            forwardButton.setImage(NCUtility.shared.loadImage(named: imageNameForward, color: .white, size: 30), for: .normal)
+            forwardButton.setImage(NCUtility.shared.loadImage(named: "goforward.10", color: .white, size: 30), for: .normal)
         }
         forwardButton.isEnabled = true
     }
     
-    // MARK: - Command Center
-    
-    func enableCommandCenter() {
-        guard let ncplayer = self.ncplayer else { return }
-        
-        UIApplication.shared.beginReceivingRemoteControlEvents()
-        var nowPlayingInfo = [String : Any]()
-
-        // Add handler for Play Command
-        MPRemoteCommandCenter.shared().playCommand.isEnabled = true
-        viewerMedia?.playCommand = MPRemoteCommandCenter.shared().playCommand.addTarget { event in
-            
-            if !ncplayer.isPlay() {
-                ncplayer.playerPlay()
-                return .success
-            }
-            return .commandFailed
-        }
-      
-        // Add handler for Pause Command
-        MPRemoteCommandCenter.shared().pauseCommand.isEnabled = true
-        viewerMedia?.pauseCommand = MPRemoteCommandCenter.shared().pauseCommand.addTarget { event in
-          
-            if ncplayer.isPlay() {
-                ncplayer.playerPause()
-                return .success
-            }
-            return .commandFailed
-        }
-        
-        // VIDEO / AUDIO () ()
-        if metadata?.classFile == NCCommunicationCommon.typeClassFile.video.rawValue || metadata?.classFile == NCCommunicationCommon.typeClassFile.audio.rawValue {
-            
-            MPRemoteCommandCenter.shared().skipForwardCommand.isEnabled = true
-            viewerMedia?.skipForwardCommand = MPRemoteCommandCenter.shared().skipForwardCommand.addTarget { event in
-                
-                let seconds = Float64((event as! MPSkipIntervalCommandEvent).interval)
-                self.skip(seconds: seconds)
-                return.success
-            }
-            
-            MPRemoteCommandCenter.shared().skipBackwardCommand.isEnabled = true
-            viewerMedia?.skipBackwardCommand = MPRemoteCommandCenter.shared().skipBackwardCommand.addTarget { event in
-                
-                let seconds = Float64((event as! MPSkipIntervalCommandEvent).interval)
-                self.skip(seconds: -seconds)
-                return.success
-            }
-        }
-                
-        // AUDIO < >
-        /*
-        if metadata?.classFile == NCCommunicationCommon.typeClassFile.audio.rawValue {
-                        
-            MPRemoteCommandCenter.shared().nextTrackCommand.isEnabled = true
-            appDelegate.nextTrackCommand = MPRemoteCommandCenter.shared().nextTrackCommand.addTarget { event in
-                
-                self.forward()
-                return .success
-            }
-            
-            MPRemoteCommandCenter.shared().previousTrackCommand.isEnabled = true
-            appDelegate.previousTrackCommand = MPRemoteCommandCenter.shared().previousTrackCommand.addTarget { event in
-             
-                self.backward()
-                return .success
-            }
-        }
-        */
-        
-        nowPlayingInfo[MPMediaItemPropertyTitle] = metadata?.fileNameView
-        nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = ncplayer.durationTime.seconds
-        if let image = self.image {
-            nowPlayingInfo[MPMediaItemPropertyArtwork] = MPMediaItemArtwork(boundsSize: image.size) { size in
-                return image
-            }
-        }
-        MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
-    }
-    
-    func disableCommandCenter() {
-        
-        UIApplication.shared.endReceivingRemoteControlEvents()
-        MPNowPlayingInfoCenter.default().nowPlayingInfo = [:]
-
-        MPRemoteCommandCenter.shared().playCommand.isEnabled = false
-        MPRemoteCommandCenter.shared().pauseCommand.isEnabled = false
-        MPRemoteCommandCenter.shared().skipForwardCommand.isEnabled = false
-        MPRemoteCommandCenter.shared().skipBackwardCommand.isEnabled = false
-        MPRemoteCommandCenter.shared().nextTrackCommand.isEnabled = false
-        MPRemoteCommandCenter.shared().previousTrackCommand.isEnabled = false
-
-        if let playCommand = viewerMedia?.playCommand {
-            MPRemoteCommandCenter.shared().playCommand.removeTarget(playCommand)
-            viewerMedia?.playCommand = nil
-        }
-        if let pauseCommand = viewerMedia?.pauseCommand {
-            MPRemoteCommandCenter.shared().pauseCommand.removeTarget(pauseCommand)
-            viewerMedia?.pauseCommand = nil
-        }
-        if let skipForwardCommand = viewerMedia?.skipForwardCommand {
-            MPRemoteCommandCenter.shared().skipForwardCommand.removeTarget(skipForwardCommand)
-            viewerMedia?.skipForwardCommand = nil
-        }
-        if let skipBackwardCommand = viewerMedia?.skipBackwardCommand {
-            MPRemoteCommandCenter.shared().skipBackwardCommand.removeTarget(skipBackwardCommand)
-            viewerMedia?.skipBackwardCommand = nil
-        }
-        if let nextTrackCommand = viewerMedia?.nextTrackCommand {
-            MPRemoteCommandCenter.shared().nextTrackCommand.removeTarget(nextTrackCommand)
-            viewerMedia?.nextTrackCommand = nil
-        }
-        if let previousTrackCommand = viewerMedia?.previousTrackCommand {
-            MPRemoteCommandCenter.shared().previousTrackCommand.removeTarget(previousTrackCommand)
-            viewerMedia?.previousTrackCommand = nil
-        }
-    }
-    
     // MARK: Handle Notifications
     
     @objc func handleRouteChange(notification: Notification) {
@@ -375,6 +235,7 @@ class NCPlayerToolBar: UIView {
     }
     
     @objc func handleInterruption(notification: Notification) {
+        
         guard let userInfo = notification.userInfo, let typeValue = userInfo[AVAudioSessionInterruptionTypeKey] as? UInt, let type = AVAudioSession.InterruptionType(rawValue: typeValue) else { return }
       
         if type == .began {
@@ -396,10 +257,9 @@ class NCPlayerToolBar: UIView {
     // MARK: -
 
     public func show(enableTimerAutoHide: Bool = false) {
-        guard let metadata = self.metadata else { return }
-        
-        if metadata.classFile != NCCommunicationCommon.typeClassFile.video.rawValue && metadata.classFile != NCCommunicationCommon.typeClassFile.audio.rawValue { return }
-        if metadata.livePhoto { return }
+                
+        if metadata?.classFile != NCCommunicationCommon.typeClassFile.video.rawValue && metadata?.classFile != NCCommunicationCommon.typeClassFile.audio.rawValue { return }
+        if let metadata = self.metadata, metadata.livePhoto { return }
         
         timerAutoHide?.invalidate()
         if enableTimerAutoHide {
@@ -456,6 +316,7 @@ class NCPlayerToolBar: UIView {
     }
     
     func skip(seconds: Float64) {
+        
         guard let ncplayer = ncplayer else { return }
         guard let player = ncplayer.player else { return }
         
@@ -465,7 +326,6 @@ class NCPlayerToolBar: UIView {
 
         if seconds > 0 {
             newTime = CMTimeAdd(currentTime, timeToAdd)
-            
             if newTime < ncplayer.durationTime {
                 ncplayer.videoSeek(time: newTime)
             } else if newTime >= ncplayer.durationTime {
@@ -477,42 +337,28 @@ class NCPlayerToolBar: UIView {
             }
         } else {
             newTime = CMTimeSubtract(currentTime, timeToAdd)
+            if newTime.seconds < 0 {
+                newTime = .zero
+            }
             ncplayer.videoSeek(time: newTime)
         }
         
+        updateToolBar()
         reStartTimerAutoHide()
     }
     
-    func forward() {
-        
-        var index: Int = 0
-        
-        if let currentIndex = self.viewerMedia?.currentIndex, let metadatas = self.viewerMedia?.metadatas, let ncplayer = self.ncplayer {
-        
-            if currentIndex == metadatas.count - 1 {
-                index = 0
-            } else {
-                index = currentIndex + 1
-            }
-            
-            self.viewerMedia?.goTo(index: index, direction: .forward, autoPlay: ncplayer.isPlay())
+    func isPictureInPictureActive() -> Bool {
+                
+        if let pictureInPictureController = self.pictureInPictureController, pictureInPictureController.isPictureInPictureActive {
+            return true
+        } else {
+            return false
         }
     }
     
-    func backward() {
+    func stopTimerAutoHide() {
         
-        var index: Int = 0
-
-        if let currentIndex = self.viewerMedia?.currentIndex, let metadatas = self.viewerMedia?.metadatas, let ncplayer = self.ncplayer {
-            
-            if currentIndex == 0 {
-                index = metadatas.count - 1
-            } else {
-                index = currentIndex - 1
-            }
-            
-            self.viewerMedia?.goTo(index: index, direction: .reverse, autoPlay: ncplayer.isPlay())
-        }
+        timerAutoHide?.invalidate()
     }
     
     //MARK: - Event / Gesture
@@ -549,12 +395,15 @@ class NCPlayerToolBar: UIView {
     //MARK: - Action
     
     @objc func didSingleTapWith(gestureRecognizer: UITapGestureRecognizer) {
+        // nothing
     }
     
     @IBAction func buttonPlayerToolBarTouchInside(_ sender: UIButton) {
+        // nothing
     }
     
     @IBAction func buttonPlayerTopToolBarTouchInside(_ sender: UIButton) {
+        // nothing
     }
     
     @IBAction func playerPause(_ sender: Any) {
@@ -594,10 +443,24 @@ class NCPlayerToolBar: UIView {
     }
     
     @IBAction func setPip(_ sender: Any) {
-        guard let metadata = self.metadata else { return }
-
-        ncplayer?.pictureInPictureController?.startPictureInPicture()
-        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterHidePlayerToolBar, userInfo: ["ocId":metadata.ocId])
+        
+        guard let videoLayer = ncplayer?.videoLayer else { return }
+        
+        if let pictureInPictureController = self.pictureInPictureController, pictureInPictureController.isPictureInPictureActive {
+            pictureInPictureController.stopPictureInPicture()
+        }
+        
+        if pictureInPictureController == nil {
+            pictureInPictureController = AVPictureInPictureController(playerLayer: videoLayer)
+            pictureInPictureController?.delegate = self
+        }
+        
+        if let pictureInPictureController = pictureInPictureController, pictureInPictureController.isPictureInPicturePossible, let metadata = self.metadata {
+            DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
+                pictureInPictureController.startPictureInPicture()
+                NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterHidePlayerToolBar, userInfo: ["ocId":metadata.ocId])
+            }
+        }
     }
     
     @IBAction func forwardButtonSec(_ sender: Any) {
@@ -625,5 +488,47 @@ class NCPlayerToolBar: UIView {
         }
         */
     }
+    
+    func forward() {
+        
+        var index: Int = 0
+        
+        if let currentIndex = self.viewerMediaPage?.currentIndex, let metadatas = self.viewerMediaPage?.metadatas, let ncplayer = self.ncplayer {
+        
+            if currentIndex == metadatas.count - 1 {
+                index = 0
+            } else {
+                index = currentIndex + 1
+            }
+            
+            self.viewerMediaPage?.goTo(index: index, direction: .forward, autoPlay: ncplayer.isPlay())
+        }
+    }
+    
+    func backward() {
+        
+        var index: Int = 0
+
+        if let currentIndex = self.viewerMediaPage?.currentIndex, let metadatas = self.viewerMediaPage?.metadatas, let ncplayer = self.ncplayer {
+            
+            if currentIndex == 0 {
+                index = metadatas.count - 1
+            } else {
+                index = currentIndex - 1
+            }
+            
+            self.viewerMediaPage?.goTo(index: index, direction: .reverse, autoPlay: ncplayer.isPlay())
+        }
+    }
+}
+
+extension NCPlayerToolBar: AVPictureInPictureControllerDelegate {
+    
+    func pictureInPictureControllerDidStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
+
+        if let metadata = self.metadata, let ncplayer = self.ncplayer, !ncplayer.isPlay() {
+            NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterShowPlayerToolBar, userInfo: ["ocId":metadata.ocId, "enableTimerAutoHide": false])
+        }
+    }
 }
 

+ 393 - 453
iOSClient/Viewer/NCViewerMedia/NCViewerMedia.swift

@@ -26,568 +26,508 @@ import SVGKit
 import NCCommunication
 
 class NCViewerMedia: UIViewController {
-
-    @IBOutlet weak var progressView: UIProgressView!
     
-    enum ScreenMode {
-        case full, normal
-    }
-    var currentScreenMode: ScreenMode = .normal
-    var saveScreenModeImage: ScreenMode = .normal
+    @IBOutlet weak var detailViewTopConstraint: NSLayoutConstraint!
+    @IBOutlet weak var detailViewHeighConstraint: NSLayoutConstraint!
+    @IBOutlet weak var imageViewTopConstraint: NSLayoutConstraint!
+    @IBOutlet weak var imageViewBottomConstraint: NSLayoutConstraint!
+    @IBOutlet weak var scrollView: UIScrollView!
+    @IBOutlet weak var imageVideoContainer: imageVideoContainerView!
+    @IBOutlet weak var statusViewImage: UIImageView!
+    @IBOutlet weak var statusLabel: UILabel!
+    @IBOutlet weak var detailView: NCViewerMediaDetailView!
+    @IBOutlet weak var playerToolBar: NCPlayerToolBar!
+    
+    private var _autoPlay: Bool = false
 
-    var pageViewController: UIPageViewController {
-        return self.children[0] as! UIPageViewController
-    }
-    
-    var currentViewController: NCViewerMediaZoom {
-        return self.pageViewController.viewControllers![0] as! NCViewerMediaZoom
+    let appDelegate = UIApplication.shared.delegate as! AppDelegate
+    var viewerMediaPage: NCViewerMediaPage?
+    var ncplayer: NCPlayer?
+    var image: UIImage?
+    var metadata: tableMetadata = tableMetadata()
+    var index: Int = 0
+    var doubleTapGestureRecognizer: UITapGestureRecognizer = UITapGestureRecognizer()
+    var imageViewConstraint: CGFloat = 0
+    var isDetailViewInitializze: Bool = false
+    
+    var autoPlay: Bool {
+        get {
+            let temp = _autoPlay
+            _autoPlay = false
+            return temp
+        }
+        set(newVal) {
+            _autoPlay = newVal
+        }
     }
     
-    let appDelegate = UIApplication.shared.delegate as! AppDelegate
+    // MARK: - View Life Cycle
 
-    var metadatas: [tableMetadata] = []
-    var currentIndex = 0
-    var nextIndex: Int?
-    var IndexInPlay: Int = -1
-    var ncplayerLivePhoto: NCPlayer?
-    var panGestureRecognizer: UIPanGestureRecognizer!
-    var singleTapGestureRecognizer: UITapGestureRecognizer!
-    var longtapGestureRecognizer: UILongPressGestureRecognizer!
-    
-    var textColor: UIColor = NCBrandColor.shared.label
+    required init?(coder aDecoder: NSCoder) {
+        super.init(coder: aDecoder)
+        
+        doubleTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(didDoubleTapWith(gestureRecognizer:)))
+        doubleTapGestureRecognizer.numberOfTapsRequired = 2
+    }
     
-    var playCommand: Any?
-    var pauseCommand: Any?
-    var skipForwardCommand: Any?
-    var skipBackwardCommand: Any?
-    var nextTrackCommand: Any?
-    var previousTrackCommand: Any?
+    deinit {
+        print("deinit NCViewerMedia")
+        
+        NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterOpenMediaDetail), object: nil)
+    }
     
-
-    // MARK: - View Life Cycle
-
     override func viewDidLoad() {
         super.viewDidLoad()
         
-        navigationItem.rightBarButtonItem = UIBarButtonItem.init(image: UIImage(named: "more")!.image(color: NCBrandColor.shared.label, size: 25), style: .plain, target: self, action: #selector(self.openMenuMore))
-        
-        singleTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(didSingleTapWith(gestureRecognizer:)))
-        panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(didPanWith(gestureRecognizer:)))
-        longtapGestureRecognizer = UILongPressGestureRecognizer()
-        longtapGestureRecognizer.delaysTouchesBegan = true
-        longtapGestureRecognizer.minimumPressDuration = 0.3
-        longtapGestureRecognizer.delegate = self
-        longtapGestureRecognizer.addTarget(self, action: #selector(didLongpressGestureEvent(gestureRecognizer:)))
-        
-        pageViewController.delegate = self
-        pageViewController.dataSource = self
-        pageViewController.view.addGestureRecognizer(panGestureRecognizer)
-        pageViewController.view.addGestureRecognizer(singleTapGestureRecognizer)
-        pageViewController.view.addGestureRecognizer(longtapGestureRecognizer)
-        
-        let viewerMediaZoom = getViewerMediaZoom(index: currentIndex, image: getImageMetadata(metadatas[currentIndex]), metadata: metadatas[currentIndex], direction: .forward)
-        pageViewController.setViewControllers([viewerMediaZoom], direction: .forward, animated: true, completion: nil)
-        
-        NotificationCenter.default.addObserver(self, selector: #selector(changeTheming), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterChangeTheming), object: nil)
-        NotificationCenter.default.addObserver(self, selector: #selector(viewUnload), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterMenuDetailClose), object: nil)
-        
-        progressView.tintColor = NCBrandColor.shared.brandElement
-        progressView.trackTintColor = .clear
-        progressView.progress = 0
-        
-        NotificationCenter.default.addObserver(self, selector: #selector(deleteFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterDeleteFile), object: nil)
-        NotificationCenter.default.addObserver(self, selector: #selector(renameFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterRenameFile), object: nil)
-        NotificationCenter.default.addObserver(self, selector: #selector(moveFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterMoveFile), object: nil)
-
-        NotificationCenter.default.addObserver(self, selector: #selector(downloadedFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterDownloadedFile), object: nil)
-        NotificationCenter.default.addObserver(self, selector: #selector(uploadedFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterUploadedFile), object: nil)
-        NotificationCenter.default.addObserver(self, selector: #selector(triggerProgressTask(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterProgressTask), object:nil)
-        
-        NotificationCenter.default.addObserver(self, selector: #selector(downloadedThumbnail(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterDownloadedThumbnail), object: nil)
+        scrollView.delegate = self
+        scrollView.maximumZoomScale = 4
+        scrollView.minimumZoomScale = 1
         
-        NotificationCenter.default.addObserver(self, selector: #selector(hidePlayerToolBar(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterHidePlayerToolBar), object: nil)
-        NotificationCenter.default.addObserver(self, selector: #selector(showPlayerToolBar(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterShowPlayerToolBar), object: nil)
-    }
-    
-    override func viewDidDisappear(_ animated: Bool) {
-        super.viewDidDisappear(animated)
+        view.addGestureRecognizer(doubleTapGestureRecognizer)
         
-        // Clear
-        if let ncplayer = currentViewController.ncplayer, ncplayer.isPlay() {
-            ncplayer.playerPause()
-            ncplayer.saveCurrentTime()
+        if NCManageDatabase.shared.getMetadataLivePhoto(metadata: metadata) != nil {
+            statusViewImage.image = NCUtility.shared.loadImage(named: "livephoto", color: .gray)
+            statusLabel.text = "LIVE"
+        }  else {
+            statusViewImage.image = nil
+            statusLabel.text = ""
         }
         
-        currentViewController.playerToolBar.disableCommandCenter()
+        playerToolBar.viewerMediaPage = viewerMediaPage
         
-        metadatas.removeAll()
-        ncplayerLivePhoto = nil
+        detailViewTopConstraint.constant = 0
+        detailView.hide()
         
-        NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterDeleteFile), object: nil)
-        NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterRenameFile), object: nil)
-        NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterMoveFile), object: nil)
+        self.image = nil
+        self.imageVideoContainer.image = nil
 
-        NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterDownloadedFile), object: nil)
-        NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterUploadedFile), object: nil)
-        NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterProgressTask), object: nil)
-        
-        NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterDownloadedThumbnail), object: nil)
-        
-        NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterHidePlayerToolBar), object: nil)
-        NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterShowPlayerToolBar), object: nil)
-    }
-    
-    override var preferredStatusBarStyle: UIStatusBarStyle {
-        
-        if currentScreenMode == .normal {
-            return .default
-        } else {
-            return .lightContent
+        loadImage(metadata: metadata) { ocId, image in
+            self.image = image
+            // do not update if is present the videoLayer
+            let numSublayers = self.imageVideoContainer.layer.sublayers?.count
+            if numSublayers == nil {
+                self.imageVideoContainer.image = image
+            }
         }
     }
     
-    // MARK: -
-    
-    func getViewerMediaZoom(index: Int, image: UIImage?, metadata: tableMetadata, direction: UIPageViewController.NavigationDirection) -> NCViewerMediaZoom {
-        
-        let viewerMediaZoom = UIStoryboard(name: "NCViewerMedia", bundle: nil).instantiateViewController(withIdentifier: "NCViewerMediaZoom") as! NCViewerMediaZoom
-        viewerMediaZoom.index = index
-        viewerMediaZoom.image = image
-        viewerMediaZoom.metadata = metadata
-        viewerMediaZoom.viewerMedia = self
-
-        singleTapGestureRecognizer.require(toFail: viewerMediaZoom.doubleTapGestureRecognizer)
-       
-        return viewerMediaZoom
-    }
-
-    @objc func viewUnload() {
-        
-        navigationController?.popViewController(animated: true)
-    }
-    
-    @objc func openMenuMore() {
+    override func viewWillAppear(_ animated: Bool) {
+        super.viewWillAppear(animated)
         
-        let imageIcon = UIImage(contentsOfFile: CCUtility.getDirectoryProviderStorageIconOcId(currentViewController.metadata.ocId, etag: currentViewController.metadata.etag))
-        NCViewer.shared.toggleMenu(viewController: self, metadata: currentViewController.metadata, webView: false, imageIcon: imageIcon)
-    }
-    
-    func changeScreenMode(mode: ScreenMode, enableTimerAutoHide: Bool = false) {
+        viewerMediaPage?.navigationController?.navigationBar.prefersLargeTitles = false
+        viewerMediaPage?.navigationItem.title = metadata.fileNameView
         
-        if mode == .normal {
+        if metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue, let viewerMediaPage = self.viewerMediaPage {
+            viewerMediaPage.currentScreenMode = viewerMediaPage.saveScreenModeImage
+        }
+                
+        if viewerMediaPage?.currentScreenMode == .full {
             
-            navigationController?.setNavigationBarHidden(false, animated: true)
-            progressView.isHidden = false
-
-            if !currentViewController.detailView.isShow() {
-                currentViewController.playerToolBar.show(enableTimerAutoHide: enableTimerAutoHide)
-            }
+            viewerMediaPage?.navigationController?.setNavigationBarHidden(true, animated: true)
             
-            NCUtility.shared.colorNavigationController(navigationController, backgroundColor: NCBrandColor.shared.systemBackground, titleColor: NCBrandColor.shared.label, tintColor: nil, withoutShadow: false)
-            view.backgroundColor = NCBrandColor.shared.systemBackground
-            textColor = NCBrandColor.shared.label
+            NCUtility.shared.colorNavigationController(viewerMediaPage?.navigationController, backgroundColor: .black, titleColor: .white, tintColor: nil, withoutShadow: false)
+            
+            viewerMediaPage?.view.backgroundColor = .black
+            viewerMediaPage?.textColor = .white
+            viewerMediaPage?.progressView.isHidden = true
             
         } else {
             
-            navigationController?.setNavigationBarHidden(true, animated: true)
-            progressView.isHidden = true
+            viewerMediaPage?.navigationController?.setNavigationBarHidden(false, animated: true)
+                
+            NCUtility.shared.colorNavigationController(viewerMediaPage?.navigationController, backgroundColor: NCBrandColor.shared.systemBackground, titleColor: NCBrandColor.shared.label, tintColor: nil, withoutShadow: false)
             
-            currentViewController.playerToolBar.hide()
-
-            view.backgroundColor = .black
-            textColor = .white
-        }
-        
-        currentScreenMode = mode
-        
-        if currentViewController.metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue {
-            saveScreenModeImage = mode
+            viewerMediaPage?.view.backgroundColor = NCBrandColor.shared.systemBackground
+            viewerMediaPage?.textColor = NCBrandColor.shared.label
+            viewerMediaPage?.progressView.isHidden = false
         }
-        
-        setNeedsStatusBarAppearanceUpdate()
-        currentViewController.reloadDetail()
     }
     
-    //MARK: - NotificationCenter
-
-    @objc func downloadedFile(_ notification: NSNotification) {
+    override func viewDidAppear(_ animated: Bool) {
+        super.viewDidAppear(animated)
         
-        if let userInfo = notification.userInfo as NSDictionary?, let ocId = userInfo["ocId"] as? String, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId), let errorCode = userInfo["errorCode"] as? Int {
-          
-            if errorCode == 0 && currentViewController.metadata.ocId == ocId, let image = getImageMetadata(metadata) {
-                currentViewController.reload(image: image, metadata: metadata)
-                if self.metadatas.first(where: { $0.ocId == metadata.ocId }) != nil {
-                    progressView.progress = 0
+        if (metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue || metadata.classFile == NCCommunicationCommon.typeClassFile.audio.rawValue) {
+            
+            NCKTVHTTPCache.shared.restartProxy(user: appDelegate.user, password: appDelegate.password)
+            
+            if ncplayer == nil, let url = NCKTVHTTPCache.shared.getVideoURL(metadata: metadata) {
+                self.ncplayer = NCPlayer.init(url: url, autoPlay: self.autoPlay, imageVideoContainer: self.imageVideoContainer, playerToolBar: self.playerToolBar, metadata: self.metadata, detailView: self.detailView)
+            } else {
+                self.ncplayer?.activateObserver(playerToolBar: self.playerToolBar)
+                if detailView.isShow() == false && ncplayer?.isPlay() == false {
+                    NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterShowPlayerToolBar, userInfo: ["ocId":metadata.ocId, "enableTimerAutoHide": false])
                 }
             }
-        }
-    }
-    
-    @objc func downloadedThumbnail(_ notification: NSNotification) {
-        
-        if let userInfo = notification.userInfo as NSDictionary?, let ocId = userInfo["ocId"] as? String, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
             
-            if currentViewController.metadata.ocId == ocId, let image = getImageMetadata(metadata) {
-                currentViewController.reload(image: image, metadata: metadata)
+            if let ncplayer = self.ncplayer {
+                self.viewerMediaPage?.updateCommandCenter(ncplayer: ncplayer, metadata: self.metadata)
             }
+            
+        } else if metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue {
+            
+            viewerMediaPage?.clearCommandCenter()
         }
+                
+        NotificationCenter.default.addObserver(self, selector: #selector(openDetail(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterOpenMediaDetail), object: nil)
     }
     
-    @objc func uploadedFile(_ notification: NSNotification) {
-        
-        if let userInfo = notification.userInfo as NSDictionary? {
-            if let ocId = userInfo["ocId"] as? String, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId), let errorCode = userInfo["errorCode"] as? Int {
-                if errorCode == 0  && metadata.ocId == currentViewController.metadata.ocId {
-                    //self.reloadCurrentPage()
-                }
-            }
-        }
+    override func viewDidDisappear(_ animated: Bool) {
+        super.viewDidDisappear(animated)
     }
     
-    @objc func triggerProgressTask(_ notification: NSNotification) {
+    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
+        super.viewWillTransition(to: size, with: coordinator)
         
-        if let userInfo = notification.userInfo as NSDictionary? {
-            if let serverUrl = userInfo["serverUrl"] as? String, let fileName = userInfo["fileName"] as? String, let progressNumber = userInfo["progress"] as? NSNumber {
-                if self.metadatas.first(where: { $0.serverUrl == serverUrl && $0.fileName == fileName}) != nil {
-                    let progress = progressNumber.floatValue
-                    if progress == 1 {
-                        self.progressView.progress = 0
-                    } else {
-                        self.progressView.progress = progress
-                    }
+        coordinator.animate(alongsideTransition: { (context) in
+            // back to the original size
+            self.scrollView.zoom(to: CGRect(x: 0, y: 0, width: self.scrollView.bounds.width, height: self.scrollView.bounds.height), animated: false)
+            self.view.layoutIfNeeded()
+            UIView.animate(withDuration: context.transitionDuration) {
+                if self.detailView.isShow() {
+                    self.openDetail()
                 }
             }
-        }
+        }) { (_) in }
     }
     
-    @objc func deleteFile(_ notification: NSNotification) {
-        
-        if let userInfo = notification.userInfo as NSDictionary? {
-            if let ocId = userInfo["ocId"] as? String {
-                
-                let metadatas = self.metadatas.filter { $0.ocId != ocId }
-                if self.metadatas.count == metadatas.count { return }
-                self.metadatas = metadatas
-                
-                if ocId == currentViewController.metadata.ocId {
-                    if !shiftCurrentPage() {
-                        self.viewUnload()
-                    }
-                }
-            }
-        }
-    }
+    //MARK: - Image
     
-    @objc func renameFile(_ notification: NSNotification) {
+    func loadImage(metadata: tableMetadata, completion: @escaping (_ ocId: String, _ image: UIImage?)->()) {
         
-        if let userInfo = notification.userInfo as NSDictionary? {
-            if let ocId = userInfo["ocId"] as? String, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
-                
-                if let index = metadatas.firstIndex(where: {$0.ocId == metadata.ocId}) {
-                    metadatas[index] = metadata
-                    if index == currentIndex {
-                        navigationItem.title = metadata.fileNameView
-                        currentViewController.metadata = metadata
-                        self.currentViewController.metadata = metadata
-                    }
+        // Download preview
+        if metadata.hasPreview && !CCUtility.fileProviderStoragePreviewIconExists(metadata.ocId, etag: metadata.etag) {
+            
+            var etagResource: String?
+            let fileNamePath = CCUtility.returnFileNamePath(fromFileName: metadata.fileName, serverUrl: metadata.serverUrl, urlBase: metadata.urlBase, account: metadata.account)!
+            let fileNamePreviewLocalPath = CCUtility.getDirectoryProviderStoragePreviewOcId(metadata.ocId, etag: metadata.etag)!
+            let fileNameIconLocalPath = CCUtility.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.etag)!
+            if FileManager.default.fileExists(atPath: fileNameIconLocalPath) && FileManager.default.fileExists(atPath: fileNamePreviewLocalPath) {
+                etagResource = metadata.etagResource
+            }
+               
+            NCCommunication.shared.downloadPreview(fileNamePathOrFileId: fileNamePath, fileNamePreviewLocalPath: fileNamePreviewLocalPath , widthPreview: NCGlobal.shared.sizePreview, heightPreview: NCGlobal.shared.sizePreview, fileNameIconLocalPath: fileNameIconLocalPath, sizeIcon: NCGlobal.shared.sizeIcon, etag: etagResource, queue: NCCommunicationCommon.shared.backgroundQueue) { (account, imagePreview, imageIcon, imageOriginal, etag, errorCode, errorDescription) in
+                     
+                if errorCode == 0 && imageIcon != nil {
+                    NCManageDatabase.shared.setMetadataEtagResource(ocId: metadata.ocId, etagResource: etag)
                 }
+                                
+                // Download file max resolution
+                downloadFile(metadata: metadata)
+                // Download file live photo
+                if metadata.livePhoto { downloadFileLivePhoto(metadata: metadata) }
             }
+        } else {
+            
+            // Download file max resolution
+            downloadFile(metadata: metadata)
+            // Download file live photo
+            if metadata.livePhoto { downloadFileLivePhoto(metadata: metadata) }
         }
-    }
-    
-    @objc func moveFile(_ notification: NSNotification) {
         
-        if let userInfo = notification.userInfo as NSDictionary? {
-            if let ocId = userInfo["ocId"] as? String, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
+        // Download file max resolution
+        func downloadFile(metadata: tableMetadata) {
+            
+            let isFolderEncrypted = CCUtility.isFolderEncrypted(metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase)
+            let ext = CCUtility.getExtension(metadata.fileNameView)
+            
+            if (CCUtility.getAutomaticDownloadImage() || (metadata.contentType == "image/heic" &&  metadata.hasPreview == false) || ext == "GIF" || ext == "SVG" || isFolderEncrypted) && (metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue && !CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView) && metadata.session == "") {
                 
-                if metadatas.firstIndex(where: {$0.ocId == metadata.ocId}) != nil {
-                    deleteFile(notification)
+                NCNetworking.shared.download(metadata: metadata, selector: "") { (_) in
+                    
+                    let image = getImageMetadata(metadata)
+                    DispatchQueue.main.async { completion(metadata.ocId, image) }
                 }
+                
+            } else {
+                
+                let image = getImageMetadata(metadata)
+                DispatchQueue.main.async { completion(metadata.ocId, image) }
             }
         }
-    }
-    
-    @objc func changeTheming() {
-    }
-    
-    @objc func hidePlayerToolBar(_ notification: NSNotification) {
         
-        if let userInfo = notification.userInfo as NSDictionary?, let ocId = userInfo["ocId"] as? String {
-            if currentViewController.metadata.ocId == ocId {
-                changeScreenMode(mode: .full)
+        // Download Live Photo
+        func downloadFileLivePhoto(metadata: tableMetadata) {
+            
+            let fileName = (metadata.fileNameView as NSString).deletingPathExtension + ".mov"
+
+            if let metadata = NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileNameView LIKE[c] %@", metadata.account, metadata.serverUrl, fileName)), !CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView) {
+
+                NCNetworking.shared.download(metadata: metadata, selector: "") { (_) in }
             }
         }
-    }
-    
-    @objc func showPlayerToolBar(_ notification: NSNotification) {
         
-        if let userInfo = notification.userInfo as NSDictionary?, let ocId = userInfo["ocId"] as? String, let enableTimerAutoHide = userInfo["enableTimerAutoHide"] as? Bool{
-            if currentViewController.metadata.ocId == ocId, let ncplayer = currentViewController.ncplayer, !ncplayer.isPictureInPictureActive() {
-                changeScreenMode(mode: .normal, enableTimerAutoHide: enableTimerAutoHide)
+        func getImageMetadata(_ metadata: tableMetadata) -> UIImage? {
+                    
+            if let image = getImage(metadata: metadata) {
+                return image
+            }
+            
+            if metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue && !metadata.hasPreview {
+                NCUtility.shared.createImageFrom(fileName: metadata.fileNameView, ocId: metadata.ocId, etag: metadata.etag, classFile: metadata.classFile)
+            }
+            
+            if CCUtility.fileProviderStoragePreviewIconExists(metadata.ocId, etag: metadata.etag) {
+                if let imagePreviewPath = CCUtility.getDirectoryProviderStoragePreviewOcId(metadata.ocId, etag: metadata.etag) {
+                    return UIImage.init(contentsOfFile: imagePreviewPath)
+                }
+            }
+            
+            if metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue {
+                return UIImage.init(named: "noPreviewVideo")!.image(color: .gray, size: view.frame.width)
+            } else if metadata.classFile == NCCommunicationCommon.typeClassFile.audio.rawValue {
+                return UIImage.init(named: "noPreviewAudio")!.image(color: .gray, size: view.frame.width)
+            } else {
+                return UIImage.init(named: "noPreview")!.image(color: .gray, size: view.frame.width)
             }
         }
-    }
-    
-    //MARK: - Image
-    
-    func getImageMetadata(_ metadata: tableMetadata) -> UIImage? {
-                
-        if let image = getImage(metadata: metadata) {
-            return image
-        }
-        
-        if metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue && !metadata.hasPreview {
-            NCUtility.shared.createImageFrom(fileName: metadata.fileNameView, ocId: metadata.ocId, etag: metadata.etag, classFile: metadata.classFile)
-        }
-        
-        if CCUtility.fileProviderStoragePreviewIconExists(metadata.ocId, etag: metadata.etag) {
-            if let imagePreviewPath = CCUtility.getDirectoryProviderStoragePreviewOcId(metadata.ocId, etag: metadata.etag) {
-                return UIImage.init(contentsOfFile: imagePreviewPath)
-            } 
-        }
-        
-        return nil
-    }
-    
-    private func getImage(metadata: tableMetadata) -> UIImage? {
-        
-        let ext = CCUtility.getExtension(metadata.fileNameView)
-        var image: UIImage?
         
-        if CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView) && metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue {
-           
-            let previewPath = CCUtility.getDirectoryProviderStoragePreviewOcId(metadata.ocId, etag: metadata.etag)!
-            let imagePath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)!
+        func getImage(metadata: tableMetadata) -> UIImage? {
             
-            if ext == "GIF" {
-                if !FileManager().fileExists(atPath: previewPath) {
-                    NCUtility.shared.createImageFrom(fileName: metadata.fileNameView, ocId: metadata.ocId, etag: metadata.etag, classFile: metadata.classFile)
-                }
-                image = UIImage.animatedImage(withAnimatedGIFURL: URL(fileURLWithPath: imagePath))
-            } else if ext == "SVG" {
-                if let svgImage = SVGKImage(contentsOfFile: imagePath) {
-                    svgImage.size = CGSize(width: NCGlobal.shared.sizePreview, height: NCGlobal.shared.sizePreview)
-                    if let image = svgImage.uiImage {
-                        if !FileManager().fileExists(atPath: previewPath) {
-                            do {
-                                try image.pngData()?.write(to: URL(fileURLWithPath: previewPath), options: .atomic)
-                            } catch { }
+            let ext = CCUtility.getExtension(metadata.fileNameView)
+            var image: UIImage?
+            
+            if CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView) && metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue {
+               
+                let previewPath = CCUtility.getDirectoryProviderStoragePreviewOcId(metadata.ocId, etag: metadata.etag)!
+                let imagePath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)!
+                
+                if ext == "GIF" {
+                    if !FileManager().fileExists(atPath: previewPath) {
+                        NCUtility.shared.createImageFrom(fileName: metadata.fileNameView, ocId: metadata.ocId, etag: metadata.etag, classFile: metadata.classFile)
+                    }
+                    image = UIImage.animatedImage(withAnimatedGIFURL: URL(fileURLWithPath: imagePath))
+                } else if ext == "SVG" {
+                    if let svgImage = SVGKImage(contentsOfFile: imagePath) {
+                        svgImage.size = CGSize(width: NCGlobal.shared.sizePreview, height: NCGlobal.shared.sizePreview)
+                        if let image = svgImage.uiImage {
+                            if !FileManager().fileExists(atPath: previewPath) {
+                                do {
+                                    try image.pngData()?.write(to: URL(fileURLWithPath: previewPath), options: .atomic)
+                                } catch { }
+                            }
+                            return image
+                        } else {
+                            return nil
                         }
-                        return image
                     } else {
                         return nil
                     }
                 } else {
-                    return nil
+                    NCUtility.shared.createImageFrom(fileName: metadata.fileNameView, ocId: metadata.ocId, etag: metadata.etag, classFile: metadata.classFile)
+                    image = UIImage.init(contentsOfFile: imagePath)
                 }
-            } else {
-                NCUtility.shared.createImageFrom(fileName: metadata.fileNameView, ocId: metadata.ocId, etag: metadata.etag, classFile: metadata.classFile)
-                image = UIImage.init(contentsOfFile: imagePath)
             }
+            
+            return image
         }
-        
-        return image
     }
-}
-
-//MARK: - UIPageViewController Delegate Datasource
-
-extension NCViewerMedia: UIPageViewControllerDelegate, UIPageViewControllerDataSource {
     
-    func shiftCurrentPage() -> Bool {
-        
-        if metadatas.count == 0 { return false }
+    //MARK: - Gesture
+
+    @objc func didDoubleTapWith(gestureRecognizer: UITapGestureRecognizer) {
         
-        var direction: UIPageViewController.NavigationDirection = .forward
+        if detailView.isShow() { return }
+        // NO ZOOM for Audio
+        if metadata.classFile == NCCommunicationCommon.typeClassFile.audio.rawValue { return }
         
-        if currentIndex == metadatas.count {
-            currentIndex -= 1
-            direction = .reverse
+        let pointInView = gestureRecognizer.location(in: self.imageVideoContainer)
+        var newZoomScale = self.scrollView.maximumZoomScale
+            
+        if self.scrollView.zoomScale >= newZoomScale || abs(self.scrollView.zoomScale - newZoomScale) <= 0.01 {
+            newZoomScale = self.scrollView.minimumZoomScale
         }
+                
+        let width = self.scrollView.bounds.width / newZoomScale
+        let height = self.scrollView.bounds.height / newZoomScale
+        let originX = pointInView.x - (width / 2.0)
+        let originY = pointInView.y - (height / 2.0)
+        let rectToZoomTo = CGRect(x: originX, y: originY, width: width, height: height)
+        self.scrollView.zoom(to: rectToZoomTo, animated: true)
+    }
+      
+    @objc func didPanWith(gestureRecognizer: UIPanGestureRecognizer) {
+                
+        let currentLocation = gestureRecognizer.translation(in: self.view)
         
-        currentViewController.ncplayer?.deactivateObserver(livePhoto: currentViewController.metadata.livePhoto)
-        
-        let viewerMediaZoom = getViewerMediaZoom(index: currentIndex, image: getImageMetadata(metadatas[currentIndex]), metadata: metadatas[currentIndex], direction: direction)
-        pageViewController.setViewControllers([viewerMediaZoom], direction: direction, animated: true, completion: nil)
-        
-        return true
-    }
-    
-    func goTo(index: Int, direction: UIPageViewController.NavigationDirection, autoPlay: Bool) {
-        
-        currentIndex = index
+        switch gestureRecognizer.state {
         
-        currentViewController.ncplayer?.deactivateObserver(livePhoto: currentViewController.metadata.livePhoto)
+        case .began:
+            
+//        let velocity = gestureRecognizer.velocity(in: self.view)
 
-        let viewerMediaZoom = getViewerMediaZoom(index: currentIndex, image: getImageMetadata(metadatas[currentIndex]), metadata: metadatas[currentIndex], direction: direction)
-        viewerMediaZoom.autoPlay = autoPlay
-        pageViewController.setViewControllers([viewerMediaZoom], direction: direction, animated: true, completion: nil)
-    }
-    
-    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
-        
-        if currentIndex == 0 { return nil }
+//            gesture moving Up
+//            if velocity.y < 0 {
 
-        let viewerMediaZoom = getViewerMediaZoom(index: currentIndex-1, image: getImageMetadata(metadatas[currentIndex-1]), metadata: metadatas[currentIndex-1], direction: .reverse)
-        return viewerMediaZoom
-    }
-    
-    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
-        
-        if currentIndex == metadatas.count-1 { return nil }
+//            }
+            break
 
-        let viewerMediaZoom = getViewerMediaZoom(index: currentIndex+1, image: getImageMetadata(metadatas[currentIndex+1]), metadata: metadatas[currentIndex+1], direction: .forward)
-        return viewerMediaZoom
-    }
-    
-    // START TRANSITION
-    func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) {
-        
-        // Save time video
-        if let ncplayer = currentViewController.ncplayer, ncplayer.isPlay() {
-            ncplayer.saveCurrentTime()
-        }
-        
-        guard let nextViewController = pendingViewControllers.first as? NCViewerMediaZoom else { return }
-        nextIndex = nextViewController.index        
-    }
-    
-    // END TRANSITION
-    func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
-        
-        if (completed && nextIndex != nil) {
-            previousViewControllers.forEach { viewController in
-                let viewerMediaZoom = viewController as! NCViewerMediaZoom
-                viewerMediaZoom.ncplayer?.deactivateObserver(livePhoto: false)
+        case .ended:
+            
+            if detailView.isShow() {
+                self.imageViewTopConstraint.constant = -imageViewConstraint
+                self.imageViewBottomConstraint.constant = imageViewConstraint
+            } else {
+                self.imageViewTopConstraint.constant = 0
+                self.imageViewBottomConstraint.constant = 0
+            }
+
+        case .changed:
+                        
+            imageViewTopConstraint.constant = (currentLocation.y - imageViewConstraint)
+            imageViewBottomConstraint.constant = -(currentLocation.y - imageViewConstraint)
+            
+            // DISMISS VIEW
+            if detailView.isHidden && (currentLocation.y > 20) {
+                
+                viewerMediaPage?.navigationController?.popViewController(animated: true)
+                gestureRecognizer.state = .ended
             }
-            currentIndex = nextIndex!
+            
+            // CLOSE DETAIL
+            if !detailView.isHidden && (currentLocation.y > 20) {
+                               
+                self.closeDetail()
+                gestureRecognizer.state = .ended
+            }
+
+            // OPEN DETAIL
+            if detailView.isHidden && (currentLocation.y < -20) {
+                       
+                self.openDetail()
+                gestureRecognizer.state = .ended
+            }
+                        
+        default:
+            break
         }
-        
-        self.nextIndex = nil
     }
 }
 
-//MARK: - UIGestureRecognizerDelegate
+//MARK: -
 
-extension NCViewerMedia: UIGestureRecognizerDelegate {
-
-    func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
+extension NCViewerMedia {
+    
+    @objc func openDetail(_ notification: NSNotification) {
         
-        if let gestureRecognizer = gestureRecognizer as? UIPanGestureRecognizer {
-            let velocity = gestureRecognizer.velocity(in: self.view)
-            
-            var velocityCheck : Bool = false
+        if let userInfo = notification.userInfo as NSDictionary?, let ocId = userInfo["ocId"] as? String, ocId == metadata.ocId {
+            openDetail()
+        }
+    }
+    
+    private func openDetail() {
+        
+        CCUtility.setExif(metadata) { (latitude, longitude, location, date, lensModel) in
             
-            if UIDevice.current.orientation.isLandscape {
-                velocityCheck = velocity.x < 0
+            if (latitude != -1 && latitude != 0 && longitude != -1 && longitude != 0) {
+                self.detailViewHeighConstraint.constant = self.view.bounds.height / 2
+            } else {
+                self.detailViewHeighConstraint.constant = 170
             }
-            else {
-                velocityCheck = velocity.y < 0
+            self.view.layoutIfNeeded()
+            
+            self.detailView.show(metadata:self.metadata, image: self.image, textColor: self.viewerMediaPage?.textColor, latitude: latitude, longitude: longitude, location: location, date: date, lensModel: lensModel, delegate: self)
+                
+            if let image = self.imageVideoContainer.image {
+                let ratioW = self.imageVideoContainer.frame.width / image.size.width
+                let ratioH = self.imageVideoContainer.frame.height / image.size.height
+                let ratio = ratioW < ratioH ? ratioW : ratioH
+                let imageHeight = image.size.height * ratio
+                self.imageViewConstraint = self.detailView.frame.height - ((self.view.frame.height - imageHeight) / 2) + self.view.safeAreaInsets.bottom
+                if self.imageViewConstraint < 0 { self.imageViewConstraint = 0 }
             }
-            if velocityCheck {
-                return false
+                
+            UIView.animate(withDuration: 0.3) {
+                self.imageViewTopConstraint.constant = -self.imageViewConstraint
+                self.imageViewBottomConstraint.constant = self.imageViewConstraint
+                self.detailViewTopConstraint.constant = self.detailViewHeighConstraint.constant
+                self.view.layoutIfNeeded()
+            } completion: { (_) in
             }
+                
+            self.scrollView.pinchGestureRecognizer?.isEnabled = false
+            self.playerToolBar.hide()
+        }
+    }
+    
+    private func closeDetail() {
+        
+        self.detailView.hide()
+        imageViewConstraint = 0
+        
+        UIView.animate(withDuration: 0.3) {
+            self.imageViewTopConstraint.constant = 0
+            self.imageViewBottomConstraint.constant = 0
+            self.detailViewTopConstraint.constant = 0
+            self.view.layoutIfNeeded()
+        } completion: { (_) in
         }
         
-        return true
+        scrollView.pinchGestureRecognizer?.isEnabled = true
+        if metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue && !metadata.livePhoto && ncplayer?.player?.timeControlStatus == .paused {
+            NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterShowPlayerToolBar, userInfo: ["ocId":metadata.ocId, "enableTimerAutoHide": false])
+        }
     }
     
-    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
+    func reloadDetail() {
         
-        if otherGestureRecognizer == currentViewController.scrollView.panGestureRecognizer {
-            if self.currentViewController.scrollView.contentOffset.y == 0 {
-                return true
+        if self.detailView.isShow() {
+            CCUtility.setExif(metadata) { (latitude, longitude, location, date, lensModel) in
+                self.detailView.show(metadata:self.metadata, image: self.image, textColor: self.viewerMediaPage?.textColor, latitude: latitude, longitude: longitude, location: location, date: date, lensModel: lensModel, delegate: self)
             }
         }
-        
-        return false
     }
+}
 
-    @objc func didPanWith(gestureRecognizer: UIPanGestureRecognizer) {
-        
-        currentViewController.didPanWith(gestureRecognizer: gestureRecognizer)
+//MARK: -
+
+extension NCViewerMedia: UIScrollViewDelegate {
+    
+    func viewForZooming(in scrollView: UIScrollView) -> UIView? {
+        return imageVideoContainer
     }
     
-    @objc func didSingleTapWith(gestureRecognizer: UITapGestureRecognizer) {
+    func scrollViewDidZoom(_ scrollView: UIScrollView) {
+        
+        if scrollView.zoomScale > 1 {
+            if let image = imageVideoContainer.image {
+                
+                let ratioW = imageVideoContainer.frame.width / image.size.width
+                let ratioH = imageVideoContainer.frame.height / image.size.height
+                let ratio = ratioW < ratioH ? ratioW : ratioH
+                let newWidth = image.size.width * ratio
+                let newHeight = image.size.height * ratio
+                let conditionLeft = newWidth*scrollView.zoomScale > imageVideoContainer.frame.width
+                let left = 0.5 * (conditionLeft ? newWidth - imageVideoContainer.frame.width : (scrollView.frame.width - scrollView.contentSize.width))
+                let conditioTop = newHeight*scrollView.zoomScale > imageVideoContainer.frame.height
                 
-        if let pictureInPictureController = currentViewController.ncplayer?.pictureInPictureController {
-            if pictureInPictureController.isPictureInPictureActive {
-                pictureInPictureController.stopPictureInPicture()
+                let top = 0.5 * (conditioTop ? newHeight - imageVideoContainer.frame.height : (scrollView.frame.height - scrollView.contentSize.height))
+                
+                scrollView.contentInset = UIEdgeInsets(top: top, left: left, bottom: top, right: left)
             }
-        }
-        
-        if currentScreenMode == .full {
-            
-            changeScreenMode(mode: .normal, enableTimerAutoHide: true)
-                        
         } else {
-            
-            changeScreenMode(mode: .full)
+            scrollView.contentInset = .zero
         }
     }
     
-    //
-    // LIVE PHOTO
-    //
-    @objc func didLongpressGestureEvent(gestureRecognizer: UITapGestureRecognizer) {
-        
-        if !currentViewController.metadata.livePhoto { return }
-        
-        if gestureRecognizer.state == .began {
-            
-            currentViewController.updateViewConstraints()
-            currentViewController.statusViewImage.isHidden = true
-            currentViewController.statusLabel.isHidden = true
-            
-            let fileName = (currentViewController.metadata.fileNameView as NSString).deletingPathExtension + ".mov"
-            if let metadata = NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileNameView LIKE[c] %@", currentViewController.metadata.account, currentViewController.metadata.serverUrl, fileName)) {
-                
-                if CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView) {
-                    
-                    AudioServicesPlaySystemSound(1519) // peek feedback
-                    
-                    if let url = NCKTVHTTPCache.shared.getVideoURL(metadata: metadata) {
-                        self.ncplayerLivePhoto = NCPlayer.init(url: url, autoPlay: true, imageVideoContainer: self.currentViewController.imageVideoContainer, playerToolBar: nil, metadata: metadata, detailView: nil)
-                    }
-                    
-                } else {
-                    
-                    let serverUrlFileName = metadata.serverUrl + "/" + metadata.fileNameView
-                    let fileNameLocalPath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)!
-                                    
-                    NCCommunication.shared.download(serverUrlFileName: serverUrlFileName, fileNameLocalPath: fileNameLocalPath, requestHandler: { (_) in
-                        
-                    }, taskHandler: { (_) in
-                        
-                    }, progressHandler: { (progress) in
-                                        
-                        self.progressView.progress = Float(progress.fractionCompleted)
-                        
-                    }) { (account, etag, date, length, allHeaderFields, error, errorCode, errorDescription) in
-                        
-                        self.progressView.progress = 0
-                        
-                        if errorCode == 0 && account == metadata.account {
-                            
-                            NCManageDatabase.shared.addLocalFile(metadata: metadata)
-                            
-                            if gestureRecognizer.state == .changed || gestureRecognizer.state == .began {
-                                AudioServicesPlaySystemSound(1519) // peek feedback
-                                
-                                if let url = NCKTVHTTPCache.shared.getVideoURL(metadata: metadata) {
-                                    self.ncplayerLivePhoto = NCPlayer.init(url: url, autoPlay: true, imageVideoContainer: self.currentViewController.imageVideoContainer, playerToolBar: nil, metadata: metadata, detailView: nil)
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-            
-        } else if gestureRecognizer.state == .ended {
-            
-            currentViewController.statusViewImage.isHidden = false
-            currentViewController.statusLabel.isHidden = false
-            self.ncplayerLivePhoto?.deactivateObserver(livePhoto: true)
-        }
+    func scrollViewDidScroll(_ scrollView: UIScrollView) {
+    }
+}
+
+extension NCViewerMedia: NCViewerMediaDetailViewDelegate  {
+    
+    func downloadFullResolution() {
+        closeDetail()
+        NCNetworking.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorOpenDetail) { (_) in }
+    }
+}
+
+//MARK: -
+
+class imageVideoContainerView: UIImageView {
+    var playerLayer: CALayer?
+    var metadata: tableMetadata?
+    override func layoutSublayers(of layer: CALayer) {
+        super.layoutSublayers(of: layer)
+        playerLayer?.frame = self.bounds
     }
 }

+ 6 - 6
iOSClient/Viewer/NCViewerMedia/NCViewerMedia.storyboard → iOSClient/Viewer/NCViewerMedia/NCViewerMediaPage.storyboard

@@ -10,10 +10,10 @@
         <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
     </dependencies>
     <scenes>
-        <!--Viewer Media-->
+        <!--Viewer Media Page-->
         <scene sceneID="yQe-Pb-BKZ">
             <objects>
-                <viewController automaticallyAdjustsScrollViewInsets="NO" hidesBottomBarWhenPushed="YES" id="ne8-hS-cp3" customClass="NCViewerMedia" customModule="Nextcloud" customModuleProvider="target" sceneMemberID="viewController">
+                <viewController automaticallyAdjustsScrollViewInsets="NO" hidesBottomBarWhenPushed="YES" id="ne8-hS-cp3" customClass="NCViewerMediaPage" customModule="Nextcloud" customModuleProvider="target" sceneMemberID="viewController">
                     <layoutGuides>
                         <viewControllerLayoutGuide type="top" id="8k6-KQ-iNa"/>
                         <viewControllerLayoutGuide type="bottom" id="baP-ZX-MR4"/>
@@ -65,10 +65,10 @@
             </objects>
             <point key="canvasLocation" x="3748" y="777.66116941529242"/>
         </scene>
-        <!--Viewer Media Zoom-->
+        <!--Viewer Media-->
         <scene sceneID="rpT-vc-Kiv">
             <objects>
-                <viewController storyboardIdentifier="NCViewerMediaZoom" automaticallyAdjustsScrollViewInsets="NO" id="gXy-ag-ank" customClass="NCViewerMediaZoom" customModule="Nextcloud" customModuleProvider="target" sceneMemberID="viewController">
+                <viewController storyboardIdentifier="NCViewerMedia" automaticallyAdjustsScrollViewInsets="NO" id="gXy-ag-ank" customClass="NCViewerMedia" customModule="Nextcloud" customModuleProvider="target" sceneMemberID="viewController">
                     <layoutGuides>
                         <viewControllerLayoutGuide type="top" id="IQm-1K-tBr"/>
                         <viewControllerLayoutGuide type="bottom" id="zwn-Sc-mqc"/>
@@ -80,7 +80,7 @@
                             <scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" maximumZoomScale="4" translatesAutoresizingMaskIntoConstraints="NO" id="CdQ-LC-Trx">
                                 <rect key="frame" x="0.0" y="0.0" width="414" height="736"/>
                                 <subviews>
-                                    <imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="logo" translatesAutoresizingMaskIntoConstraints="NO" id="kPV-JM-UnM" customClass="imageVideoContainerView" customModule="Nextcloud" customModuleProvider="target">
+                                    <imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="networkInProgress" translatesAutoresizingMaskIntoConstraints="NO" id="kPV-JM-UnM" customClass="imageVideoContainerView" customModule="Nextcloud" customModuleProvider="target">
                                         <rect key="frame" x="0.0" y="0.0" width="414" height="736"/>
                                     </imageView>
                                     <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="2AU-85-K8y">
@@ -451,7 +451,7 @@
         <image name="audioOn" width="28" height="28"/>
         <image name="gobackward.10" catalog="system" width="121" height="128"/>
         <image name="goforward.10" catalog="system" width="121" height="128"/>
-        <image name="logo" width="256" height="128"/>
+        <image name="networkInProgress" width="300" height="300"/>
         <image name="pip.enter" catalog="system" width="128" height="96"/>
         <image name="play.fill" catalog="system" width="116" height="128"/>
         <systemColor name="systemBackgroundColor">

+ 587 - 0
iOSClient/Viewer/NCViewerMedia/NCViewerMediaPage.swift

@@ -0,0 +1,587 @@
+//
+//  NCViewerMediaPage.swift
+//  Nextcloud
+//
+//  Created by Marino Faggiana on 24/10/2020.
+//  Copyright © 2020 Marino Faggiana. All rights reserved.
+//
+//  Author Marino Faggiana <marino.faggiana@nextcloud.com>
+//
+//  This program is free software: you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation, either version 3 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+import UIKit
+import NCCommunication
+import MediaPlayer
+
+class NCViewerMediaPage: UIViewController {
+
+    @IBOutlet weak var progressView: UIProgressView!
+    
+    enum ScreenMode {
+        case full, normal
+    }
+    var currentScreenMode: ScreenMode = .normal
+    var saveScreenModeImage: ScreenMode = .normal
+
+    var pageViewController: UIPageViewController {
+        return self.children[0] as! UIPageViewController
+    }
+    
+    var currentViewController: NCViewerMedia {
+        return self.pageViewController.viewControllers![0] as! NCViewerMedia
+    }
+    
+    let appDelegate = UIApplication.shared.delegate as! AppDelegate
+
+    var metadatas: [tableMetadata] = []
+    var currentIndex = 0
+    var nextIndex: Int?
+    var IndexInPlay: Int = -1
+    var ncplayerLivePhoto: NCPlayer?
+    var panGestureRecognizer: UIPanGestureRecognizer!
+    var singleTapGestureRecognizer: UITapGestureRecognizer!
+    var longtapGestureRecognizer: UILongPressGestureRecognizer!
+    var textColor: UIColor = NCBrandColor.shared.label    
+    var playCommand: Any?
+    var pauseCommand: Any?
+    var skipForwardCommand: Any?
+    var skipBackwardCommand: Any?
+    var nextTrackCommand: Any?
+    var previousTrackCommand: Any?
+    
+
+    // MARK: - View Life Cycle
+
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        
+        navigationItem.rightBarButtonItem = UIBarButtonItem.init(image: UIImage(named: "more")!.image(color: NCBrandColor.shared.label, size: 25), style: .plain, target: self, action: #selector(self.openMenuMore))
+        
+        singleTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(didSingleTapWith(gestureRecognizer:)))
+        panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(didPanWith(gestureRecognizer:)))
+        longtapGestureRecognizer = UILongPressGestureRecognizer()
+        longtapGestureRecognizer.delaysTouchesBegan = true
+        longtapGestureRecognizer.minimumPressDuration = 0.3
+        longtapGestureRecognizer.delegate = self
+        longtapGestureRecognizer.addTarget(self, action: #selector(didLongpressGestureEvent(gestureRecognizer:)))
+        
+        pageViewController.delegate = self
+        pageViewController.dataSource = self
+        pageViewController.view.addGestureRecognizer(panGestureRecognizer)
+        pageViewController.view.addGestureRecognizer(singleTapGestureRecognizer)
+        pageViewController.view.addGestureRecognizer(longtapGestureRecognizer)
+        
+        let viewerMedia = getViewerMedia(index: currentIndex, metadata: metadatas[currentIndex], direction: .forward)
+        pageViewController.setViewControllers([viewerMedia], direction: .forward, animated: true, completion: nil)
+        
+        NotificationCenter.default.addObserver(self, selector: #selector(changeTheming), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterChangeTheming), object: nil)
+        NotificationCenter.default.addObserver(self, selector: #selector(viewUnload), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterMenuDetailClose), object: nil)
+        
+        progressView.tintColor = NCBrandColor.shared.brandElement
+        progressView.trackTintColor = .clear
+        progressView.progress = 0
+        
+        NotificationCenter.default.addObserver(self, selector: #selector(deleteFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterDeleteFile), object: nil)
+        NotificationCenter.default.addObserver(self, selector: #selector(renameFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterRenameFile), object: nil)
+        NotificationCenter.default.addObserver(self, selector: #selector(moveFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterMoveFile), object: nil)
+
+        NotificationCenter.default.addObserver(self, selector: #selector(downloadedFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterDownloadedFile), object: nil)
+        NotificationCenter.default.addObserver(self, selector: #selector(triggerProgressTask(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterProgressTask), object:nil)
+        
+        NotificationCenter.default.addObserver(self, selector: #selector(hidePlayerToolBar(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterHidePlayerToolBar), object: nil)
+        NotificationCenter.default.addObserver(self, selector: #selector(showPlayerToolBar(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterShowPlayerToolBar), object: nil)
+        
+        NotificationCenter.default.addObserver(self, selector: #selector(applicationDidBecomeActive(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterApplicationDidBecomeActive), object: nil)
+    }
+    
+    override func viewDidDisappear(_ animated: Bool) {
+        super.viewDidDisappear(animated)
+        
+        // Clear
+        if let ncplayer = currentViewController.ncplayer, ncplayer.isPlay() {
+            ncplayer.playerPause()
+            ncplayer.saveCurrentTime()
+        }
+        currentViewController.playerToolBar.stopTimerAutoHide()
+        clearCommandCenter()
+        
+        metadatas.removeAll()
+        ncplayerLivePhoto = nil
+        
+        // Remove Observer
+        NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterDeleteFile), object: nil)
+        NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterRenameFile), object: nil)
+        NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterMoveFile), object: nil)
+
+        NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterDownloadedFile), object: nil)
+        NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterProgressTask), object: nil)
+        
+        NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterHidePlayerToolBar), object: nil)
+        NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterShowPlayerToolBar), object: nil)
+        
+        NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterApplicationDidBecomeActive), object: nil)
+    }
+    
+    override var preferredStatusBarStyle: UIStatusBarStyle {
+        
+        if currentScreenMode == .normal {
+            return .default
+        } else {
+            return .lightContent
+        }
+    }
+    
+    // MARK: -
+    
+    func getViewerMedia(index: Int, metadata: tableMetadata, direction: UIPageViewController.NavigationDirection) -> NCViewerMedia {
+        
+        let viewerMedia = UIStoryboard(name: "NCViewerMediaPage", bundle: nil).instantiateViewController(withIdentifier: "NCViewerMedia") as! NCViewerMedia
+        viewerMedia.index = index
+        viewerMedia.metadata = metadata
+        viewerMedia.viewerMediaPage = self
+
+        singleTapGestureRecognizer.require(toFail: viewerMedia.doubleTapGestureRecognizer)
+       
+        return viewerMedia
+    }
+
+    @objc func viewUnload() {
+        
+        navigationController?.popViewController(animated: true)
+    }
+    
+    @objc func openMenuMore() {
+        
+        let imageIcon = UIImage(contentsOfFile: CCUtility.getDirectoryProviderStorageIconOcId(currentViewController.metadata.ocId, etag: currentViewController.metadata.etag))
+        NCViewer.shared.toggleMenu(viewController: self, metadata: currentViewController.metadata, webView: false, imageIcon: imageIcon)
+    }
+    
+    func changeScreenMode(mode: ScreenMode, enableTimerAutoHide: Bool = false) {
+        
+        if mode == .normal {
+            
+            navigationController?.setNavigationBarHidden(false, animated: true)
+            progressView.isHidden = false
+
+            if !currentViewController.detailView.isShow() {
+                currentViewController.playerToolBar.show(enableTimerAutoHide: enableTimerAutoHide)
+            }
+            
+            NCUtility.shared.colorNavigationController(navigationController, backgroundColor: NCBrandColor.shared.systemBackground, titleColor: NCBrandColor.shared.label, tintColor: nil, withoutShadow: false)
+            view.backgroundColor = NCBrandColor.shared.systemBackground
+            textColor = NCBrandColor.shared.label
+            
+        } else {
+            
+            navigationController?.setNavigationBarHidden(true, animated: true)
+            progressView.isHidden = true
+            
+            currentViewController.playerToolBar.hide()
+
+            view.backgroundColor = .black
+            textColor = .white
+        }
+        
+        currentScreenMode = mode
+        
+        if currentViewController.metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue {
+            saveScreenModeImage = mode
+        }
+        
+        setNeedsStatusBarAppearanceUpdate()
+        currentViewController.reloadDetail()
+    }
+    
+    //MARK: - NotificationCenter
+
+    @objc func downloadedFile(_ notification: NSNotification) {
+        
+        progressView.progress = 0
+    }
+    
+    @objc func triggerProgressTask(_ notification: NSNotification) {
+        
+        if let userInfo = notification.userInfo as NSDictionary? {
+            if let progressNumber = userInfo["progress"] as? NSNumber {
+                let progress = progressNumber.floatValue
+                if progress == 1 {
+                    self.progressView.progress = 0
+                } else {
+                    self.progressView.progress = progress
+                }
+            }
+        }
+    }
+    
+    @objc func deleteFile(_ notification: NSNotification) {
+        
+        if let userInfo = notification.userInfo as NSDictionary? {
+            if let ocId = userInfo["ocId"] as? String {
+                
+                let metadatas = self.metadatas.filter { $0.ocId != ocId }
+                if self.metadatas.count == metadatas.count { return }
+                self.metadatas = metadatas
+                
+                if ocId == currentViewController.metadata.ocId {
+                    shiftCurrentPage()
+                }
+            }
+        }
+    }
+    
+    @objc func renameFile(_ notification: NSNotification) {
+        
+        if let userInfo = notification.userInfo as NSDictionary? {
+            if let ocId = userInfo["ocId"] as? String, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
+                
+                if let index = metadatas.firstIndex(where: {$0.ocId == metadata.ocId}) {
+                    metadatas[index] = metadata
+                    if index == currentIndex {
+                        navigationItem.title = metadata.fileNameView
+                        currentViewController.metadata = metadata
+                        self.currentViewController.metadata = metadata
+                    }
+                }
+            }
+        }
+    }
+    
+    @objc func moveFile(_ notification: NSNotification) {
+        
+        if let userInfo = notification.userInfo as NSDictionary? {
+            if let ocId = userInfo["ocId"] as? String, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
+                
+                if metadatas.firstIndex(where: {$0.ocId == metadata.ocId}) != nil {
+                    deleteFile(notification)
+                }
+            }
+        }
+    }
+    
+    @objc func changeTheming() {
+    }
+    
+    @objc func hidePlayerToolBar(_ notification: NSNotification) {
+        
+        if let userInfo = notification.userInfo as NSDictionary?, let ocId = userInfo["ocId"] as? String {
+            if currentViewController.metadata.ocId == ocId {
+                changeScreenMode(mode: .full)
+            }
+        }
+    }
+    
+    @objc func showPlayerToolBar(_ notification: NSNotification) {
+        
+        if let userInfo = notification.userInfo as NSDictionary?, let ocId = userInfo["ocId"] as? String, let enableTimerAutoHide = userInfo["enableTimerAutoHide"] as? Bool{
+            if currentViewController.metadata.ocId == ocId, let playerToolBar = currentViewController.playerToolBar, !playerToolBar.isPictureInPictureActive() {
+                changeScreenMode(mode: .normal, enableTimerAutoHide: enableTimerAutoHide)
+            }
+        }
+    }
+    
+    @objc func applicationDidBecomeActive(_ notification: NSNotification) {
+        
+        progressView.progress = 0
+    }
+    
+    // MARK: - Command Center
+
+    func updateCommandCenter(ncplayer: NCPlayer, metadata: tableMetadata) {
+
+        var nowPlayingInfo = [String : Any]()
+
+        // Clear
+        clearCommandCenter()
+        UIApplication.shared.beginReceivingRemoteControlEvents()
+
+        // Add handler for Play Command
+        MPRemoteCommandCenter.shared().playCommand.isEnabled = true
+        playCommand = MPRemoteCommandCenter.shared().playCommand.addTarget { event in
+            
+            if !ncplayer.isPlay() {
+                ncplayer.playerPlay()
+                return .success
+            }
+            return .commandFailed
+        }
+      
+        // Add handler for Pause Command
+        MPRemoteCommandCenter.shared().pauseCommand.isEnabled = true
+        pauseCommand = MPRemoteCommandCenter.shared().pauseCommand.addTarget { event in
+          
+            if ncplayer.isPlay() {
+                ncplayer.playerPause()
+                return .success
+            }
+            return .commandFailed
+        }
+        
+        // VIDEO / AUDIO () ()
+        if metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue || metadata.classFile == NCCommunicationCommon.typeClassFile.audio.rawValue {
+            
+            MPRemoteCommandCenter.shared().skipForwardCommand.isEnabled = true
+            skipForwardCommand = MPRemoteCommandCenter.shared().skipForwardCommand.addTarget { event in
+                
+                let seconds = Float64((event as! MPSkipIntervalCommandEvent).interval)
+                self.currentViewController.playerToolBar.skip(seconds: seconds)
+                return.success
+            }
+            
+            MPRemoteCommandCenter.shared().skipBackwardCommand.isEnabled = true
+            skipBackwardCommand = MPRemoteCommandCenter.shared().skipBackwardCommand.addTarget { event in
+                
+                let seconds = Float64((event as! MPSkipIntervalCommandEvent).interval)
+                self.currentViewController.playerToolBar.skip(seconds: -seconds)
+                return.success
+            }
+        }
+                
+        // AUDIO < >
+        /*
+        if metadata?.classFile == NCCommunicationCommon.typeClassFile.audio.rawValue {
+                        
+            MPRemoteCommandCenter.shared().nextTrackCommand.isEnabled = true
+            appDelegate.nextTrackCommand = MPRemoteCommandCenter.shared().nextTrackCommand.addTarget { event in
+                
+                self.forward()
+                return .success
+            }
+            
+            MPRemoteCommandCenter.shared().previousTrackCommand.isEnabled = true
+            appDelegate.previousTrackCommand = MPRemoteCommandCenter.shared().previousTrackCommand.addTarget { event in
+             
+                self.backward()
+                return .success
+            }
+        }
+        */
+        
+        nowPlayingInfo[MPMediaItemPropertyTitle] = metadata.fileNameView
+        nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = ncplayer.durationTime.seconds
+        if let image = currentViewController.image {
+            nowPlayingInfo[MPMediaItemPropertyArtwork] = MPMediaItemArtwork(boundsSize: image.size) { size in
+                return image
+            }
+        }
+        MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
+    }
+
+    func clearCommandCenter() {
+        
+        UIApplication.shared.endReceivingRemoteControlEvents()
+        MPNowPlayingInfoCenter.default().nowPlayingInfo = [:]
+
+        MPRemoteCommandCenter.shared().playCommand.isEnabled = false
+        MPRemoteCommandCenter.shared().pauseCommand.isEnabled = false
+        MPRemoteCommandCenter.shared().skipForwardCommand.isEnabled = false
+        MPRemoteCommandCenter.shared().skipBackwardCommand.isEnabled = false
+        MPRemoteCommandCenter.shared().nextTrackCommand.isEnabled = false
+        MPRemoteCommandCenter.shared().previousTrackCommand.isEnabled = false
+
+        if let playCommand = playCommand {
+            MPRemoteCommandCenter.shared().playCommand.removeTarget(playCommand)
+            self.playCommand = nil
+        }
+        if let pauseCommand = pauseCommand {
+            MPRemoteCommandCenter.shared().pauseCommand.removeTarget(pauseCommand)
+            self.pauseCommand = nil
+        }
+        if let skipForwardCommand = skipForwardCommand {
+            MPRemoteCommandCenter.shared().skipForwardCommand.removeTarget(skipForwardCommand)
+            self.skipForwardCommand = nil
+        }
+        if let skipBackwardCommand = skipBackwardCommand {
+            MPRemoteCommandCenter.shared().skipBackwardCommand.removeTarget(skipBackwardCommand)
+            self.skipBackwardCommand = nil
+        }
+        if let nextTrackCommand = nextTrackCommand {
+            MPRemoteCommandCenter.shared().nextTrackCommand.removeTarget(nextTrackCommand)
+            self.nextTrackCommand = nil
+        }
+        if let previousTrackCommand = previousTrackCommand {
+            MPRemoteCommandCenter.shared().previousTrackCommand.removeTarget(previousTrackCommand)
+            self.previousTrackCommand = nil
+        }
+    }
+}
+
+// MARK: - UIPageViewController Delegate Datasource
+
+extension NCViewerMediaPage: UIPageViewControllerDelegate, UIPageViewControllerDataSource {
+    
+    func shiftCurrentPage() {
+        
+        if metadatas.count == 0 {
+            self.viewUnload()
+            return
+        }
+        
+        var direction: UIPageViewController.NavigationDirection = .forward
+        
+        if currentIndex == metadatas.count {
+            currentIndex -= 1
+            direction = .reverse
+        }
+        
+        currentViewController.ncplayer?.deactivateObserver()
+        
+        let viewerMedia = getViewerMedia(index: currentIndex, metadata: metadatas[currentIndex], direction: direction)
+        pageViewController.setViewControllers([viewerMedia], direction: direction, animated: true, completion: nil)
+    }
+    
+    func goTo(index: Int, direction: UIPageViewController.NavigationDirection, autoPlay: Bool) {
+        
+        currentIndex = index
+        
+        currentViewController.ncplayer?.deactivateObserver()
+
+        let viewerMedia = getViewerMedia(index: currentIndex, metadata: metadatas[currentIndex], direction: direction)
+        viewerMedia.autoPlay = autoPlay
+        pageViewController.setViewControllers([viewerMedia], direction: direction, animated: true, completion: nil)
+    }
+    
+    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
+        
+        if currentIndex == 0 { return nil }
+
+        let viewerMedia = getViewerMedia(index: currentIndex-1, metadata: metadatas[currentIndex-1], direction: .reverse)
+        return viewerMedia
+    }
+    
+    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
+        
+        if currentIndex == metadatas.count-1 { return nil }
+
+        let viewerMedia = getViewerMedia(index: currentIndex+1, metadata: metadatas[currentIndex+1], direction: .forward)
+        return viewerMedia
+    }
+    
+    // START TRANSITION
+    func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) {
+        
+        // Save time video
+        if let ncplayer = currentViewController.ncplayer, ncplayer.isPlay() {
+            ncplayer.saveCurrentTime()
+        }
+        
+        guard let nextViewController = pendingViewControllers.first as? NCViewerMedia else { return }
+        nextIndex = nextViewController.index        
+    }
+    
+    // END TRANSITION
+    func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
+        
+        if (completed && nextIndex != nil) {
+            previousViewControllers.forEach { viewController in
+                let viewerMedia = viewController as! NCViewerMedia
+                viewerMedia.ncplayer?.deactivateObserver()
+            }
+            currentIndex = nextIndex!
+        }
+        
+        self.nextIndex = nil
+    }
+}
+
+//MARK: - UIGestureRecognizerDelegate
+
+extension NCViewerMediaPage: UIGestureRecognizerDelegate {
+
+    func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
+        
+        if let gestureRecognizer = gestureRecognizer as? UIPanGestureRecognizer {
+            let velocity = gestureRecognizer.velocity(in: self.view)
+            
+            var velocityCheck : Bool = false
+            
+            if UIDevice.current.orientation.isLandscape {
+                velocityCheck = velocity.x < 0
+            }
+            else {
+                velocityCheck = velocity.y < 0
+            }
+            if velocityCheck {
+                return false
+            }
+        }
+        
+        return true
+    }
+    
+    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
+        
+        if otherGestureRecognizer == currentViewController.scrollView.panGestureRecognizer {
+            if self.currentViewController.scrollView.contentOffset.y == 0 {
+                return true
+            }
+        }
+        
+        return false
+    }
+
+    @objc func didPanWith(gestureRecognizer: UIPanGestureRecognizer) {
+        
+        currentViewController.didPanWith(gestureRecognizer: gestureRecognizer)
+    }
+    
+    @objc func didSingleTapWith(gestureRecognizer: UITapGestureRecognizer) {
+                
+        if let playerToolBar = currentViewController.playerToolBar, playerToolBar.isPictureInPictureActive() {
+            playerToolBar.pictureInPictureController?.stopPictureInPicture()
+        }
+        
+        if currentScreenMode == .full {
+            
+            changeScreenMode(mode: .normal, enableTimerAutoHide: true)
+                        
+        } else {
+            
+            changeScreenMode(mode: .full)
+        }
+    }
+    
+    //
+    // LIVE PHOTO
+    //
+    @objc func didLongpressGestureEvent(gestureRecognizer: UITapGestureRecognizer) {
+        
+        if !currentViewController.metadata.livePhoto { return }
+        
+        if gestureRecognizer.state == .began {
+            
+            currentViewController.updateViewConstraints()
+            currentViewController.statusViewImage.isHidden = true
+            currentViewController.statusLabel.isHidden = true
+            
+            let fileName = (currentViewController.metadata.fileNameView as NSString).deletingPathExtension + ".mov"
+            if let metadata = NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileNameView LIKE[c] %@", currentViewController.metadata.account, currentViewController.metadata.serverUrl, fileName)), CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView) {
+                
+                AudioServicesPlaySystemSound(1519) // peek feedback
+                
+                if let url = NCKTVHTTPCache.shared.getVideoURL(metadata: metadata) {
+                    self.ncplayerLivePhoto = NCPlayer.init(url: url, autoPlay: true, imageVideoContainer: self.currentViewController.imageVideoContainer, playerToolBar: nil, metadata: metadata, detailView: nil)
+                }
+            }
+            
+        } else if gestureRecognizer.state == .ended {
+            
+            currentViewController.statusViewImage.isHidden = false
+            currentViewController.statusLabel.isHidden = false
+            currentViewController.imageVideoContainer.image = currentViewController.image
+            ncplayerLivePhoto?.videoLayer?.removeFromSuperlayer()
+            ncplayerLivePhoto?.deactivateObserver()
+        }
+    }
+}

+ 0 - 447
iOSClient/Viewer/NCViewerMedia/NCViewerMediaZoom.swift

@@ -1,447 +0,0 @@
-//
-//  NCViewerMediaZoom.swift
-//  Nextcloud
-//
-//  Created by Marino Faggiana on 24/10/2020.
-//  Copyright © 2020 Marino Faggiana. All rights reserved.
-//
-//  Author Marino Faggiana <marino.faggiana@nextcloud.com>
-//
-//  This program is free software: you can redistribute it and/or modify
-//  it under the terms of the GNU General Public License as published by
-//  the Free Software Foundation, either version 3 of the License, or
-//  (at your option) any later version.
-//
-//  This program is distributed in the hope that it will be useful,
-//  but WITHOUT ANY WARRANTY; without even the implied warranty of
-//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-//  GNU General Public License for more details.
-//
-//  You should have received a copy of the GNU General Public License
-//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
-//
-
-import UIKit
-import NCCommunication
-
-class NCViewerMediaZoom: UIViewController {
-    
-    @IBOutlet weak var detailViewTopConstraint: NSLayoutConstraint!
-    @IBOutlet weak var detailViewHeighConstraint: NSLayoutConstraint!
-    @IBOutlet weak var imageViewTopConstraint: NSLayoutConstraint!
-    @IBOutlet weak var imageViewBottomConstraint: NSLayoutConstraint!
-    @IBOutlet weak var scrollView: UIScrollView!
-    @IBOutlet weak var imageVideoContainer: imageVideoContainerView!
-    @IBOutlet weak var statusViewImage: UIImageView!
-    @IBOutlet weak var statusLabel: UILabel!
-    @IBOutlet weak var detailView: NCViewerMediaDetailView!
-    @IBOutlet weak var playerToolBar: NCPlayerToolBar!
-    
-    private var _autoPlay: Bool = false
-
-    let appDelegate = UIApplication.shared.delegate as! AppDelegate
-    var viewerMedia: NCViewerMedia?
-    var ncplayer: NCPlayer?
-    var image: UIImage?
-    var metadata: tableMetadata = tableMetadata()
-    var index: Int = 0
-    var doubleTapGestureRecognizer: UITapGestureRecognizer = UITapGestureRecognizer()
-    var imageViewConstraint: CGFloat = 0
-    var isDetailViewInitializze: Bool = false
-    
-    var autoPlay: Bool {
-        get {
-            let temp = _autoPlay
-            _autoPlay = false
-            return temp
-        }
-        set(newVal) {
-            _autoPlay = newVal
-        }
-    }
-    
-    // MARK: - View Life Cycle
-
-    required init?(coder aDecoder: NSCoder) {
-        super.init(coder: aDecoder)
-        
-        doubleTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(didDoubleTapWith(gestureRecognizer:)))
-        doubleTapGestureRecognizer.numberOfTapsRequired = 2
-    }
-    
-    deinit {
-        print("deinit NCViewerMediaZoom")
-        
-        NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterOpenMediaDetail), object: nil)
-    }
-    
-    override func viewDidLoad() {
-        super.viewDidLoad()
-        
-        scrollView.delegate = self
-        scrollView.maximumZoomScale = 4
-        scrollView.minimumZoomScale = 1
-        
-        view.addGestureRecognizer(doubleTapGestureRecognizer)
-        
-        if metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue {
-            if image == nil {
-                image = UIImage.init(named: "noPreviewVideo")!.image(color: .gray, size: view.frame.width)
-            }
-            imageVideoContainer.image = image
-            imageVideoContainer.sourceImage = image
-            
-        } else if metadata.classFile == NCCommunicationCommon.typeClassFile.audio.rawValue {
-            if image == nil {
-                image = UIImage.init(named: "noPreviewAudio")!.image(color: .gray, size: view.frame.width)
-            }
-            imageVideoContainer.image = image
-            imageVideoContainer.sourceImage = image
-
-        } else {
-            if image == nil {
-                image = UIImage.init(named: "noPreview")!.image(color: .gray, size: view.frame.width)
-            }
-            imageVideoContainer.image = image
-            imageVideoContainer.sourceImage = image
-        }
-        
-        if NCManageDatabase.shared.getMetadataLivePhoto(metadata: metadata) != nil {
-            statusViewImage.image = NCUtility.shared.loadImage(named: "livephoto", color: .gray)
-            statusLabel.text = "LIVE"
-        }  else {
-            statusViewImage.image = nil
-            statusLabel.text = ""
-        }
-        
-        playerToolBar.viewerMedia = viewerMedia
-        
-        detailViewTopConstraint.constant = 0
-        detailView.hide()
-    }
-    
-    override func viewWillAppear(_ animated: Bool) {
-        super.viewWillAppear(animated)
-        
-        imageVideoContainer.image = image
-
-        viewerMedia?.navigationController?.navigationBar.prefersLargeTitles = false
-        viewerMedia?.navigationItem.title = metadata.fileNameView
-        
-        if metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue, let viewerMedia = self.viewerMedia {
-            viewerMedia.currentScreenMode = viewerMedia.saveScreenModeImage
-        }
-                
-        if viewerMedia?.currentScreenMode == .full {
-            
-            viewerMedia?.navigationController?.setNavigationBarHidden(true, animated: true)
-            
-            NCUtility.shared.colorNavigationController(viewerMedia?.navigationController, backgroundColor: .black, titleColor: .white, tintColor: nil, withoutShadow: false)
-            
-            viewerMedia?.view.backgroundColor = .black
-            viewerMedia?.textColor = .white
-            viewerMedia?.progressView.isHidden = true
-            
-        } else {
-            
-            viewerMedia?.navigationController?.setNavigationBarHidden(false, animated: true)
-                
-            NCUtility.shared.colorNavigationController(viewerMedia?.navigationController, backgroundColor: NCBrandColor.shared.systemBackground, titleColor: NCBrandColor.shared.label, tintColor: nil, withoutShadow: false)
-            
-            viewerMedia?.view.backgroundColor = NCBrandColor.shared.systemBackground
-            viewerMedia?.textColor = NCBrandColor.shared.label
-            viewerMedia?.progressView.isHidden = false
-        }
-    }
-    
-    override func viewDidAppear(_ animated: Bool) {
-        super.viewDidAppear(animated)
-        
-        if (metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue || metadata.classFile == NCCommunicationCommon.typeClassFile.audio.rawValue) {
-            
-            if ncplayer == nil, let url = NCKTVHTTPCache.shared.getVideoURL(metadata: metadata) {
-                self.ncplayer = NCPlayer.init(url: url, autoPlay: self.autoPlay, imageVideoContainer: self.imageVideoContainer, playerToolBar: self.playerToolBar, metadata: self.metadata, detailView: self.detailView)
-            } else {
-                self.ncplayer?.activateObserver(playerToolBar: self.playerToolBar)
-                if detailView.isShow() == false && ncplayer?.isPlay() == false {
-                    NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterShowPlayerToolBar, userInfo: ["ocId":metadata.ocId, "enableTimerAutoHide": false])
-                }
-            }
-        }
-        
-        // DOWNLOAD
-        downloadFile()
-        
-        NotificationCenter.default.addObserver(self, selector: #selector(openDetail(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterOpenMediaDetail), object: nil)
-    }
-    
-    override func viewDidDisappear(_ animated: Bool) {
-        super.viewDidDisappear(animated)
-    }
-    
-    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
-        super.viewWillTransition(to: size, with: coordinator)
-        
-        coordinator.animate(alongsideTransition: { (context) in
-            // back to the original size
-            self.scrollView.zoom(to: CGRect(x: 0, y: 0, width: self.scrollView.bounds.width, height: self.scrollView.bounds.height), animated: false)
-            self.view.layoutIfNeeded()
-            UIView.animate(withDuration: context.transitionDuration) {
-                if self.detailView.isShow() {
-                    self.openDetail()
-                }
-            }
-        }) { (_) in }
-    }
-    
-    func reload(image: UIImage, metadata: tableMetadata) {
-        
-        self.image = image
-
-        imageVideoContainer.image = image
-        imageVideoContainer.sourceImage = image
-        
-        self.metadata = metadata
-    }
-        
-    //MARK: - Gesture
-
-    @objc func didDoubleTapWith(gestureRecognizer: UITapGestureRecognizer) {
-        
-        if detailView.isShow() { return }
-        
-        // NO ZOOM for Audio / Video
-        if (metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue || metadata.classFile == NCCommunicationCommon.typeClassFile.audio.rawValue) && playerToolBar.isShow() {
-            return
-        }
-        
-        let pointInView = gestureRecognizer.location(in: self.imageVideoContainer)
-        var newZoomScale = self.scrollView.maximumZoomScale
-            
-        if self.scrollView.zoomScale >= newZoomScale || abs(self.scrollView.zoomScale - newZoomScale) <= 0.01 {
-            newZoomScale = self.scrollView.minimumZoomScale
-        }
-                
-        let width = self.scrollView.bounds.width / newZoomScale
-        let height = self.scrollView.bounds.height / newZoomScale
-        let originX = pointInView.x - (width / 2.0)
-        let originY = pointInView.y - (height / 2.0)
-        let rectToZoomTo = CGRect(x: originX, y: originY, width: width, height: height)
-        self.scrollView.zoom(to: rectToZoomTo, animated: true)
-    }
-      
-    @objc func didPanWith(gestureRecognizer: UIPanGestureRecognizer) {
-                
-        let currentLocation = gestureRecognizer.translation(in: self.view)
-        
-        switch gestureRecognizer.state {
-        
-        case .began:
-            
-//        let velocity = gestureRecognizer.velocity(in: self.view)
-
-//            gesture moving Up
-//            if velocity.y < 0 {
-
-//            }
-            break
-
-        case .ended:
-            
-            if detailView.isShow() {
-                self.imageViewTopConstraint.constant = -imageViewConstraint
-                self.imageViewBottomConstraint.constant = imageViewConstraint
-            } else {
-                self.imageViewTopConstraint.constant = 0
-                self.imageViewBottomConstraint.constant = 0
-            }
-
-        case .changed:
-                        
-            imageViewTopConstraint.constant = (currentLocation.y - imageViewConstraint)
-            imageViewBottomConstraint.constant = -(currentLocation.y - imageViewConstraint)
-            
-            // DISMISS VIEW
-            if detailView.isHidden && (currentLocation.y > 20) {
-                
-                viewerMedia?.navigationController?.popViewController(animated: true)
-                gestureRecognizer.state = .ended
-            }
-            
-            // CLOSE DETAIL
-            if !detailView.isHidden && (currentLocation.y > 20) {
-                               
-                self.closeDetail()
-                gestureRecognizer.state = .ended
-            }
-
-            // OPEN DETAIL
-            if detailView.isHidden && (currentLocation.y < -20) {
-                       
-                self.openDetail()
-                gestureRecognizer.state = .ended
-            }
-                        
-        default:
-            break
-        }
-    }
-}
-
-//MARK: -
-
-extension NCViewerMediaZoom {
-    
-    @objc func openDetail(_ notification: NSNotification) {
-        
-        if let userInfo = notification.userInfo as NSDictionary?, let ocId = userInfo["ocId"] as? String, ocId == metadata.ocId {
-            openDetail()
-        }
-    }
-    
-    private func openDetail() {
-        
-        CCUtility.setExif(metadata) { (latitude, longitude, location, date, lensModel) in
-            
-            if (latitude != -1 && latitude != 0 && longitude != -1 && longitude != 0) {
-                self.detailViewHeighConstraint.constant = self.view.bounds.height / 2
-            } else {
-                self.detailViewHeighConstraint.constant = 170
-            }
-            self.view.layoutIfNeeded()
-            
-            self.detailView.show(metadata:self.metadata, image: self.image, textColor: self.viewerMedia?.textColor, latitude: latitude, longitude: longitude, location: location, date: date, lensModel: lensModel, delegate: self)
-                
-            if let image = self.imageVideoContainer.image {
-                let ratioW = self.imageVideoContainer.frame.width / image.size.width
-                let ratioH = self.imageVideoContainer.frame.height / image.size.height
-                let ratio = ratioW < ratioH ? ratioW : ratioH
-                let imageHeight = image.size.height * ratio
-                self.imageViewConstraint = self.detailView.frame.height - ((self.view.frame.height - imageHeight) / 2) + self.view.safeAreaInsets.bottom
-                if self.imageViewConstraint < 0 { self.imageViewConstraint = 0 }
-            }
-                
-            UIView.animate(withDuration: 0.3) {
-                self.imageViewTopConstraint.constant = -self.imageViewConstraint
-                self.imageViewBottomConstraint.constant = self.imageViewConstraint
-                self.detailViewTopConstraint.constant = self.detailViewHeighConstraint.constant
-                self.view.layoutIfNeeded()
-            } completion: { (_) in
-            }
-                
-            self.scrollView.pinchGestureRecognizer?.isEnabled = false
-            self.playerToolBar.hide()
-        }
-    }
-    
-    private func closeDetail() {
-        
-        self.detailView.hide()
-        imageViewConstraint = 0
-        
-        UIView.animate(withDuration: 0.3) {
-            self.imageViewTopConstraint.constant = 0
-            self.imageViewBottomConstraint.constant = 0
-            self.detailViewTopConstraint.constant = 0
-            self.view.layoutIfNeeded()
-        } completion: { (_) in
-        }
-        
-        scrollView.pinchGestureRecognizer?.isEnabled = true
-        if metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue && !metadata.livePhoto && ncplayer?.player?.timeControlStatus == .paused {
-            NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterShowPlayerToolBar, userInfo: ["ocId":metadata.ocId, "enableTimerAutoHide": false])
-        }
-    }
-    
-    func reloadDetail() {
-        
-        if self.detailView.isShow() {
-            CCUtility.setExif(metadata) { (latitude, longitude, location, date, lensModel) in
-                self.detailView.show(metadata:self.metadata, image: self.image, textColor: self.viewerMedia?.textColor, latitude: latitude, longitude: longitude, location: location, date: date, lensModel: lensModel, delegate: self)
-            }
-        }
-    }
-    
-    func downloadFile() {
-        
-        let isFolderEncrypted = CCUtility.isFolderEncrypted(metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase)
-        let ext = CCUtility.getExtension(metadata.fileNameView)
-        
-        if !NCOperationQueue.shared.downloadExists(metadata: metadata) {
-            viewerMedia?.progressView.progress = 0
-        }
-        
-        // DOWNLOAD FILE
-        if ((metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue && CCUtility.getAutomaticDownloadImage()) || (metadata.contentType == "image/heic" &&  metadata.hasPreview == false) || ext == "GIF" || ext == "SVG" || isFolderEncrypted) && metadata.session == "" && !CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView) {
-            NCOperationQueue.shared.download(metadata: metadata, selector: "")
-        }
-        
-        // DOWNLOAD FILE LIVE PHOTO
-        if let metadataLivePhoto = NCManageDatabase.shared.getMetadataLivePhoto(metadata: metadata) {
-            if CCUtility.getAutomaticDownloadImage() && !CCUtility.fileProviderStorageExists(metadataLivePhoto.ocId, fileNameView: metadataLivePhoto.fileNameView) {
-                NCOperationQueue.shared.download(metadata: metadataLivePhoto, selector: "")
-            }
-        }
-        
-        // DOWNLOAD preview for image
-        if !CCUtility.fileProviderStoragePreviewIconExists(metadata.ocId, etag: metadata.etag) && metadata.hasPreview && metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue {
-            
-            NCOperationQueue.shared.downloadThumbnail(metadata: metadata, placeholder: false, cell: nil, view: nil)
-        }
-    }
-}
-
-//MARK: -
-
-extension NCViewerMediaZoom: UIScrollViewDelegate {
-    
-    func viewForZooming(in scrollView: UIScrollView) -> UIView? {
-        return imageVideoContainer
-    }
-    
-    func scrollViewDidZoom(_ scrollView: UIScrollView) {
-        
-        if scrollView.zoomScale > 1 {
-            if let image = imageVideoContainer.image {
-                
-                let ratioW = imageVideoContainer.frame.width / image.size.width
-                let ratioH = imageVideoContainer.frame.height / image.size.height
-                let ratio = ratioW < ratioH ? ratioW : ratioH
-                let newWidth = image.size.width * ratio
-                let newHeight = image.size.height * ratio
-                let conditionLeft = newWidth*scrollView.zoomScale > imageVideoContainer.frame.width
-                let left = 0.5 * (conditionLeft ? newWidth - imageVideoContainer.frame.width : (scrollView.frame.width - scrollView.contentSize.width))
-                let conditioTop = newHeight*scrollView.zoomScale > imageVideoContainer.frame.height
-                
-                let top = 0.5 * (conditioTop ? newHeight - imageVideoContainer.frame.height : (scrollView.frame.height - scrollView.contentSize.height))
-                
-                scrollView.contentInset = UIEdgeInsets(top: top, left: left, bottom: top, right: left)
-            }
-        } else {
-            scrollView.contentInset = .zero
-        }
-    }
-    
-    func scrollViewDidScroll(_ scrollView: UIScrollView) {
-    }
-}
-
-extension NCViewerMediaZoom: NCViewerMediaDetailViewDelegate  {
-    
-    func downloadFullResolution() {
-        closeDetail()
-        NCNetworking.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorOpenDetail) { (_) in }
-    }
-}
-
-//MARK: -
-
-class imageVideoContainerView: UIImageView {
-    var playerLayer: CALayer?
-    var metadata: tableMetadata?
-    var sourceImage: UIImage?
-    override func layoutSublayers(of layer: CALayer) {
-        super.layoutSublayers(of: layer)
-        playerLayer?.frame = self.bounds
-    }
-}