NCViewerMediaDetailView.swift 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. //
  2. // NCViewerMediaDetailView.swift
  3. // Nextcloud
  4. //
  5. // Created by Marino Faggiana on 31/10/2020.
  6. // Copyright © 2020 Marino Faggiana. All rights reserved.
  7. //
  8. // Author Marino Faggiana <marino.faggiana@nextcloud.com>
  9. //
  10. // This program is free software: you can redistribute it and/or modify
  11. // it under the terms of the GNU General Public License as published by
  12. // the Free Software Foundation, either version 3 of the License, or
  13. // (at your option) any later version.
  14. //
  15. // This program is distributed in the hope that it will be useful,
  16. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. // GNU General Public License for more details.
  19. //
  20. // You should have received a copy of the GNU General Public License
  21. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  22. //
  23. import UIKit
  24. import MapKit
  25. import NextcloudKit
  26. import Alamofire
  27. public protocol NCViewerMediaDetailViewDelegate: AnyObject {
  28. func downloadFullResolution()
  29. }
  30. class NCViewerMediaDetailView: UIView {
  31. @IBOutlet weak var mapContainer: UIView!
  32. @IBOutlet weak var outerMapContainer: UIView!
  33. @IBOutlet weak var dayLabel: UILabel!
  34. @IBOutlet weak var dateLabel: UILabel!
  35. @IBOutlet weak var noDateLabel: UILabel!
  36. @IBOutlet weak var timeLabel: UILabel!
  37. @IBOutlet weak var nameLabel: UILabel!
  38. @IBOutlet weak var modelLabel: UILabel!
  39. @IBOutlet weak var deviceContainer: UIView!
  40. @IBOutlet weak var outerContainer: UIView!
  41. @IBOutlet weak var lensLabel: UILabel!
  42. @IBOutlet weak var megaPixelLabel: UILabel!
  43. @IBOutlet weak var megaPixelLabelDivider: UILabel!
  44. @IBOutlet weak var resolutionLabel: UILabel!
  45. @IBOutlet weak var resolutionLabelDivider: UILabel!
  46. @IBOutlet weak var sizeLabel: UILabel!
  47. @IBOutlet weak var extensionLabel: UILabel!
  48. @IBOutlet weak var livePhotoImageView: UIImageView!
  49. @IBOutlet weak var isoLabel: UILabel!
  50. @IBOutlet weak var lensSizeLabel: UILabel!
  51. @IBOutlet weak var exposureValueLabel: UILabel!
  52. @IBOutlet weak var apertureLabel: UILabel!
  53. @IBOutlet weak var shutterSpeedLabel: UILabel!
  54. @IBOutlet weak var locationLabel: UILabel!
  55. @IBOutlet weak var downloadImageButton: UIButton!
  56. @IBOutlet weak var downloadImageLabel: UILabel!
  57. @IBOutlet weak var downloadImageButtonContainer: UIStackView!
  58. @IBOutlet weak var dateContainer: UIView!
  59. @IBOutlet weak var lensInfoStackViewLeadingConstraint: NSLayoutConstraint!
  60. @IBOutlet weak var lensInfoStackViewTrailingConstraint: NSLayoutConstraint!
  61. @IBOutlet weak var lensInfoLeadingFakePadding: UILabel!
  62. @IBOutlet weak var lensInfoTrailingFakePadding: UILabel!
  63. private var metadata: tableMetadata?
  64. private var mapView: MKMapView?
  65. private var ncplayer: NCPlayer?
  66. weak var delegate: NCViewerMediaDetailViewDelegate?
  67. let utilityFileSystem = NCUtilityFileSystem()
  68. private var exif: ExifData?
  69. var isShown: Bool {
  70. return !self.isHidden
  71. }
  72. deinit {
  73. print("deinit NCViewerMediaDetailView")
  74. self.mapView?.removeFromSuperview()
  75. self.mapView = nil
  76. }
  77. func show(metadata: tableMetadata,
  78. image: UIImage?,
  79. textColor: UIColor?,
  80. exif: ExifData,
  81. ncplayer: NCPlayer?,
  82. delegate: NCViewerMediaDetailViewDelegate?) {
  83. self.metadata = metadata
  84. self.exif = exif
  85. self.ncplayer = ncplayer
  86. self.delegate = delegate
  87. outerMapContainer.isHidden = true
  88. downloadImageButtonContainer.isHidden = true
  89. if let latitude = exif.latitude, let longitude = exif.longitude, NCNetworking.shared.isOnline {
  90. // We hide the map view on phones in landscape (aka compact height), since there is too little space to fit all of it.
  91. mapContainer.isHidden = traitCollection.verticalSizeClass == .compact
  92. outerMapContainer.isHidden = false
  93. let annotation = MKPointAnnotation()
  94. annotation.coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
  95. let region = MKCoordinateRegion(center: annotation.coordinate, latitudinalMeters: 500, longitudinalMeters: 500)
  96. if mapView == nil, mapView?.region.center.latitude != latitude, mapView?.region.center.longitude != longitude {
  97. let mapView = MKMapView()
  98. self.mapView = mapView
  99. mapContainer.subviews.forEach { $0.removeFromSuperview() }
  100. self.mapContainer.addSubview(mapView)
  101. mapView.translatesAutoresizingMaskIntoConstraints = false
  102. NSLayoutConstraint.activate([
  103. mapView.topAnchor.constraint(equalTo: self.mapContainer.topAnchor),
  104. mapView.bottomAnchor.constraint(equalTo: self.mapContainer.bottomAnchor),
  105. mapView.leadingAnchor.constraint(equalTo: self.mapContainer.leadingAnchor),
  106. mapView.trailingAnchor.constraint(equalTo: self.mapContainer.trailingAnchor)
  107. ])
  108. mapView.isZoomEnabled = true
  109. mapView.isScrollEnabled = false
  110. mapView.isUserInteractionEnabled = false
  111. mapView.addAnnotation(annotation)
  112. mapView.setRegion(region, animated: false)
  113. }
  114. }
  115. if let make = exif.make, let model = exif.model, let lensModel = exif.lensModel {
  116. modelLabel.text = "\(make) \(model)"
  117. lensLabel.text = lensModel
  118. .replacingOccurrences(of: make, with: "")
  119. .replacingOccurrences(of: model, with: "")
  120. .replacingOccurrences(of: "f/", with: "ƒ").trimmingCharacters(in: .whitespacesAndNewlines).firstUppercased
  121. } else {
  122. modelLabel.text = NSLocalizedString("_no_camera_information_", comment: "")
  123. lensLabel.text = NSLocalizedString("_no_lens_information_", comment: "")
  124. }
  125. nameLabel.text = (metadata.fileNameView as NSString).deletingPathExtension
  126. sizeLabel.text = utilityFileSystem.transformedSize(metadata.size)
  127. if let shutterSpeedApex = exif.shutterSpeedApex {
  128. prepareLensInfoViewsForData()
  129. shutterSpeedLabel.text = "1/\(Int(pow(2, shutterSpeedApex))) s"
  130. }
  131. if let iso = exif.iso {
  132. prepareLensInfoViewsForData()
  133. isoLabel.text = "ISO \(iso)"
  134. }
  135. if let apertureValue = exif.apertureValue {
  136. apertureLabel.text = "ƒ\(apertureValue)"
  137. }
  138. if let exposureValue = exif.exposureValue {
  139. exposureValueLabel.text = "\(exposureValue) ev"
  140. }
  141. if let lensLength = exif.lensLength {
  142. lensSizeLabel.text = "\(lensLength) mm"
  143. }
  144. if let date = exif.date {
  145. dateContainer.isHidden = false
  146. noDateLabel.isHidden = true
  147. let formatter = DateFormatter()
  148. formatter.dateFormat = "EEEE"
  149. let dayString = formatter.string(from: date as Date)
  150. dayLabel.text = dayString
  151. formatter.dateFormat = "d MMM yyyy"
  152. let dateString = formatter.string(from: date as Date)
  153. dateLabel.text = dateString
  154. formatter.dateFormat = "HH:mm"
  155. let timeString = formatter.string(from: date as Date)
  156. timeLabel.text = timeString
  157. } else {
  158. noDateLabel.text = NSLocalizedString("_no_date_information_", comment: "")
  159. }
  160. if let height = exif.height, let width = exif.width {
  161. megaPixelLabel.isHidden = false
  162. megaPixelLabelDivider.isHidden = false
  163. resolutionLabel.isHidden = false
  164. resolutionLabelDivider.isHidden = false
  165. resolutionLabel.text = "\(width) x \(height)"
  166. let megaPixels: Double = Double(width * height) / 1000000
  167. megaPixelLabel.text = megaPixels < 1 ? String(format: "%.1f MP", megaPixels) : "\(Int(megaPixels)) MP"
  168. }
  169. extensionLabel.text = metadata.fileExtension.uppercased()
  170. if exif.location?.isEmpty == false {
  171. locationLabel.text = exif.location
  172. }
  173. if metadata.isLivePhoto {
  174. livePhotoImageView.isHidden = false
  175. }
  176. if metadata.isImage && !utilityFileSystem.fileProviderStorageExists(metadata) && metadata.session.isEmpty {
  177. downloadImageButton.setTitle(NSLocalizedString("_try_download_full_resolution_", comment: ""), for: .normal)
  178. downloadImageLabel.text = NSLocalizedString("_full_resolution_image_info_", comment: "")
  179. downloadImageButtonContainer.isHidden = false
  180. }
  181. self.isHidden = false
  182. layoutIfNeeded()
  183. }
  184. func hide() {
  185. self.isHidden = true
  186. }
  187. private func prepareLensInfoViewsForData() {
  188. lensInfoLeadingFakePadding.isHidden = true
  189. lensInfoTrailingFakePadding.isHidden = true
  190. lensInfoStackViewLeadingConstraint.constant = 5
  191. lensInfoStackViewTrailingConstraint.constant = 5
  192. }
  193. // MARK: - Action
  194. @IBAction func touchLocation(_ sender: Any) {
  195. guard let latitude = exif?.latitude, let longitude = exif?.longitude else { return }
  196. let latitudeDeg: CLLocationDegrees = latitude
  197. let longitudeDeg: CLLocationDegrees = longitude
  198. let coordinates = CLLocationCoordinate2DMake(latitudeDeg, longitudeDeg)
  199. let placemark = MKPlacemark(coordinate: coordinates, addressDictionary: nil)
  200. let mapItem = MKMapItem(placemark: placemark)
  201. if let location = exif?.location {
  202. mapItem.name = location
  203. }
  204. mapItem.openInMaps()
  205. }
  206. @IBAction func touchDownload(_ sender: Any) {
  207. delegate?.downloadFullResolution()
  208. }
  209. }