@ -1,25 +1,12 @@
//
// C o p y r i g h t ( c ) 2 0 1 9 O p e n W h i s p e r S y s t e m s . A l l r i g h t s r e s e r v e d .
//
import UIKit
import PureLayout
import CoreServices
import PromiseKit
import SessionUIKit
import CoreServices
@objc
public class ShareViewController : UIViewController , ShareViewDelegate , SAEFailedViewDelegate , AppModeManagerDelegate {
// MARK: - D e p e n d e n c i e s
private var tsAccountManager : TSAccountManager {
return TSAccountManager . sharedInstance ( )
}
// MARK: -
final class ShareVC : UINavigationController , ShareViewDelegate , AppModeManagerDelegate {
private var areVersionMigrationsComplete = false
public static var attachmentPrepPromise : Promise < [ SignalAttachment ] >?
// MARK: E r r o r
enum ShareViewControllerError : Error {
case assertionError ( description : String )
case unsupportedMedia
@ -27,16 +14,8 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed
case obsoleteShare
}
private var hasInitialRootViewController = false
private var isReadyForAppExtensions = false
private var areVersionMigrationsComplete = false
private var progressPoller : ProgressPoller ?
var loadViewController : SAELoadViewController ?
private var shareViewNavigationController : OWSNavigationController ?
override open func loadView ( ) {
// MARK: L i f e c y c l e
override func loadView ( ) {
super . loadView ( )
// T h i s s h o u l d b e t h e f i r s t t h i n g w e d o .
@ -56,8 +35,6 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed
_ = AppVersion . sharedInstance ( )
startupLogging ( )
Cryptography . seedRandom ( )
// W e d o n ' t n e e d t o u s e D e v i c e S l e e p M a n a g e r i n t h e S A E .
@ -69,18 +46,6 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed
return
}
// I f w e h a v e n ' t m i g r a t e d t h e d a t a b a s e f i l e t o t h e s h a r e d d a t a
// d i r e c t o r y w e c a n ' t l o a d i t , a n d t h e r e f o r e c a n ' t i n i t T S S P r i m a r y S t o r a g e ,
// a n d t h e r e f o r e d o n ' t w a n t t o s e t u p m o s t o f o u r m a c h i n e r y ( E n v i r o n m e n t ,
// m o s t o f t h e s i n g l e t o n s , e t c . ) . W e j u s t w a n t t o s h o w a n e r r o r v i e w a n d
// a b o r t .
isReadyForAppExtensions = OWSPreferences . isReadyForAppExtensions ( )
guard isReadyForAppExtensions else {
showNotReadyView ( )
return
}
// W e s h o u l d n ' t s e t u p o u r e n v i r o n m e n t u n t i l a f t e r w e ' v e c o n s u l t e d i s R e a d y F o r A p p E x t e n s i o n s .
AppSetup . setupEnvironment ( appSpecificSingletonBlock : {
SSKEnvironment . shared . notificationsManager = NoopNotificationsManager ( )
} , migrationCompletion : { [ weak self ] in
@ -93,120 +58,16 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed
strongSelf . versionMigrationsDidComplete ( )
} )
let shareViewNavigationController = OWSNavigationController ( )
self . shareViewNavigationController = shareViewNavigationController
let loadViewController = SAELoadViewController ( delegate : self )
self . loadViewController = loadViewController
// D o n ' t d i s p l a y l o a d s c r e e n i m m e d i a t e l y , i n h o p e s t h a t w e c a n a v o i d i t a l t o g e t h e r .
after ( seconds : 0.5 ) . done { [ weak self ] in
AssertIsOnMainThread ( )
guard let strongSelf = self else { return }
guard strongSelf . presentedViewController = = nil else {
Logger . debug ( " setup completed quickly, no need to present load view controller. " )
return
}
Logger . debug ( " setup is slow - showing loading screen " )
strongSelf . showPrimaryViewController ( loadViewController )
} . retainUntilComplete ( )
// W e d o n ' t n e e d t o u s e " s c r e e n p r o t e c t i o n " i n t h e S A E .
NotificationCenter . default . addObserver ( self ,
selector : #selector ( storageIsReady ) ,
name : . StorageIsReady ,
object : nil )
NotificationCenter . default . addObserver ( self ,
selector : #selector ( registrationStateDidChange ) ,
name : . RegistrationStateDidChange ,
object : nil )
NotificationCenter . default . addObserver ( self ,
selector : #selector ( owsApplicationWillEnterForeground ) ,
name : . OWSApplicationWillEnterForeground ,
object : nil )
NotificationCenter . default . addObserver ( self ,
selector : #selector ( applicationDidEnterBackground ) ,
name : . OWSApplicationDidEnterBackground ,
object : nil )
Logger . info ( " completed. " )
}
deinit {
Logger . info ( " deinit " )
NotificationCenter . default . removeObserver ( self )
// S h a r e e x t e n s i o n s r e s i d e i n a p r o c e s s t h a t m a y b e r e u s e d b e t w e e n u s a g e s .
// T h a t i s n ' t s a f e ; t h e c o d e b a s e i s f u l l o f s t a t i c s ( e . g . s i n g l e t o n s ) w h i c h
// w e c a n ' t e a s i l y c l e a n u p .
ExitShareExtension ( )
}
@objc
public func applicationDidEnterBackground ( ) {
AssertIsOnMainThread ( )
Logger . info ( " " )
if OWSScreenLock . shared . isScreenLockEnabled ( ) {
Logger . info ( " dismissing. " )
self . dismiss ( animated : false ) { [ weak self ] in
AssertIsOnMainThread ( )
guard let strongSelf = self else { return }
strongSelf . extensionContext ! . completeRequest ( returningItems : [ ] , completionHandler : nil )
}
}
}
private func activate ( ) {
AssertIsOnMainThread ( )
Logger . debug ( " " )
// W e d o n ' t n e e d t o u s e " s c r e e n p r o t e c t i o n " i n t h e S A E .
ensureRootViewController ( )
// W e d o n ' t n e e d t o u s e R T C I n i t i a l i z e S S L ( ) i n t h e S A E .
if tsAccountManager . isRegistered ( ) {
// A t t h i s p o i n t , p o t e n t i a l l y l e n g t h y D B l o c k i n g m i g r a t i o n s c o u l d b e r u n n i n g .
// A v o i d b l o c k i n g a p p l a u n c h b y p u t t i n g a l l f u r t h e r p o s s i b l e D B a c c e s s i n a s y n c b l o c k
DispatchQueue . global ( ) . async { [ weak self ] in
guard let _ = self else { return }
Logger . info ( " running post launch block for registered user: \( TSAccountManager . localNumber ( ) ! ) " )
// W e d o n ' t n e e d t o u s e O W S D i s a p p e a r i n g M e s s a g e s J o b i n t h e S A E .
// W e d o n ' t n e e d t o u s e O W S F a i l e d M e s s a g e s J o b i n t h e S A E .
// W e d o n ' t n e e d t o u s e O W S F a i l e d A t t a c h m e n t D o w n l o a d s J o b i n t h e S A E .
}
} else {
Logger . info ( " running post launch block for unregistered user. " )
// W e d o n ' t n e e d t o u p d a t e t h e a p p i c o n b a d g e n u m b e r i n t h e S A E .
// W e d o n ' t n e e d t o p r o d t h e T S S o c k e t M a n a g e r i n t h e S A E .
}
if tsAccountManager . isRegistered ( ) {
DispatchQueue . main . async { [ weak self ] in
guard let _ = self else { return }
Logger . info ( " running post launch block for registered user: \( TSAccountManager . localNumber ( ) ! ) " )
// W e d o n ' t n e e d t o u s e t h e T S S o c k e t M a n a g e r i n t h e S A E .
// W e d o n ' t n e e d t o f e t c h m e s s a g e s i n t h e S A E .
// W e d o n ' t n e e d t o u s e O W S S y n c P u s h T o k e n s J o b i n t h e S A E .
}
}
}
@objc
@ -256,19 +117,15 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed
// i t w i l l a l s o r u n a l l d e f e r r e d b l o c k s .
AppReadiness . setAppIsReady ( )
if tsAccountManager . isRegistered ( ) {
Logger . info ( " localNumber: \( TSAccountManager . localNumber ( ) ! ) " )
// W e d o n ' t n e e d t o u s e m e s s a g e F e t c h e r J o b i n t h e S A E .
// W e d o n ' t n e e d t o u s e S y n c P u s h T o k e n s J o b i n t h e S A E .
}
// W e d o n ' t n e e d t o u s e D e v i c e S l e e p M a n a g e r i n t h e S A E .
AppVersion . sharedInstance ( ) . saeLaunchDidComplete ( )
ensureRootViewController ( )
showLockScreenOrMainContent ( )
// W e d o n ' t n e e d t o u s e O W S M e s s a g e R e c e i v e r i n t h e S A E .
// W e d o n ' t n e e d t o u s e O W S B a t c h M e s s a g e P r o c e s s o r i n t h e S A E .
@ -282,163 +139,33 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed
OWSReadReceiptManager . shared ( ) . prepareCachedValues ( )
}
@objc
func registrationStateDidChange ( ) {
AssertIsOnMainThread ( )
Logger . debug ( " " )
if tsAccountManager . isRegistered ( ) {
Logger . info ( " localNumber: \( TSAccountManager . localNumber ( ) ! ) " )
// W e d o n ' t n e e d t o u s e E x p e r i e n c e U p g r a d e F i n d e r i n t h e S A E .
// W e d o n ' t n e e d t o u s e O W S D i s a p p e a r i n g M e s s a g e s J o b i n t h e S A E .
OWSProfileManager . shared ( ) . ensureLocalProfileCached ( )
}
}
private func ensureRootViewController ( ) {
AssertIsOnMainThread ( )
Logger . debug ( " " )
guard AppReadiness . isAppReady ( ) else {
return
}
guard ! hasInitialRootViewController else {
return
}
hasInitialRootViewController = true
Logger . info ( " Presenting initial root view controller " )
if OWSScreenLock . shared . isScreenLockEnabled ( ) {
presentScreenLock ( )
} else {
presentContentView ( )
}
}
private func presentContentView ( ) {
override func viewDidLoad ( ) {
super . viewDidLoad ( )
AppReadiness . runNowOrWhenAppDidBecomeReady { [ weak self ] in
AssertIsOnMainThread ( )
Logger . debug ( " " )
Logger . info ( " Presenting content view " )
if ! tsAccountManager . isRegistered ( ) {
showNotRegisteredView ( )
} else if ! OWSProfileManager . shared ( ) . localProfileExists ( ) {
// T h i s i s a r a r e e d g e c a s e , b u t w e w a n t t o e n s u r e t h a t t h e u s e r
// i s h a s a l r e a d y s a v e d t h e i r l o c a l p r o f i l e k e y i n t h e m a i n a p p .
showNotReadyView ( )
} else {
DispatchQueue . main . async { [ weak self ] in
guard let strongSelf = self else { return }
strongSelf . buildAttachmentsAndPresentConversationPicker ( )
}
}
// W e d o n ' t u s e t h e A p p U p d a t e N a g i n t h e S A E .
}
func startupLogging ( ) {
Logger . info ( " iOS Version: \( UIDevice . current . systemVersion ) } " )
let locale = NSLocale . current as NSLocale
if let localeIdentifier = locale . object ( forKey : NSLocale . Key . identifier ) as ? String ,
localeIdentifier . count > 0 {
Logger . info ( " Locale Identifier: \( localeIdentifier ) " )
} else {
owsFailDebug ( " Locale Identifier: Unknown " )
}
if let countryCode = locale . object ( forKey : NSLocale . Key . countryCode ) as ? String ,
countryCode . count > 0 {
Logger . info ( " Country Code: \( countryCode ) " )
} else {
owsFailDebug ( " Country Code: Unknown " )
}
if let languageCode = locale . object ( forKey : NSLocale . Key . languageCode ) as ? String ,
languageCode . count > 0 {
Logger . info ( " Language Code: \( languageCode ) " )
} else {
owsFailDebug ( " Language Code: Unknown " )
}
}
// MARK: E r r o r V i e w s
private func showNotReadyView ( ) {
AssertIsOnMainThread ( )
let failureTitle = NSLocalizedString ( " SHARE_EXTENSION_NOT_YET_MIGRATED_TITLE " ,
comment : " Title indicating that the share extension cannot be used until the main app has been launched at least once. " )
let failureMessage = NSLocalizedString ( " SHARE_EXTENSION_NOT_YET_MIGRATED_MESSAGE " ,
comment : " Message indicating that the share extension cannot be used until the main app has been launched at least once. " )
showErrorView ( title : failureTitle , message : failureMessage )
strongSelf . showLockScreenOrMainContent ( )
}
private func showNotRegisteredView ( ) {
AssertIsOnMainThread ( )
let failureTitle = NSLocalizedString ( " SHARE_EXTENSION_NOT_REGISTERED_TITLE " ,
comment : " Title indicating that the share extension cannot be used until the user has registered in the main app. " )
let failureMessage = NSLocalizedString ( " SHARE_EXTENSION_NOT_REGISTERED_MESSAGE " ,
comment : " Message indicating that the share extension cannot be used until the user has registered in the main app. " )
showErrorView ( title : failureTitle , message : failureMessage )
}
private func showErrorView ( title : String , message : String ) {
@objc
public func applicationDidEnterBackground ( ) {
AssertIsOnMainThread ( )
let viewController = SAEFailedViewController ( delegate : self , title : title , message : message )
self . showPrimaryViewController ( viewController )
}
// MARK: V i e w L i f e c y c l e
override open func viewDidLoad ( ) {
super . viewDidLoad ( )
Logger . info ( " " )
Logger . debug ( " " )
if OWSScreenLock . shared . isScreenLockEnabled ( ) {
if isReadyForAppExtensions {
AppReadiness . runNowOrWhenAppDidBecomeReady { [ weak self ] in
self . dismiss ( animated : false ) { [ weak self ] in
AssertIsOnMainThread ( )
guard let strongSelf = self else { return }
strongSelf . activate ( )
}
}
}
override open func viewWillAppear ( _ animated : Bool ) {
Logger . debug ( " " )
super . viewWillAppear ( animated )
strongSelf . extensionContext ! . completeRequest ( returningItems : [ ] , completionHandler : nil )
}
override open func viewDidAppear ( _ animated : Bool ) {
Logger . debug ( " " )
super . viewDidAppear ( animated )
}
override open func viewWillDisappear ( _ animated : Bool ) {
Logger . debug ( " " )
super . viewWillDisappear ( animated )
Logger . flush ( )
}
override open func viewDidDisappear ( _ animated : Bool ) {
Logger . debug ( " " )
super . viewDidDisappear ( animated )
Logger . flush ( )
deinit {
NotificationCenter . default . removeObserver ( self )
// S h a r e e x t e n s i o n s r e s i d e i n a p r o c e s s t h a t m a y b e r e u s e d b e t w e e n u s a g e s .
// T h a t i s n ' t s a f e ; t h e c o d e b a s e i s f u l l o f s t a t i c s ( e . g . s i n g l e t o n s ) w h i c h
@ -446,136 +173,68 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed
ExitShareExtension ( )
}
@objc
func owsApplicationWillEnterForeground ( ) throws {
AssertIsOnMainThread ( )
Logger . debug ( " " )
// I f a u s e r u n r e g i s t e r s i n t h e m a i n a p p , t h e S A E s h o u l d s h u t d o w n
// i m m e d i a t e l y .
guard ! tsAccountManager . isRegistered ( ) else {
// I f u s e r i s r e g i s t e r e d , d o n o t h i n g .
return
}
guard let shareViewNavigationController = shareViewNavigationController else {
owsFailDebug ( " Missing shareViewNavigationController " )
return
}
guard let firstViewController = shareViewNavigationController . viewControllers . first else {
// I f n o v i e w h a s b e e n p r e s e n t e d y e t , d o n o t h i n g .
return
}
if let _ = firstViewController as ? SAEFailedViewController {
// I f r o o t v i e w i s a n e r r o r v i e w , d o n o t h i n g .
return
}
throw ShareViewControllerError . notRegistered
}
// MARK: S h a r e V i e w D e l e g a t e , S A E F a i l e d V i e w D e l e g a t e
public func shareViewWasUnlocked ( ) {
Logger . info ( " " )
presentContentView ( )
// MARK: A p p M o d e
public func getCurrentAppMode ( ) -> AppMode {
guard let window = self . view . window else { return . light }
let userInterfaceStyle = window . traitCollection . userInterfaceStyle
let isLightMode = ( userInterfaceStyle = = . light || userInterfaceStyle = = . unspecified )
return isLightMode ? . light : . dark
}
public func shareViewWasCompleted ( ) {
Logger . info ( " " )
self . dismiss ( animated : true ) { [ weak self ] in
AssertIsOnMainThread ( )
guard let strongSelf = self else { return }
strongSelf . extensionContext ! . completeRequest ( returningItems : [ ] , completionHandler : nil )
}
public func setCurrentAppMode ( to appMode : AppMode ) {
return // N o t a p p l i c a b l e t o s h a r e e x t e n s i o n s
}
public func shareViewWasCancelled ( ) {
Logger . info ( " " )
self . dismiss ( animated : true ) { [ weak self ] in
AssertIsOnMainThread ( )
guard let strongSelf = self else { return }
strongSelf . extensionContext ! . completeRequest ( returningItems : [ ] , completionHandler : nil )
// MARK: U p d a t i n g
private func showLockScreenOrMainContent ( ) {
if OWSScreenLock . shared . isScreenLockEnabled ( ) {
showLockScreen ( )
} else {
showMainContent ( )
}
}
public func shareViewFailed ( error : Error ) {
Logger . info ( " " )
self . dismiss ( animated : true ) { [ weak self ] in
AssertIsOnMainThread ( )
guard let strongSelf = self else { return }
strongSelf . extensionContext ! . cancelRequest ( withError : error )
}
private func showLockScreen ( ) {
let screenLockVC = SAEScreenLockViewController ( shareViewDelegate : self )
setViewControllers ( [ screenLockVC ] , animated : false )
}
// MARK: H e l p e r s
// T h i s v i e w c o n t r o l l e r i s n o t v i s i b l e t o t h e u s e r . I t e x i s t s t o i n t e r c e p t t o u c h e s , s e t u p t h e
// e x t e n s i o n s d e p e n d e n c i e s , a n d e v e n t u a l l y p r e s e n t a v i s i b l e v i e w t o t h e u s e r .
// F o r s p e e d o f p r e s e n t a t i o n , w e o n l y p r e s e n t a s i n g l e m o d a l , a n d i f i t ' s a l r e a d y b e e n p r e s e n t e d
// w e s w a p o u t t h e c o n t e n t s .
// e . g . i f l o a d i n g i s t a k i n g a w h i l e , t h e u s e r w i l l s e e t h e l o a d s c r e e n p r e s e n t e d w i t h a m o d a l
// a n i m a t i o n . N e x t , w h e n l o a d i n g c o m p l e t e s , t h e l o a d v i e w w i l l b e s w i t c h e d o u t f o r t h e c o n t a c t
// p i c k e r v i e w .
private func showPrimaryViewController ( _ viewController : UIViewController ) {
AssertIsOnMainThread ( )
guard let shareViewNavigationController = shareViewNavigationController else {
owsFailDebug ( " Missing shareViewNavigationController " )
return
private func showMainContent ( ) {
let threadPickerVC = ThreadPickerVC ( )
threadPickerVC . shareVC = self
setViewControllers ( [ threadPickerVC ] , animated : false )
let promise = buildAttachments ( )
ModalActivityIndicatorViewController . present ( fromViewController : self , canCancel : false , message : NSLocalizedString ( " vc_share_loading_message " , comment : " " ) ) { activityIndicator in
promise . done { _ in
activityIndicator . dismiss { }
} . catch { _ in
activityIndicator . dismiss { }
}
shareViewNavigationController . setViewControllers ( [ viewController ] , animated : false )
if self . presentedViewController = = nil {
Logger . debug ( " presenting modally: \( viewController ) " )
self . present ( shareViewNavigationController , animated : true )
} else {
Logger . debug ( " modal already presented. swapping modal content for: \( viewController ) " )
assert ( self . presentedViewController = = shareViewNavigationController )
}
ShareVC . attachmentPrepPromise = promise
}
private func buildAttachmentsAndPresentConversationPicker ( ) {
AssertIsOnMainThread ( )
self . buildAttachments ( ) . map { [ weak self ] attachments in
AssertIsOnMainThread ( )
guard let strongSelf = self else { return }
strongSelf . progressPoller = nil
strongSelf . loadViewController = nil
let conversationPicker = SharingThreadPickerViewController ( shareViewDelegate : strongSelf )
Logger . debug ( " presentConversationPicker: \( conversationPicker ) " )
conversationPicker . attachments = attachments
strongSelf . showPrimaryViewController ( conversationPicker )
Logger . info ( " showing picker with attachments: \( attachments ) " )
} . catch { [ weak self ] error in
AssertIsOnMainThread ( )
guard let strongSelf = self else { return }
let alertTitle = NSLocalizedString ( " SHARE_EXTENSION_UNABLE_TO_BUILD_ATTACHMENT_ALERT_TITLE " ,
comment : " Shown when trying to share content to a Signal user for the share extension. Followed by failure details. " )
OWSAlerts . showAlert ( title : alertTitle ,
message : error . localizedDescription ,
buttonTitle : CommonStrings . cancelButton ) { _ in
strongSelf . shareViewWasCancelled ( )
func shareViewWasUnlocked ( ) {
showMainContent ( )
}
owsFailDebug ( " building attachment failed with error: \( error ) " )
} . retainUntilComplete ( )
func shareViewWasCompleted ( ) {
extensionContext ! . completeRequest ( returningItems : [ ] , completionHandler : nil )
}
private func presentScreenLock ( ) {
AssertIsOnMainThread ( )
func shareViewWasCancelled ( ) {
extensionContext ! . completeRequest ( returningItems : [ ] , completionHandler : nil )
}
let screenLockUI = SAEScreenLockViewController ( shareViewDelegate : self )
Logger . debug ( " presentScreenLock: \( screenLockUI ) " )
showPrimaryViewController ( screenLockUI )
Logger . info ( " showing screen lock " )
func shareViewFailed ( error : Error ) {
let alert = UIAlertController ( title : " Session " , message : error . localizedDescription , preferredStyle : . alert )
alert . addAction ( UIAlertAction ( title : NSLocalizedString ( " OK " , comment : " " ) , style : . default , handler : { _ in
self . extensionContext ! . cancelRequest ( withError : error )
} ) )
present ( alert , animated : true , completion : nil )
}
// MARK: A t t a c h m e n t P r e p
private class func itemMatchesSpecificUtiType ( itemProvider : NSItemProvider , utiType : String ) -> Bool {
// U R L s , c o n t a c t s a n d o t h e r s p e c i a l i t e m s h a v e t o b e d e t e c t e d s e p a r a t e l y .
// M a n y s h a r e s ( e . g . p d f s ) w i l l r e g i s t e r m a n y U T I t y p e s a n d / o r c o n f o r m t o k U T T y p e D a t a .
@ -708,7 +367,7 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed
Logger . error ( " invalid inputItem \( inputItemRaw ) " )
continue
}
if let itemProviders = ShareV iew Controller . preferredItemProviders ( inputItem : inputItem ) {
if let itemProviders = ShareV C. preferredItemProviders ( inputItem : inputItem ) {
return Promise . value ( itemProviders )
}
}
@ -753,7 +412,7 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed
// * U T I s a r e n ' t v e r y d e s c r i p t i v e ( t h e r e a r e f a r m o r e M I M E t y p e s t h a n U T I t y p e s )
// s o i n t h e c a s e o f f i l e a t t a c h m e n t s w e t r y t o r e f i n e t h e a t t a c h m e n t t y p e
// u s i n g t h e f i l e e x t e n s i o n .
guard let srcUtiType = ShareV iew Controller . utiType ( itemProvider : itemProvider ) else {
guard let srcUtiType = ShareV C. utiType ( itemProvider : itemProvider ) else {
let error = ShareViewControllerError . unsupportedMedia
return Promise ( error : error )
}
@ -880,7 +539,7 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed
Logger . debug ( " building DataSource with url: \( url ) , utiType: \( utiType ) " )
guard let dataSource = ShareV iew Controller . createDataSource ( utiType : utiType , url : url , customFileName : loadedItem . customFileName ) else {
guard let dataSource = ShareV C. createDataSource ( utiType : utiType , url : url , customFileName : loadedItem . customFileName ) else {
let error = ShareViewControllerError . assertionError ( description : " Unable to read attachment data " )
return Promise ( error : error )
}
@ -901,27 +560,7 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed
guard ! SignalAttachment . isInvalidVideo ( dataSource : dataSource , dataUTI : specificUTIType ) else {
// T h i s c a n h a p p e n , e . g . w h e n s h a r i n g a q u i c k t i m e - v i d e o f r o m i C l o u d d r i v e .
let ( promise , exportSession ) = SignalAttachment . compressVideoAsMp4 ( dataSource : dataSource , dataUTI : specificUTIType )
// TODO: H o w c a n w e m o v e w a i t i n g f o r t h i s e x p o r t t o t h e e n d o f t h e s h a r e f l o w r a t h e r t h a n h a v i n g t o d o i t u p f r o n t ?
// I d e a l l y w e ' d b e a b l e t o s t a r t i t h e r e , a n d n o t b l o c k t h e U I o n c o n v e r s i o n u n l e s s t h e r e ' s s t i l l w o r k t o b e d o n e
// w h e n t h e u s e r h i t s " s e n d " .
if let exportSession = exportSession {
let progressPoller = ProgressPoller ( timeInterval : 0.1 , ratioCompleteBlock : { return exportSession . progress } )
self . progressPoller = progressPoller
progressPoller . startPolling ( )
guard let loadViewController = self . loadViewController else {
owsFailDebug ( " load view controller was unexpectedly nil " )
return promise
}
DispatchQueue . main . async {
loadViewController . progress = progressPoller . progress
}
}
let ( promise , _ ) = SignalAttachment . compressVideoAsMp4 ( dataSource : dataSource , dataUTI : specificUTIType )
return promise
}
@ -1004,60 +643,4 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed
// a p p l y a n y c o n v e r s i o n , s o n o n e e d t o r e l o c a t e t h e a p p .
return ! itemProvider . registeredTypeIdentifiers . contains ( kUTTypeMPEG4 as String )
}
// MARK: A p p M o d e
public func getCurrentAppMode ( ) -> AppMode {
guard let window = self . view . window else { return . light }
let userInterfaceStyle = window . traitCollection . userInterfaceStyle
let isLightMode = ( userInterfaceStyle = = . light || userInterfaceStyle = = . unspecified )
return isLightMode ? . light : . dark
}
public func setCurrentAppMode ( to appMode : AppMode ) {
return // N o t a p p l i c a b l e t o s h a r e e x t e n s i o n s
}
}
// E x p o s e s a P r o g r e s s o b j e c t , w h o s e p r o g r e s s i s u p d a t e d b y p o l l i n g t h e r e t u r n o f a g i v e n b l o c k
private class ProgressPoller : NSObject {
let progress : Progress
private ( set ) var timer : Timer ?
// H i g h e r n u m b e r o f f e r s h i g h e r g a n u l a r i t y
let progressTotalUnitCount : Int64 = 10000
private let timeInterval : Double
private let ratioCompleteBlock : ( ) -> Float
init ( timeInterval : TimeInterval , ratioCompleteBlock : @ escaping ( ) -> Float ) {
self . timeInterval = timeInterval
self . ratioCompleteBlock = ratioCompleteBlock
self . progress = Progress ( )
progress . totalUnitCount = progressTotalUnitCount
progress . completedUnitCount = Int64 ( ratioCompleteBlock ( ) * Float ( progressTotalUnitCount ) )
}
func startPolling ( ) {
guard self . timer = = nil else {
owsFailDebug ( " already started timer " )
return
}
self . timer = WeakTimer . scheduledTimer ( timeInterval : timeInterval , target : self , userInfo : nil , repeats : true ) { [ weak self ] ( timer ) in
guard let strongSelf = self else {
return
}
let completedUnitCount = Int64 ( strongSelf . ratioCompleteBlock ( ) * Float ( strongSelf . progressTotalUnitCount ) )
strongSelf . progress . completedUnitCount = completedUnitCount
if completedUnitCount = = strongSelf . progressTotalUnitCount {
Logger . debug ( " progress complete " )
timer . invalidate ( )
}
}
}
}