// // NCImageCache.swift // Nextcloud // // Created by Marino Faggiana on 18/10/23. // Copyright © 2021 Marino Faggiana. All rights reserved. // // Author Marino Faggiana // // 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 . // import Foundation import UIKit import LRUCache import NextcloudKit import RealmSwift class NCImageCache: NSObject { static let shared = NCImageCache() private let utility = NCUtility() private let global = NCGlobal.shared private let allowExtensions = [NCGlobal.shared.previewExt256] private var brandElementColor: UIColor? public var countLimit: Int = 0 lazy var cache: LRUCache = { return LRUCache(countLimit: countLimit) }() public var isLoadingCache: Bool = false override init() { super.init() countLimit = calculateMaxImages(percentage: 5.0, imageSizeKB: 30.0) // 5% of cache = 20 NextcloudKit.shared.nkCommonInstance.writeLog("Counter cache image: \(countLimit)") NotificationCenter.default.addObserver(forName: LRUCacheMemoryWarningNotification, object: nil, queue: nil) { _ in self.cache.removeAllValues() self.countLimit = self.countLimit - 500 if self.countLimit <= 0 { self.countLimit = 100 } self.cache = LRUCache(countLimit: self.countLimit) #if DEBUG NCContentPresenter().messageNotification("Cache image memory warning \(self.countLimit)", error: .success, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, priority: .max) #endif } NotificationCenter.default.addObserver(forName: UIApplication.didEnterBackgroundNotification, object: nil, queue: nil) { _ in autoreleasepool { self.cache.removeAllValues() } } NotificationCenter.default.addObserver(forName: UIApplication.willEnterForegroundNotification, object: nil, queue: nil) { _ in #if !EXTENSION var files: [NCFiles] = [] var cost: Int = 0 if let activeTableAccount = NCManageDatabase.shared.getActiveTableAccount(), NCImageCache.shared.cache.count == 0 { let session = NCSession.shared.getSession(account: activeTableAccount.account) for mainTabBarController in SceneManager.shared.getControllers() { if let currentVC = mainTabBarController.selectedViewController as? UINavigationController, let file = currentVC.visibleViewController as? NCFiles { files.append(file) } } /// MEDIA if let metadatas = NCManageDatabase.shared.getResultsMetadatas(predicate: self.getMediaPredicate(filterLivePhotoFile: true, session: session, showOnlyImages: false, showOnlyVideos: false), sortedByKeyPath: "date", freeze: true)?.prefix(self.countLimit) { self.cache.removeAllValues() self.isLoadingCache = true metadatas.forEach { metadata in if let image = self.utility.getImage(ocId: metadata.ocId, etag: metadata.etag, ext: NCGlobal.shared.previewExt256) { self.addImageCache(ocId: metadata.ocId, etag: metadata.etag, image: image, ext: NCGlobal.shared.previewExt256, cost: cost) cost += 1 } } self.isLoadingCache = false } /// FILE for file in files where !file.serverUrl.isEmpty { NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource, userInfo: ["serverUrl": file.serverUrl]) } } #endif } } deinit { NotificationCenter.default.removeObserver(self, name: LRUCacheMemoryWarningNotification, object: nil) } func calculateMaxImages(percentage: Double, imageSizeKB: Double) -> Int { let totalRamBytes = Double(ProcessInfo.processInfo.physicalMemory) let cacheSizeBytes = totalRamBytes * (percentage / 100.0) let imageSizeBytes = imageSizeKB * 1024 let maxImages = Int(cacheSizeBytes / imageSizeBytes) return maxImages } func allowExtensions(ext: String) -> Bool { return allowExtensions.contains(ext) } func addImageCache(ocId: String, etag: String, data: Data, ext: String, cost: Int) { guard allowExtensions.contains(ext), let image = UIImage(data: data) else { return } cache.setValue(image, forKey: ocId + etag + ext, cost: cost) } func addImageCache(ocId: String, etag: String, image: UIImage, ext: String, cost: Int) { guard allowExtensions.contains(ext) else { return } cache.setValue(image, forKey: ocId + etag + ext, cost: cost) } func getImageCache(ocId: String, etag: String, ext: String) -> UIImage? { return cache.value(forKey: ocId + etag + ext) } func removeImageCache(ocIdPlusEtag: String) { for i in 0.. NSPredicate { guard let tableAccount = NCManageDatabase.shared.getTableAccount(predicate: NSPredicate(format: "account == %@", session.account)) else { return NSPredicate() } var predicate = NSPredicate() let startServerUrl = NCUtilityFileSystem().getHomeServer(session: session) + tableAccount.mediaPath var showBothPredicateMediaString = "account == %@ AND serverUrl BEGINSWITH %@ AND hasPreview == true AND (classFile == '\(NKCommon.TypeClassFile.image.rawValue)' OR classFile == '\(NKCommon.TypeClassFile.video.rawValue)') AND NOT (status IN %@)" var showOnlyPredicateMediaString = "account == %@ AND serverUrl BEGINSWITH %@ AND hasPreview == true AND classFile == %@ AND NOT (status IN %@)" if filterLivePhotoFile { showBothPredicateMediaString = showBothPredicateMediaString + " AND NOT (livePhotoFile != '' AND classFile == '\(NKCommon.TypeClassFile.video.rawValue)')" showOnlyPredicateMediaString = showOnlyPredicateMediaString + " AND NOT (livePhotoFile != '' AND classFile == '\(NKCommon.TypeClassFile.video.rawValue)')" } if showOnlyImages { predicate = NSPredicate(format: showOnlyPredicateMediaString, session.account, startServerUrl, NKCommon.TypeClassFile.image.rawValue, global.metadataStatusHideInView) } else if showOnlyVideos { predicate = NSPredicate(format: showOnlyPredicateMediaString, session.account, startServerUrl, NKCommon.TypeClassFile.video.rawValue, global.metadataStatusHideInView) } else { predicate = NSPredicate(format: showBothPredicateMediaString, session.account, startServerUrl, global.metadataStatusHideInView) } return predicate } // MARK: - func getImageFile() -> UIImage { return utility.loadImage(named: "doc", colors: [NCBrandColor.shared.iconImageColor2]) } func getImageShared() -> UIImage { return utility.loadImage(named: "person.fill.badge.plus", colors: NCBrandColor.shared.iconImageMultiColors) } func getImageCanShare() -> UIImage { return utility.loadImage(named: "person.fill.badge.plus", colors: NCBrandColor.shared.iconImageMultiColors) } func getImageShareByLink() -> UIImage { return utility.loadImage(named: "link", colors: [NCBrandColor.shared.iconImageColor]) } func getImageFavorite() -> UIImage { return utility.loadImage(named: "star.fill", colors: [NCBrandColor.shared.yellowFavorite]) } func getImageOfflineFlag() -> UIImage { return utility.loadImage(named: "arrow.down.circle.fill", colors: [.systemGreen]) } func getImageLocal() -> UIImage { return utility.loadImage(named: "checkmark.circle.fill", colors: [.systemGreen]) } func getImageCheckedYes() -> UIImage { return utility.loadImage(named: "checkmark.circle.fill", colors: [NCBrandColor.shared.iconImageColor2]) } func getImageCheckedNo() -> UIImage { return utility.loadImage(named: "circle", colors: [NCBrandColor.shared.iconImageColor]) } func getImageButtonMore() -> UIImage { return utility.loadImage(named: "ellipsis", colors: [NCBrandColor.shared.iconImageColor]) } func getImageButtonStop() -> UIImage { return utility.loadImage(named: "stop.circle", colors: [NCBrandColor.shared.iconImageColor]) } func getImageButtonMoreLock() -> UIImage { return utility.loadImage(named: "lock.fill", colors: [NCBrandColor.shared.iconImageColor]) } func getFolder(account: String) -> UIImage { return UIImage(named: "folder")!.image(color: NCBrandColor.shared.getElement(account: account)) } func getFolderEncrypted(account: String) -> UIImage { return UIImage(named: "folderEncrypted")!.image(color: NCBrandColor.shared.getElement(account: account)) } func getFolderSharedWithMe(account: String) -> UIImage { return UIImage(named: "folder_shared_with_me")!.image(color: NCBrandColor.shared.getElement(account: account)) } func getFolderPublic(account: String) -> UIImage { return UIImage(named: "folder_public")!.image(color: NCBrandColor.shared.getElement(account: account)) } func getFolderGroup(account: String) -> UIImage { return UIImage(named: "folder_group")!.image(color: NCBrandColor.shared.getElement(account: account)) } func getFolderExternal(account: String) -> UIImage { return UIImage(named: "folder_external")!.image(color: NCBrandColor.shared.getElement(account: account)) } func getFolderAutomaticUpload(account: String) -> UIImage { return UIImage(named: "folderAutomaticUpload")!.image(color: NCBrandColor.shared.getElement(account: account)) } }