PhotosRightsHelper.swift 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. // _____ _
  2. // |_ _| |_ _ _ ___ ___ _ __ __ _
  3. // | | | ' \| '_/ -_) -_) ' \/ _` |_
  4. // |_| |_||_|_| \___\___|_|_|_\__,_(_)
  5. //
  6. // Threema iOS Client
  7. // Copyright (c) 2020 Threema GmbH
  8. //
  9. // This program is free software: you can redistribute it and/or modify
  10. // it under the terms of the GNU Affero General Public License, version 3,
  11. // as published by the Free Software Foundation.
  12. //
  13. // This program is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. // GNU Affero General Public License for more details.
  17. //
  18. // You should have received a copy of the GNU Affero General Public License
  19. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. import Foundation
  21. #if compiler(>=5.3)
  22. import PhotosUI
  23. #endif
  24. enum PhotosRights {
  25. case full
  26. case write
  27. case potentialWrite
  28. case none
  29. }
  30. protocol PhotosRightsHelperProtocol {
  31. func accessLevelDetermined() -> Bool
  32. func requestWriteAccess() -> Bool
  33. func requestReadAccess() -> Bool
  34. func haveFullAccess() -> Bool
  35. func haveWriteAccess() -> Bool
  36. func checknewPhotosApi() -> Bool
  37. }
  38. @objc class PhotosRightsHelper : NSObject, PhotosRightsHelperProtocol {
  39. func haveFullAccess() -> Bool {
  40. #if compiler(>=5.3)
  41. if #available(iOS 14, *) {
  42. let authStatus = PHPhotoLibrary.authorizationStatus(for: .readWrite)
  43. return authStatus == .authorized
  44. }
  45. #endif
  46. // Fallback on earlier versions
  47. let authStatus = PHPhotoLibrary.authorizationStatus()
  48. return authStatus == .authorized
  49. }
  50. func haveLimitedAccess() -> Bool {
  51. #if compiler(>=5.3)
  52. if #available(iOS 14, *) {
  53. let authStatus = PHPhotoLibrary.authorizationStatus()
  54. return authStatus == .limited
  55. }
  56. #endif
  57. // Fallback on earlier versions
  58. return false
  59. }
  60. static func checkAccessAllowed(rightsHelper : PhotosRightsHelperProtocol) -> PhotosRights {
  61. var accessAllowed = PhotosRights.none
  62. if !rightsHelper.accessLevelDetermined() {
  63. if rightsHelper.checknewPhotosApi() {
  64. accessAllowed = rightsHelper.requestWriteAccess() ? .write : .none
  65. } else {
  66. accessAllowed = rightsHelper.requestReadAccess() ? .full : .potentialWrite
  67. }
  68. } else {
  69. if rightsHelper.haveFullAccess() {
  70. accessAllowed = .full
  71. } else {
  72. accessAllowed = rightsHelper.haveWriteAccess() ? .write : .potentialWrite
  73. }
  74. }
  75. return accessAllowed
  76. }
  77. func checknewPhotosApi() -> Bool {
  78. if #available(iOS 14, *) {
  79. return true
  80. }
  81. return false
  82. }
  83. /// Check whether we have write access to the photos
  84. /// There is no sepearte check for the write permission on iOS 13 and lower. True in that case.
  85. /// - Returns: True if we have access or on iOS 13 and lower. False if access was not granted or not yet granted
  86. func haveWriteAccess() -> Bool {
  87. #if compiler(>=5.3)
  88. if #available(iOS 14, *) {
  89. let authStatus = PHPhotoLibrary.authorizationStatus(for: .addOnly)
  90. return authStatus == .authorized
  91. }
  92. #endif
  93. // Fallback on earlier versions
  94. return true
  95. }
  96. func requestReadAccess() -> Bool {
  97. #if compiler(>=5.3)
  98. if #available(iOS 14, *) {
  99. return requestAccess(accessLevel: .readWrite)
  100. }
  101. #endif
  102. // Fallback on earlier versions
  103. return requestAccess()
  104. }
  105. func requestWriteAccess() -> Bool {
  106. #if compiler(>=5.3)
  107. if #available(iOS 14, *) {
  108. return requestAccess(accessLevel: .addOnly)
  109. }
  110. #endif
  111. // Fallback on earlier versions
  112. return requestAccess()
  113. }
  114. func accessLevelDetermined() -> Bool {
  115. #if compiler(>=5.3)
  116. if #available(iOS 14, *) {
  117. let ao = PHPhotoLibrary.authorizationStatus(for: .addOnly) != .notDetermined
  118. let rw = PHPhotoLibrary.authorizationStatus(for: .readWrite) != .notDetermined
  119. return ao && rw
  120. }
  121. #endif
  122. return PHPhotoLibrary.authorizationStatus() != .notDetermined
  123. }
  124. private func requestAccess() -> Bool {
  125. var status = PHAuthorizationStatus.notDetermined
  126. let sema = DispatchSemaphore.init(value: 0)
  127. DispatchQueue.global(qos: .userInitiated).sync {
  128. PHPhotoLibrary.requestAuthorization({ authStat in
  129. status = authStat
  130. sema.signal()
  131. })
  132. }
  133. sema.wait()
  134. return status == .authorized
  135. }
  136. #if compiler(>=5.3)
  137. @available(iOS 14, *)
  138. private func requestAccess(accessLevel : PHAccessLevel) -> Bool {
  139. var status = PHAuthorizationStatus.notDetermined
  140. let sema = DispatchSemaphore.init(value: 0)
  141. DispatchQueue.global(qos: .userInitiated).sync {
  142. PHPhotoLibrary.requestAuthorization(for: accessLevel, handler: { authStat in
  143. status = authStat
  144. sema.signal()
  145. })
  146. }
  147. sema.wait()
  148. return status == .authorized
  149. }
  150. #endif
  151. }