Merge pull request #404 from oxen-io/sharing-2

Fix Share Extension
pull/406/head 1.10.2
Niels Andriesse 4 years ago committed by GitHub
commit c46099735d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -26,7 +26,6 @@
3441FD9F21A3604F00BB9542 /* BackupRestoreViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3441FD9E21A3604F00BB9542 /* BackupRestoreViewController.swift */; };
34480B361FD0929200BC14EF /* ShareAppExtensionContext.m in Sources */ = {isa = PBXBuildFile; fileRef = 34480B351FD0929200BC14EF /* ShareAppExtensionContext.m */; };
344825C6211390C800DB4BD8 /* OWSOrphanDataCleaner.m in Sources */ = {isa = PBXBuildFile; fileRef = 344825C5211390C800DB4BD8 /* OWSOrphanDataCleaner.m */; };
3461284B1FD0B94000532771 /* SAELoadViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3461284A1FD0B93F00532771 /* SAELoadViewController.swift */; };
346129991FD1E4DA00532771 /* SignalApp.m in Sources */ = {isa = PBXBuildFile; fileRef = 346129971FD1E4D900532771 /* SignalApp.m */; };
34641E1F2088DA6D00E2EDE5 /* SAEScreenLockViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34641E1E2088DA6D00E2EDE5 /* SAEScreenLockViewController.m */; };
34661FB820C1C0D60056EDD6 /* message_sent.aiff in Resources */ = {isa = PBXBuildFile; fileRef = 34661FB720C1C0D60056EDD6 /* message_sent.aiff */; };
@ -36,7 +35,6 @@
347850331FD7494A007B8332 /* fontawesome-webfont.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 34330A591E7875FB00DF2FB9 /* fontawesome-webfont.ttf */; };
3478504C1FD7496D007B8332 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B66DBF4919D5BBC8006EA940 /* Images.xcassets */; };
347850551FD749C0007B8332 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = B6F509951AA53F760068F56A /* Localizable.strings */; };
347850571FD86544007B8332 /* SAEFailedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 347850561FD86544007B8332 /* SAEFailedViewController.swift */; };
3488F9362191CC4000E524CC /* MediaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3488F9352191CC4000E524CC /* MediaView.swift */; };
3496744F2076ACD000080B5F /* LongTextViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3496744E2076ACCE00080B5F /* LongTextViewController.swift */; };
3496955C219B605E00DCFE74 /* ImagePickerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34969559219B605E00DCFE74 /* ImagePickerController.swift */; };
@ -81,7 +79,6 @@
4520D8D51D417D8E00123472 /* Photos.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4520D8D41D417D8E00123472 /* Photos.framework */; };
4521C3C01F59F3BA00B4C582 /* TextFieldHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4521C3BF1F59F3BA00B4C582 /* TextFieldHelper.swift */; };
452EC6DF205E9E30000E787C /* MediaGalleryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452EC6DE205E9E30000E787C /* MediaGalleryViewController.swift */; };
4535186B1FC635DD00210559 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4535186A1FC635DD00210559 /* ShareViewController.swift */; };
4535186E1FC635DD00210559 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4535186C1FC635DD00210559 /* MainInterface.storyboard */; };
453518721FC635DD00210559 /* SessionShareExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 453518681FC635DD00210559 /* SessionShareExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
4539B5861F79348F007141FF /* PushRegistrationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4539B5851F79348F007141FF /* PushRegistrationManager.swift */; };
@ -165,6 +162,8 @@
B8041A9525C8FA1D003C2166 /* MediaLoaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8041A9425C8FA1D003C2166 /* MediaLoaderView.swift */; };
B8041AA725C90927003C2166 /* TypingIndicatorCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8041AA625C90927003C2166 /* TypingIndicatorCell.swift */; };
B80A579F23DFF1F300876683 /* NewClosedGroupVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B80A579E23DFF1F300876683 /* NewClosedGroupVC.swift */; };
B817AD9A26436593009DF825 /* SimplifiedConversationCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B817AD9926436593009DF825 /* SimplifiedConversationCell.swift */; };
B817AD9C26436F73009DF825 /* ThreadPickerVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B817AD9B26436F73009DF825 /* ThreadPickerVC.swift */; };
B81D25C426157F40004D1FE1 /* storage-seed-3.crt in Resources */ = {isa = PBXBuildFile; fileRef = B81D25B926157F20004D1FE1 /* storage-seed-3.crt */; };
B81D25C526157F40004D1FE1 /* storage-seed-1.crt in Resources */ = {isa = PBXBuildFile; fileRef = B81D25B726157F20004D1FE1 /* storage-seed-1.crt */; };
B81D25C626157F40004D1FE1 /* public-loki-foundation.crt in Resources */ = {isa = PBXBuildFile; fileRef = B81D25B826157F20004D1FE1 /* public-loki-foundation.crt */; };
@ -689,6 +688,7 @@
C3AABDDF2553ECF00042FF4C /* Array+Description.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3C2A5D12553860800C340D1 /* Array+Description.swift */; };
C3AAFFE825AE975D0089E6DD /* ConfigurationMessage+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3AAFFDE25AE96FF0089E6DD /* ConfigurationMessage+Convenience.swift */; };
C3AAFFF225AE99710089E6DD /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3AAFFF125AE99710089E6DD /* AppDelegate.swift */; };
C3ADC66126426688005F1414 /* ShareVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3ADC66026426688005F1414 /* ShareVC.swift */; };
C3BBE0762554CDA60050F1E3 /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3BBE0752554CDA60050F1E3 /* Configuration.swift */; };
C3BBE0802554CDD70050F1E3 /* Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3BBE07F2554CDD70050F1E3 /* Storage.swift */; };
C3BBE0A72554D4DE0050F1E3 /* Promise+Retrying.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3C2A5D62553860B00C340D1 /* Promise+Retrying.swift */; };
@ -974,14 +974,12 @@
34480B381FD092E300BC14EF /* SessionShareExtension-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "SessionShareExtension-Prefix.pch"; sourceTree = "<group>"; };
344825C4211390C700DB4BD8 /* OWSOrphanDataCleaner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSOrphanDataCleaner.h; sourceTree = "<group>"; };
344825C5211390C800DB4BD8 /* OWSOrphanDataCleaner.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSOrphanDataCleaner.m; sourceTree = "<group>"; };
3461284A1FD0B93F00532771 /* SAELoadViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SAELoadViewController.swift; sourceTree = "<group>"; };
346129971FD1E4D900532771 /* SignalApp.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SignalApp.m; sourceTree = "<group>"; };
346129981FD1E4DA00532771 /* SignalApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SignalApp.h; sourceTree = "<group>"; };
34641E1D2088DA6C00E2EDE5 /* SAEScreenLockViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SAEScreenLockViewController.h; sourceTree = "<group>"; };
34641E1E2088DA6D00E2EDE5 /* SAEScreenLockViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SAEScreenLockViewController.m; sourceTree = "<group>"; };
34661FB720C1C0D60056EDD6 /* message_sent.aiff */ = {isa = PBXFileReference; lastKnownFileType = audio.aiff; name = message_sent.aiff; path = Session/Meta/AudioFiles/message_sent.aiff; sourceTree = SOURCE_ROOT; };
346B66301F4E29B200E5122F /* CropScaleImageViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CropScaleImageViewController.swift; sourceTree = "<group>"; };
347850561FD86544007B8332 /* SAEFailedViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SAEFailedViewController.swift; sourceTree = "<group>"; };
3488F9352191CC4000E524CC /* MediaView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaView.swift; sourceTree = "<group>"; };
3496744E2076ACCE00080B5F /* LongTextViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LongTextViewController.swift; sourceTree = "<group>"; };
34969559219B605E00DCFE74 /* ImagePickerController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImagePickerController.swift; sourceTree = "<group>"; };
@ -1042,7 +1040,6 @@
4521C3BF1F59F3BA00B4C582 /* TextFieldHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextFieldHelper.swift; sourceTree = "<group>"; };
452EC6DE205E9E30000E787C /* MediaGalleryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaGalleryViewController.swift; sourceTree = "<group>"; };
453518681FC635DD00210559 /* SessionShareExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = SessionShareExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
4535186A1FC635DD00210559 /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = "<group>"; };
4535186D1FC635DD00210559 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = "<group>"; };
4535186F1FC635DD00210559 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
4539B5851F79348F007141FF /* PushRegistrationManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PushRegistrationManager.swift; sourceTree = "<group>"; };
@ -1156,6 +1153,8 @@
B8041A9425C8FA1D003C2166 /* MediaLoaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaLoaderView.swift; sourceTree = "<group>"; };
B8041AA625C90927003C2166 /* TypingIndicatorCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypingIndicatorCell.swift; sourceTree = "<group>"; };
B80A579E23DFF1F300876683 /* NewClosedGroupVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewClosedGroupVC.swift; sourceTree = "<group>"; };
B817AD9926436593009DF825 /* SimplifiedConversationCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimplifiedConversationCell.swift; sourceTree = "<group>"; };
B817AD9B26436F73009DF825 /* ThreadPickerVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadPickerVC.swift; sourceTree = "<group>"; };
B81D25B726157F20004D1FE1 /* storage-seed-1.crt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "storage-seed-1.crt"; sourceTree = "<group>"; };
B81D25B826157F20004D1FE1 /* public-loki-foundation.crt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "public-loki-foundation.crt"; sourceTree = "<group>"; };
B81D25B926157F20004D1FE1 /* storage-seed-3.crt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "storage-seed-3.crt"; sourceTree = "<group>"; };
@ -1690,6 +1689,7 @@
C3AAFFCB25AE92150089E6DD /* OpenGroupManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenGroupManager.swift; sourceTree = "<group>"; };
C3AAFFDE25AE96FF0089E6DD /* ConfigurationMessage+Convenience.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ConfigurationMessage+Convenience.swift"; sourceTree = "<group>"; };
C3AAFFF125AE99710089E6DD /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
C3ADC66026426688005F1414 /* ShareVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareVC.swift; sourceTree = "<group>"; };
C3AECBEA24EF5244005743DE /* fa */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fa; path = fa.lproj/Localizable.strings; sourceTree = "<group>"; };
C3B7845C25649DA600ADB2E7 /* TSIncomingMessage+Conversion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TSIncomingMessage+Conversion.swift"; sourceTree = "<group>"; };
C3BBE0752554CDA60050F1E3 /* Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Configuration.swift; sourceTree = "<group>"; };
@ -1982,13 +1982,13 @@
children = (
C31C21A4255BCA4800EC2D66 /* Meta */,
4535186C1FC635DD00210559 /* MainInterface.storyboard */,
347850561FD86544007B8332 /* SAEFailedViewController.swift */,
3461284A1FD0B93F00532771 /* SAELoadViewController.swift */,
34641E1D2088DA6C00E2EDE5 /* SAEScreenLockViewController.h */,
34641E1E2088DA6D00E2EDE5 /* SAEScreenLockViewController.m */,
4535186A1FC635DD00210559 /* ShareViewController.swift */,
34480B341FD0929200BC14EF /* ShareAppExtensionContext.h */,
34480B351FD0929200BC14EF /* ShareAppExtensionContext.m */,
C3ADC66026426688005F1414 /* ShareVC.swift */,
B817AD9B26436F73009DF825 /* ThreadPickerVC.swift */,
B817AD9926436593009DF825 /* SimplifiedConversationCell.swift */,
);
path = SessionShareExtension;
sourceTree = "<group>";
@ -4405,11 +4405,11 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
4535186B1FC635DD00210559 /* ShareViewController.swift in Sources */,
B817AD9A26436593009DF825 /* SimplifiedConversationCell.swift in Sources */,
34480B361FD0929200BC14EF /* ShareAppExtensionContext.m in Sources */,
C3ADC66126426688005F1414 /* ShareVC.swift in Sources */,
34641E1F2088DA6D00E2EDE5 /* SAEScreenLockViewController.m in Sources */,
3461284B1FD0B94000532771 /* SAELoadViewController.swift in Sources */,
347850571FD86544007B8332 /* SAEFailedViewController.swift in Sources */,
B817AD9C26436F73009DF825 /* ThreadPickerVC.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

@ -489,3 +489,6 @@
"vc_qr_code_view_scan_qr_code_tab_title" = "QR-Code scannen";
"vc_qr_code_view_scan_qr_code_explanation" = "Scannen Sie den QR-Code einer Person, um ein Gespräch mit ihr zu beginnen.";
"vc_view_my_qr_code_explanation" = "Das ist Ihr QR-Code. Andere Benutzer können ihn scannen, um eine Session mit Ihnen zu starten.";
"vc_share_title" = "Share to Session";
"vc_share_loading_message" = "Preparing attachments...";
"vc_share_sending_message" = "Sending...";

@ -520,3 +520,6 @@
"modal_link_previews_title" = "Enable Link Previews?";
"modal_link_previews_explanation" = "Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session's settings.";
"modal_link_previews_button_title" = "Enable";
"vc_share_title" = "Share to Session";
"vc_share_loading_message" = "Preparing attachments...";
"vc_share_sending_message" = "Sending...";

@ -489,3 +489,6 @@
"vc_qr_code_view_scan_qr_code_tab_title" = "Escanear código QR";
"vc_qr_code_view_scan_qr_code_explanation" = "Escanea el código QR de una persona para comenzar una conversación con ella";
"vc_view_my_qr_code_explanation" = "Este es tu código QR. Otros usuarios pueden escanearlo para empezar una Session contigo.";
"vc_share_title" = "Share to Session";
"vc_share_loading_message" = "Preparing attachments...";
"vc_share_sending_message" = "Sending...";

@ -489,3 +489,6 @@
"vc_qr_code_view_scan_qr_code_tab_title" = "اسکن کد QR";
"vc_qr_code_view_scan_qr_code_explanation" = "برای شروع مکالمه با دیگران، کد QR شخصی را اسکن کنید";
"vc_view_my_qr_code_explanation" = "این کد QR شماست. سایر کاربران می‌توانند برای شروع Session با شما آن را اسکن کنند.";
"vc_share_title" = "Share to Session";
"vc_share_loading_message" = "Preparing attachments...";
"vc_share_sending_message" = "Sending...";

@ -489,3 +489,6 @@
"vc_qr_code_view_scan_qr_code_tab_title" = "Scanner le code QR";
"vc_qr_code_view_scan_qr_code_explanation" = "Scannez le code QR d'un autre utilisateur pour démarrer une session";
"vc_view_my_qr_code_explanation" = "Ceci est votre code QR. Les autres utilisateurs peuvent le scanner pour démarrer une session avec vous.";
"vc_share_title" = "Share to Session";
"vc_share_loading_message" = "Preparing attachments...";
"vc_share_sending_message" = "Sending...";

@ -490,3 +490,6 @@
"vc_qr_code_view_scan_qr_code_tab_title" = "Pindai kode QR";
"vc_qr_code_view_scan_qr_code_explanation" = "Pindai kode QR pengguna lain untuk memulai percakapan";
"vc_view_my_qr_code_explanation" = "Ini adalah kode QR anda. Pengguna lain bisa memindainya untuk memulai percakapan dengan anda";
"vc_share_title" = "Share to Session";
"vc_share_loading_message" = "Preparing attachments...";
"vc_share_sending_message" = "Sending...";

@ -489,3 +489,6 @@
"vc_qr_code_view_scan_qr_code_tab_title" = "Scansiona il codice QR";
"vc_qr_code_view_scan_qr_code_explanation" = "Scansiona il codice QR di un utente per iniziare una conversazione con questa persona";
"vc_view_my_qr_code_explanation" = "Questo è il tuo codice QR. Altri utenti possono scansionarlo per iniziare una sessione con te.";
"vc_share_title" = "Share to Session";
"vc_share_loading_message" = "Preparing attachments...";
"vc_share_sending_message" = "Sending...";

@ -490,3 +490,6 @@
"vc_qr_code_view_scan_qr_code_tab_title" = "QR コードをスキャンする";
"vc_qr_code_view_scan_qr_code_explanation" = "誰かの QR コードをスキャンして、会話を始めましょう";
"vc_view_my_qr_code_explanation" = "これはあなたの QR コードです。他のユーザーはそれをスキャンして、あなたとの Session を開始できます。";
"vc_share_title" = "Share to Session";
"vc_share_loading_message" = "Preparing attachments...";
"vc_share_sending_message" = "Sending...";

@ -489,3 +489,6 @@
"vc_qr_code_view_scan_qr_code_tab_title" = "Skanowania QR code";
"vc_qr_code_view_scan_qr_code_explanation" = "Zeskanuj czyjś kod QR, aby rozpocząć z nim rozmowę";
"vc_view_my_qr_code_explanation" = "To jest twój kod QR. Inni użytkownicy mogą go zeskanować, aby rozpocząć z tobą sesję.";
"vc_share_title" = "Share to Session";
"vc_share_loading_message" = "Preparing attachments...";
"vc_share_sending_message" = "Sending...";

@ -489,3 +489,6 @@
"vc_qr_code_view_scan_qr_code_tab_title" = "Escanear código QR";
"vc_qr_code_view_scan_qr_code_explanation" = "Escaneie o código QR de alguém para iniciar uma conversa com essa pessoa";
"vc_view_my_qr_code_explanation" = "Este é o seu código QR. Outros usuários podem escaneá-lo para iniciar uma sessão com você.";
"vc_share_title" = "Share to Session";
"vc_share_loading_message" = "Preparing attachments...";
"vc_share_sending_message" = "Sending...";

@ -520,3 +520,6 @@
"modal_link_previews_title" = "Включить предварительный просмотр ссылок?";
"modal_link_previews_explanation" = "Включение предпросмотра ссылок покажет превью для отправляемых и получаемых ссылок. Это может быть полезно, но Session нужно будет соединиться с сайтами, связанными с ссылками, чтобы сгенерировать предпросмотр. Вы всегда можете отключить предпросмотр ссылок в настройках Session.";
"modal_link_previews_button_title" = "Включить";
"vc_share_title" = "Share to Session";
"vc_share_loading_message" = "Preparing attachments...";
"vc_share_sending_message" = "Sending...";

@ -520,3 +520,6 @@
"modal_link_previews_title" = "Povoliť náhľad odkazov?";
"modal_link_previews_explanation" = "Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session's settings.";
"modal_link_previews_button_title" = "Povoliť";
"vc_share_title" = "Share to Session";
"vc_share_loading_message" = "Preparing attachments...";
"vc_share_sending_message" = "Sending...";

@ -496,3 +496,6 @@
"vc_qr_code_view_scan_qr_code_tab_title" = "Quét mã QR";
"vc_qr_code_view_scan_qr_code_explanation" = "Quét mã QR của ai đó để bắt đầu trò chuyện với họ";
"vc_view_my_qr_code_explanation" = "Đây là mã QR của bạn. Những người dùng khác có thể quét mã này và bắt đầu session với bạn.";
"vc_share_title" = "Share to Session";
"vc_share_loading_message" = "Preparing attachments...";
"vc_share_sending_message" = "Sending...";

@ -489,3 +489,6 @@
"vc_qr_code_view_scan_qr_code_tab_title" = "扫描二维码";
"vc_qr_code_view_scan_qr_code_explanation" = "扫描对方的二维码以发起对话";
"vc_view_my_qr_code_explanation" = "这是您的二维码。其他用户可以对其进行扫描以发起与您的对话。";
"vc_share_title" = "Share to Session";
"vc_share_loading_message" = "Preparing attachments...";
"vc_share_sending_message" = "Sending...";

@ -1,28 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="j1y-V4-xli">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="j1y-V4-xli">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13527"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Share View Controller-->
<!--ShareVC-->
<scene sceneID="ceB-am-kn3">
<objects>
<viewController id="j1y-V4-xli" customClass="ShareViewController" customModule="SignalShareExtension" customModuleProvider="target" sceneMemberID="viewController">
<viewController id="j1y-V4-xli" customClass="ShareVC" customModule="SessionShareExtension" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" opaque="NO" contentMode="scaleToFill" id="wbc-yd-nQP">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="SessionGreen64" translatesAutoresizingMaskIntoConstraints="NO" id="Hvu-b1-e3Q">
<rect key="frame" x="155.5" y="301.5" width="64" height="64"/>
<constraints>
<constraint firstAttribute="width" constant="64" id="bwQ-vq-ZId"/>
<constraint firstAttribute="height" constant="64" id="vrS-W4-ZXG"/>
</constraints>
</imageView>
</subviews>
<viewLayoutGuide key="safeArea" id="1Xd-am-t49"/>
<color key="backgroundColor" red="0.086274509803921567" green="0.086274509803921567" blue="0.086274509803921567" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="Hvu-b1-e3Q" firstAttribute="centerY" secondItem="wbc-yd-nQP" secondAttribute="centerY" id="0no-Qo-O3k"/>
<constraint firstItem="Hvu-b1-e3Q" firstAttribute="centerX" secondItem="wbc-yd-nQP" secondAttribute="centerX" id="Vsx-JI-9HN"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="CEy-Cv-SGf" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="126" y="133"/>
</scene>
</scenes>
<resources>
<image name="SessionGreen64" width="57.5" height="64"/>
</resources>
</document>

@ -1,96 +0,0 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
import UIKit
import PureLayout
// All Observer methods will be invoked from the main thread.
protocol SAEFailedViewDelegate: class {
func shareViewWasCancelled()
}
class SAEFailedViewController: UIViewController {
weak var delegate: SAEFailedViewDelegate?
let failureTitle: String
let failureMessage: String
// MARK: Initializers and Factory Methods
init(delegate: SAEFailedViewDelegate, title: String, message: String) {
self.delegate = delegate
self.failureTitle = title
self.failureMessage = message
super.init(nibName: nil, bundle: nil)
}
@available(*, unavailable, message:"use other constructor instead.")
required init?(coder aDecoder: NSCoder) {
notImplemented()
}
override func loadView() {
super.loadView()
self.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel,
target: self,
action: #selector(cancelPressed))
self.navigationItem.title = "Session"
self.view.backgroundColor = UIColor.ows_signalBrandBlue
let logoImage = UIImage(named: "logoSignal")
let logoImageView = UIImageView(image: logoImage)
self.view.addSubview(logoImageView)
logoImageView.autoCenterInSuperview()
let logoSize = CGFloat(120)
logoImageView.autoSetDimension(.width, toSize: logoSize)
logoImageView.autoSetDimension(.height, toSize: logoSize)
let titleLabel = UILabel()
titleLabel.textColor = UIColor.white
titleLabel.font = UIFont.ows_mediumFont(withSize: 18)
titleLabel.text = failureTitle
titleLabel.textAlignment = .center
titleLabel.numberOfLines = 0
titleLabel.lineBreakMode = .byWordWrapping
self.view.addSubview(titleLabel)
titleLabel.autoPinEdge(toSuperviewEdge: .leading, withInset: 20)
titleLabel.autoPinEdge(toSuperviewEdge: .trailing, withInset: 20)
titleLabel.autoPinEdge(.top, to: .bottom, of: logoImageView, withOffset: 25)
let messageLabel = UILabel()
messageLabel.textColor = UIColor.white
messageLabel.font = UIFont.ows_regularFont(withSize: 14)
messageLabel.text = failureMessage
messageLabel.textAlignment = .center
messageLabel.numberOfLines = 0
messageLabel.lineBreakMode = .byWordWrapping
self.view.addSubview(messageLabel)
messageLabel.autoPinEdge(toSuperviewEdge: .leading, withInset: 20)
messageLabel.autoPinEdge(toSuperviewEdge: .trailing, withInset: 20)
messageLabel.autoPinEdge(.top, to: .bottom, of: titleLabel, withOffset: 10)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.isNavigationBarHidden = false
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
}
// MARK: - Event Handlers
@objc func cancelPressed(sender: UIButton) {
guard let delegate = delegate else {
owsFailDebug("missing delegate")
return
}
delegate.shareViewWasCancelled()
}
}

@ -1,110 +0,0 @@
//
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
import UIKit
import PureLayout
class SAELoadViewController: UIViewController {
weak var delegate: ShareViewDelegate?
var activityIndicator: UIActivityIndicatorView!
var progressView: UIProgressView!
var progress: Progress? {
didSet {
guard progressView != nil else {
return
}
updateProgressViewVisability()
progressView.observedProgress = progress
}
}
func updateProgressViewVisability() {
guard progressView != nil, activityIndicator != nil else {
return
}
// Prefer to show progress view when progress is present
if self.progress == nil {
activityIndicator.startAnimating()
self.progressView.isHidden = true
self.activityIndicator.isHidden = false
} else {
activityIndicator.stopAnimating()
self.progressView.isHidden = false
self.activityIndicator.isHidden = true
}
}
// MARK: Initializers and Factory Methods
init(delegate: ShareViewDelegate) {
self.delegate = delegate
super.init(nibName: nil, bundle: nil)
}
@available(*, unavailable, message:"use other constructor instead.")
required init?(coder aDecoder: NSCoder) {
notImplemented()
}
override func loadView() {
super.loadView()
self.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel,
target: self,
action: #selector(cancelPressed))
self.navigationItem.title = "Session"
self.view.backgroundColor = UIColor.ows_signalBrandBlue
let activityIndicator = UIActivityIndicatorView(style: .whiteLarge)
self.activityIndicator = activityIndicator
self.view.addSubview(activityIndicator)
activityIndicator.autoCenterInSuperview()
progressView = UIProgressView(progressViewStyle: .default)
progressView.observedProgress = progress
self.view.addSubview(progressView)
progressView.autoVCenterInSuperview()
progressView.autoPinWidthToSuperview(withMargin: ScaleFromIPhone5(30))
progressView.progressTintColor = UIColor.white
updateProgressViewVisability()
let label = UILabel()
label.textColor = UIColor.white
label.font = UIFont.ows_mediumFont(withSize: 18)
label.text = NSLocalizedString("SHARE_EXTENSION_LOADING",
comment: "Indicates that the share extension is still loading.")
self.view.addSubview(label)
label.autoHCenterInSuperview()
label.autoPinEdge(.top, to: .bottom, of: activityIndicator, withOffset: 25)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.isNavigationBarHidden = false
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
}
// MARK: - Event Handlers
@objc func cancelPressed(sender: UIButton) {
guard let delegate = delegate else {
owsFailDebug("missing delegate")
return
}
delegate.shareViewWasCancelled()
}
}

@ -43,7 +43,7 @@ NS_ASSUME_NONNULL_BEGIN
UIView.appearance.tintColor = LKColors.text;
// Loki: Set gradient background
// Gradient background
self.view.backgroundColor = UIColor.clearColor;
CAGradientLayer *layer = [CAGradientLayer new];
layer.frame = UIScreen.mainScreen.bounds;
@ -52,20 +52,21 @@ NS_ASSUME_NONNULL_BEGIN
layer.colors = @[ (id)gradientStartColor.CGColor, (id)gradientEndColor.CGColor ];
[self.view.layer insertSublayer:layer atIndex:0];
// Loki: Set navigation bar background color
// Navigation bar background color
UINavigationBar *navigationBar = self.navigationController.navigationBar;
[navigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
navigationBar.shadowImage = [UIImage new];
[navigationBar setTranslucent:NO];
navigationBar.barTintColor = LKColors.navigationBarBackground;
// Loki: Customize title
// Title
UILabel *titleLabel = [UILabel new];
titleLabel.text = NSLocalizedString(@"Share to Session", @"");
titleLabel.text = NSLocalizedString(@"vc_share_title", @"");
titleLabel.textColor = LKColors.text;
titleLabel.font = [UIFont boldSystemFontOfSize:LKValues.veryLargeFontSize];
self.navigationItem.titleView = titleLabel;
// Close button
UIBarButtonItem *closeButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"X"] style:UIBarButtonItemStylePlain target:self action:@selector(dismissPressed:)];
closeButton.tintColor = LKColors.text;
self.navigationItem.leftBarButtonItem = closeButton;

@ -1,25 +1,12 @@
//
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
import UIKit
import PureLayout
import CoreServices
import PromiseKit
import SessionUIKit
import CoreServices
@objc
public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailedViewDelegate, AppModeManagerDelegate {
// MARK: - Dependencies
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: Error
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: Lifecycle
override func loadView() {
super.loadView()
// This should be the first thing we do.
@ -56,8 +35,6 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed
_ = AppVersion.sharedInstance()
startupLogging()
Cryptography.seedRandom()
// We don't need to use DeviceSleepManager in the SAE.
@ -69,18 +46,6 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed
return
}
// If we haven't migrated the database file to the shared data
// directory we can't load it, and therefore can't init TSSPrimaryStorage,
// and therefore don't want to setup most of our machinery (Environment,
// most of the singletons, etc.). We just want to show an error view and
// abort.
isReadyForAppExtensions = OWSPreferences.isReadyForAppExtensions()
guard isReadyForAppExtensions else {
showNotReadyView()
return
}
// We shouldn't set up our environment until after we've consulted isReadyForAppExtensions.
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
// Don't display load screen immediately, in hopes that we can avoid it altogether.
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()
// We don't need to use "screen protection" in the SAE.
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)
// Share extensions reside in a process that may be reused between usages.
// That isn't safe; the codebase is full of statics (e.g. singletons) which
// we can't easily clean up.
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("")
// We don't need to use "screen protection" in the SAE.
ensureRootViewController()
// We don't need to use RTCInitializeSSL() in the SAE.
if tsAccountManager.isRegistered() {
// At this point, potentially lengthy DB locking migrations could be running.
// Avoid blocking app launch by putting all further possible DB access in async block
DispatchQueue.global().async { [weak self] in
guard let _ = self else { return }
Logger.info("running post launch block for registered user: \(TSAccountManager.localNumber()!)")
// We don't need to use OWSDisappearingMessagesJob in the SAE.
// We don't need to use OWSFailedMessagesJob in the SAE.
// We don't need to use OWSFailedAttachmentDownloadsJob in the SAE.
}
} else {
Logger.info("running post launch block for unregistered user.")
// We don't need to update the app icon badge number in the SAE.
// We don't need to prod the TSSocketManager in the SAE.
}
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()!)")
// We don't need to use the TSSocketManager in the SAE.
// We don't need to fetch messages in the SAE.
// We don't need to use OWSSyncPushTokensJob in the SAE.
}
}
}
@objc
@ -256,19 +117,15 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed
// it will also run all deferred blocks.
AppReadiness.setAppIsReady()
if tsAccountManager.isRegistered() {
Logger.info("localNumber: \(TSAccountManager.localNumber()!)")
// We don't need to use messageFetcherJob in the SAE.
// We don't need to use SyncPushTokensJob in the SAE.
}
// We don't need to use DeviceSleepManager in the SAE.
AppVersion.sharedInstance().saeLaunchDidComplete()
ensureRootViewController()
showLockScreenOrMainContent()
// We don't need to use OWSMessageReceiver in the SAE.
// We don't need to use OWSBatchMessageProcessor in the SAE.
@ -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()!)")
// We don't need to use ExperienceUpgradeFinder in the SAE.
// We don't need to use OWSDisappearingMessagesJob in the SAE.
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() {
// This is a rare edge case, but we want to ensure that the user
// is has already saved their local profile key in the main app.
showNotReadyView()
} else {
DispatchQueue.main.async { [weak self] in
guard let strongSelf = self else { return }
strongSelf.buildAttachmentsAndPresentConversationPicker()
}
}
// We don't use the AppUpdateNag in the SAE.
}
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: Error Views
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: View Lifecycle
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)
// Share extensions reside in a process that may be reused between usages.
// That isn't safe; the codebase is full of statics (e.g. singletons) which
@ -446,136 +173,68 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed
ExitShareExtension()
}
@objc
func owsApplicationWillEnterForeground() throws {
AssertIsOnMainThread()
Logger.debug("")
// If a user unregisters in the main app, the SAE should shut down
// immediately.
guard !tsAccountManager.isRegistered() else {
// If user is registered, do nothing.
return
}
guard let shareViewNavigationController = shareViewNavigationController else {
owsFailDebug("Missing shareViewNavigationController")
return
}
guard let firstViewController = shareViewNavigationController.viewControllers.first else {
// If no view has been presented yet, do nothing.
return
}
if let _ = firstViewController as? SAEFailedViewController {
// If root view is an error view, do nothing.
return
}
throw ShareViewControllerError.notRegistered
}
// MARK: ShareViewDelegate, SAEFailedViewDelegate
public func shareViewWasUnlocked() {
Logger.info("")
presentContentView()
// MARK: App Mode
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 // Not applicable to share extensions
}
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: Updating
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: Helpers
// This view controller is not visible to the user. It exists to intercept touches, set up the
// extensions dependencies, and eventually present a visible view to the user.
// For speed of presentation, we only present a single modal, and if it's already been presented
// we swap out the contents.
// e.g. if loading is taking a while, the user will see the load screen presented with a modal
// animation. Next, when loading completes, the load view will be switched out for the contact
// picker view.
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: Attachment Prep
private class func itemMatchesSpecificUtiType(itemProvider: NSItemProvider, utiType: String) -> Bool {
// URLs, contacts and other special items have to be detected separately.
// Many shares (e.g. pdfs) will register many UTI types and/or conform to kUTTypeData.
@ -708,7 +367,7 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed
Logger.error("invalid inputItem \(inputItemRaw)")
continue
}
if let itemProviders = ShareViewController.preferredItemProviders(inputItem: inputItem) {
if let itemProviders = ShareVC.preferredItemProviders(inputItem: inputItem) {
return Promise.value(itemProviders)
}
}
@ -753,7 +412,7 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed
// * UTIs aren't very descriptive (there are far more MIME types than UTI types)
// so in the case of file attachments we try to refine the attachment type
// using the file extension.
guard let srcUtiType = ShareViewController.utiType(itemProvider: itemProvider) else {
guard let srcUtiType = ShareVC.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 = ShareViewController.createDataSource(utiType: utiType, url: url, customFileName: loadedItem.customFileName) else {
guard let dataSource = ShareVC.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 {
// This can happen, e.g. when sharing a quicktime-video from iCloud drive.
let (promise, exportSession) = SignalAttachment.compressVideoAsMp4(dataSource: dataSource, dataUTI: specificUTIType)
// TODO: How can we move waiting for this export to the end of the share flow rather than having to do it up front?
// Ideally we'd be able to start it here, and not block the UI on conversion unless there's still work to be done
// when the user hits "send".
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
// apply any conversion, so no need to relocate the app.
return !itemProvider.registeredTypeIdentifiers.contains(kUTTypeMPEG4 as String)
}
// MARK: App Mode
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 // Not applicable to share extensions
}
}
// Exposes a Progress object, whose progress is updated by polling the return of a given block
private class ProgressPoller: NSObject {
let progress: Progress
private(set) var timer: Timer?
// Higher number offers higher ganularity
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()
}
}
}
}

@ -0,0 +1,92 @@
import UIKit
import SessionUIKit
final class SimplifiedConversationCell : UITableViewCell {
var threadViewModel: ThreadViewModel! { didSet { update() } }
static let reuseIdentifier = "SimplifiedConversationCell"
// MARK: UI Components
private lazy var accentLineView: UIView = {
let result = UIView()
result.backgroundColor = Colors.destructive
return result
}()
private lazy var profilePictureView = ProfilePictureView()
private lazy var displayNameLabel: UILabel = {
let result = UILabel()
result.font = .boldSystemFont(ofSize: Values.mediumFontSize)
result.textColor = Colors.text
result.lineBreakMode = .byTruncatingTail
return result
}()
// MARK: Initialization
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setUpViewHierarchy()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setUpViewHierarchy()
}
private func setUpViewHierarchy() {
// Background color
backgroundColor = Colors.cellBackground
// Highlight color
let selectedBackgroundView = UIView()
selectedBackgroundView.backgroundColor = Colors.cellSelected
self.selectedBackgroundView = selectedBackgroundView
// Accent line view
accentLineView.set(.width, to: Values.accentLineThickness)
accentLineView.set(.height, to: 68)
// Profile picture view
let profilePictureViewSize = Values.mediumProfilePictureSize
profilePictureView.set(.width, to: profilePictureViewSize)
profilePictureView.set(.height, to: profilePictureViewSize)
profilePictureView.size = profilePictureViewSize
// Main stack view
let stackView = UIStackView(arrangedSubviews: [ accentLineView, profilePictureView, displayNameLabel, UIView.hSpacer(0) ])
stackView.axis = .horizontal
stackView.alignment = .center
stackView.spacing = Values.mediumSpacing
addSubview(stackView)
stackView.pin(to: self)
}
// MARK: Updating
private func update() {
AssertIsOnMainThread()
guard let thread = threadViewModel?.threadRecord else { return }
let isBlocked: Bool
if let thread = thread as? TSContactThread {
isBlocked = SSKEnvironment.shared.blockingManager.isRecipientIdBlocked(thread.contactSessionID())
} else {
isBlocked = false
}
accentLineView.alpha = isBlocked ? 1 : 0
profilePictureView.update(for: thread)
displayNameLabel.text = getDisplayName()
}
private func getDisplayName() -> String {
if threadViewModel.isGroupThread {
if threadViewModel.name.isEmpty {
return "Unknown Group"
} else {
return threadViewModel.name
}
} else {
if threadViewModel.threadRecord.isNoteToSelf() {
return NSLocalizedString("NOTE_TO_SELF", comment: "")
} else {
let hexEncodedPublicKey = threadViewModel.contactSessionID!
return Storage.shared.getContact(with: hexEncodedPublicKey)?.displayName(for: .regular) ?? hexEncodedPublicKey
}
}
}
}

@ -0,0 +1,157 @@
import SessionUIKit
final class ThreadPickerVC : UIViewController, UITableViewDataSource, UITableViewDelegate, AttachmentApprovalViewControllerDelegate {
private var threads: YapDatabaseViewMappings!
private var threadViewModelCache: [String:ThreadViewModel] = [:] // Thread ID to ThreadViewModel
private var selectedThread: TSThread?
var shareVC: ShareVC?
private var threadCount: UInt {
threads.numberOfItems(inGroup: TSInboxGroup)
}
private lazy var dbConnection: YapDatabaseConnection = {
let result = OWSPrimaryStorage.shared().newDatabaseConnection()
result.objectCacheLimit = 500
return result
}()
private lazy var tableView: UITableView = {
let result = UITableView()
result.backgroundColor = .clear
result.separatorStyle = .none
result.register(SimplifiedConversationCell.self, forCellReuseIdentifier: SimplifiedConversationCell.reuseIdentifier)
result.showsVerticalScrollIndicator = false
return result
}()
private lazy var fadeView: UIView = {
let result = UIView()
let gradient = Gradients.homeVCFade
result.setGradient(gradient)
result.isUserInteractionEnabled = false
return result
}()
// MARK: Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
// Gradient
view.backgroundColor = .clear
let gradient = Gradients.defaultBackground
view.setGradient(gradient)
// Threads
dbConnection.beginLongLivedReadTransaction() // Freeze the connection for use on the main thread (this gives us a stable data source that doesn't change until we tell it to)
threads = YapDatabaseViewMappings(groups: [ TSInboxGroup ], view: TSThreadDatabaseViewExtensionName) // The extension should be registered at this point
threads.setIsReversed(true, forGroup: TSInboxGroup)
dbConnection.read { transaction in
self.threads.update(with: transaction) // Perform the initial update
}
// Title
let titleLabel = UILabel()
titleLabel.text = NSLocalizedString("vc_share_title", comment: "")
titleLabel.textColor = Colors.text
titleLabel.font = .boldSystemFont(ofSize: Values.veryLargeFontSize)
navigationItem.titleView = titleLabel
// Table view
tableView.dataSource = self
tableView.delegate = self
view.addSubview(tableView)
tableView.pin(to: view)
view.addSubview(fadeView)
fadeView.pin(.leading, to: .leading, of: view)
let topInset = 0.15 * view.height()
fadeView.pin(.top, to: .top, of: view, withInset: topInset)
fadeView.pin(.trailing, to: .trailing, of: view)
fadeView.pin(.bottom, to: .bottom, of: view)
// Reload
reload()
}
// MARK: Table View Data Source
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Int(threadCount)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: SimplifiedConversationCell.reuseIdentifier) as! SimplifiedConversationCell
cell.threadViewModel = threadViewModel(at: indexPath.row)
return cell
}
// MARK: Updating
private func reload() {
AssertIsOnMainThread()
dbConnection.beginLongLivedReadTransaction() // Jump to the latest commit
dbConnection.read { transaction in
self.threads.update(with: transaction)
}
threadViewModelCache.removeAll()
tableView.reloadData()
}
// MARK: Interaction
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let thread = self.thread(at: indexPath.row), let attachments = ShareVC.attachmentPrepPromise?.value else { return }
self.selectedThread = thread
tableView.deselectRow(at: indexPath, animated: true)
let approvalVC = AttachmentApprovalViewController.wrappedInNavController(attachments: attachments, approvalDelegate: self)
navigationController!.present(approvalVC, animated: true, completion: nil)
}
func attachmentApproval(_ attachmentApproval: AttachmentApprovalViewController, didApproveAttachments attachments: [SignalAttachment], messageText: String?) {
let message = VisibleMessage()
message.sentTimestamp = NSDate.millisecondTimestamp()
message.text = messageText
let tsMessage = TSOutgoingMessage.from(message, associatedWith: selectedThread!)
Storage.write { transaction in
tsMessage.save(with: transaction)
}
shareVC!.dismiss(animated: true, completion: nil)
ModalActivityIndicatorViewController.present(fromViewController: shareVC!, canCancel: false, message: NSLocalizedString("vc_share_sending_message", comment: "")) { activityIndicator in
Storage.write { transaction in
MessageSender.sendNonDurably(message, with: attachments, in: self.selectedThread!, using: transaction).done { [weak self] _ in
guard let self = self else { return }
activityIndicator.dismiss { }
self.shareVC!.shareViewWasCompleted()
}.catch { [weak self] error in
guard let self = self else { return }
activityIndicator.dismiss { }
self.shareVC!.shareViewFailed(error: error)
}
}
}
}
func attachmentApprovalDidCancel(_ attachmentApproval: AttachmentApprovalViewController) {
// Do nothing
}
func attachmentApproval(_ attachmentApproval: AttachmentApprovalViewController, didChangeMessageText newMessageText: String?) {
// Do nothing
}
// MARK: Convenience
private func thread(at index: Int) -> TSThread? {
var thread: TSThread? = nil
dbConnection.read { transaction in
let ext = transaction.ext(TSThreadDatabaseViewExtensionName) as! YapDatabaseViewTransaction
thread = ext.object(atRow: UInt(index), inSection: 0, with: self.threads) as! TSThread?
}
return thread
}
private func threadViewModel(at index: Int) -> ThreadViewModel? {
guard let thread = thread(at: index) else { return nil }
if let cachedThreadViewModel = threadViewModelCache[thread.uniqueId!] {
return cachedThreadViewModel
} else {
var threadViewModel: ThreadViewModel? = nil
dbConnection.read { transaction in
threadViewModel = ThreadViewModel(thread: thread, transaction: transaction)
}
threadViewModelCache[thread.uniqueId!] = threadViewModel
return threadViewModel
}
}
}

@ -54,7 +54,6 @@ public class ModalActivityIndicatorViewController: OWSViewController {
fromViewController.present(view, animated: false) {
DispatchQueue.global().async {
backgroundBlock(view)
}
}
}

Loading…
Cancel
Save