NCViewerMediaDetailView.swift 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  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. private var exif: ExifData?
  68. var isShown: Bool {
  69. return !self.isHidden
  70. }
  71. deinit {
  72. print("deinit NCViewerMediaDetailView")
  73. self.mapView?.removeFromSuperview()
  74. self.mapView = nil
  75. }
  76. func show(metadata: tableMetadata,
  77. image: UIImage?,
  78. textColor: UIColor?,
  79. exif: ExifData,
  80. ncplayer: NCPlayer?,
  81. delegate: NCViewerMediaDetailViewDelegate?) {
  82. self.metadata = metadata
  83. self.exif = exif
  84. self.ncplayer = ncplayer
  85. self.delegate = delegate
  86. outerMapContainer.isHidden = true
  87. downloadImageButtonContainer.isHidden = true
  88. if let latitude = exif.latitude, let longitude = exif.longitude, NCNetworking.shared.networkReachability != .notReachable {
  89. // We hide the map view on phones in landscape (aka compact height), since there is too little space to fit all of it.
  90. mapContainer.isHidden = traitCollection.verticalSizeClass == .compact
  91. outerMapContainer.isHidden = false
  92. let annotation = MKPointAnnotation()
  93. annotation.coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
  94. let region = MKCoordinateRegion(center: annotation.coordinate, latitudinalMeters: 500, longitudinalMeters: 500)
  95. if mapView == nil, mapView?.region.center.latitude != latitude, mapView?.region.center.longitude != longitude {
  96. let mapView = MKMapView()
  97. self.mapView = mapView
  98. mapContainer.subviews.forEach { $0.removeFromSuperview() }
  99. self.mapContainer.addSubview(mapView)
  100. mapView.translatesAutoresizingMaskIntoConstraints = false
  101. NSLayoutConstraint.activate([
  102. mapView.topAnchor.constraint(equalTo: self.mapContainer.topAnchor),
  103. mapView.bottomAnchor.constraint(equalTo: self.mapContainer.bottomAnchor),
  104. mapView.leadingAnchor.constraint(equalTo: self.mapContainer.leadingAnchor),
  105. mapView.trailingAnchor.constraint(equalTo: self.mapContainer.trailingAnchor)
  106. ])
  107. mapView.isZoomEnabled = true
  108. mapView.isScrollEnabled = false
  109. mapView.isUserInteractionEnabled = false
  110. mapView.addAnnotation(annotation)
  111. mapView.setRegion(region, animated: false)
  112. }
  113. }
  114. if let make = exif.make, let model = exif.model, let lensModel = exif.lensModel {
  115. modelLabel.text = "\(make) \(model)"
  116. lensLabel.text = lensModel
  117. .replacingOccurrences(of: make, with: "")
  118. .replacingOccurrences(of: model, with: "")
  119. .replacingOccurrences(of: "f/", with: "ƒ").trimmingCharacters(in: .whitespacesAndNewlines).firstUppercased
  120. } else {
  121. modelLabel.text = NSLocalizedString("_no_camera_information_", comment: "")
  122. lensLabel.text = NSLocalizedString("_no_lens_information_", comment: "")
  123. }
  124. nameLabel.text = (metadata.fileNameView as NSString).deletingPathExtension
  125. sizeLabel.text = CCUtility.transformedSize(metadata.size)
  126. if let shutterSpeedApex = exif.shutterSpeedApex {
  127. prepareLensInfoViewsForData()
  128. shutterSpeedLabel.text = "1/\(Int(pow(2, shutterSpeedApex))) s"
  129. }
  130. if let iso = exif.iso {
  131. prepareLensInfoViewsForData()
  132. isoLabel.text = "ISO \(iso)"
  133. }
  134. if let apertureValue = exif.apertureValue {
  135. apertureLabel.text = "ƒ\(apertureValue)"
  136. }
  137. if let exposureValue = exif.exposureValue {
  138. exposureValueLabel.text = "\(exposureValue) ev"
  139. }
  140. if let lensLength = exif.lensLength {
  141. lensSizeLabel.text = "\(lensLength) mm"
  142. }
  143. if let date = exif.date {
  144. dateContainer.isHidden = false
  145. noDateLabel.isHidden = true
  146. let formatter = DateFormatter()
  147. formatter.dateFormat = "EEEE"
  148. let dayString = formatter.string(from: date as Date)
  149. dayLabel.text = dayString
  150. formatter.dateFormat = "d MMM yyyy"
  151. let dateString = formatter.string(from: date as Date)
  152. dateLabel.text = dateString
  153. formatter.dateFormat = "HH:mm"
  154. let timeString = formatter.string(from: date as Date)
  155. timeLabel.text = timeString
  156. } else {
  157. noDateLabel.text = NSLocalizedString("_no_date_information_", comment: "")
  158. }
  159. if let height = exif.height, let width = exif.width {
  160. megaPixelLabel.isHidden = false
  161. megaPixelLabelDivider.isHidden = false
  162. resolutionLabel.isHidden = false
  163. resolutionLabelDivider.isHidden = false
  164. resolutionLabel.text = "\(width) x \(height)"
  165. let megaPixels: Double = Double(width * height) / 1000000
  166. megaPixelLabel.text = megaPixels < 1 ? String(format: "%.1f MP", megaPixels) : "\(Int(megaPixels)) MP"
  167. }
  168. extensionLabel.text = metadata.fileExtension.uppercased()
  169. if exif.location?.isEmpty == false {
  170. locationLabel.text = exif.location
  171. }
  172. if metadata.livePhoto {
  173. livePhotoImageView.isHidden = false
  174. }
  175. if metadata.isImage && !CCUtility.fileProviderStorageExists(metadata) && metadata.session.isEmpty {
  176. downloadImageButton.setTitle(NSLocalizedString("_try_download_full_resolution_", comment: ""), for: .normal)
  177. downloadImageLabel.text = NSLocalizedString("_full_resolution_image_info_", comment: "")
  178. downloadImageButtonContainer.isHidden = false
  179. }
  180. self.isHidden = false
  181. layoutIfNeeded()
  182. }
  183. func hide() {
  184. self.isHidden = true
  185. }
  186. private func prepareLensInfoViewsForData() {
  187. lensInfoLeadingFakePadding.isHidden = true
  188. lensInfoTrailingFakePadding.isHidden = true
  189. lensInfoStackViewLeadingConstraint.constant = 5
  190. lensInfoStackViewTrailingConstraint.constant = 5
  191. }
  192. // MARK: - Action
  193. @IBAction func touchLocation(_ sender: Any) {
  194. guard let latitude = exif?.latitude, let longitude = exif?.longitude else { return }
  195. let latitudeDeg: CLLocationDegrees = latitude
  196. let longitudeDeg: CLLocationDegrees = longitude
  197. let coordinates = CLLocationCoordinate2DMake(latitudeDeg, longitudeDeg)
  198. let placemark = MKPlacemark(coordinate: coordinates, addressDictionary: nil)
  199. let mapItem = MKMapItem(placemark: placemark)
  200. if let location = exif?.location {
  201. mapItem.name = location
  202. }
  203. mapItem.openInMaps()
  204. }
  205. @IBAction func touchDownload(_ sender: Any) {
  206. delegate?.downloadFullResolution()
  207. }
  208. }