Merge branch 'develop' into doug/4484_contacts_access

This commit is contained in:
Doug 2021-08-04 17:07:12 +01:00
commit ae438e1dfe
75 changed files with 710 additions and 713 deletions

View file

@ -1,17 +1,81 @@
Changes to be released in next version
=================================================
✨ Features
*
🙌 Improvements
* Settings: The notifications toggle no longer detects the system's "Deliver Quietly" configuration as disabled (#2368).
* Settings: Adds a link to open the Settings app to quickly configure app notifications.
* VoIP: Text & icon changes on call tiles (#4642).
🐛 Bugfix
*
⚠️ API Changes
*
🗣 Translations
*
🧱 Build
*
Others
*
Changes in 1.4.9 (2021-08-03)
=================================================
✨ Features
*
🙌 Improvements
* Voice Messages: Increased recording state microphone icon size
* Voice Messages: Using "Voice message - MM.dd.yyyy HH.mm.ss" as the format for recorded audio files
🐛 Bugfix
* Voice Messages: Fixed race conditions when sending voice messages (#4641)
⚠️ API Changes
*
🗣 Translations
*
🧱 Build
*
Others
*
Improvements:
Changes in 1.4.8 (2021-07-29)
=================================================
✨ Features
*
🙌 Improvements
* Room: Added support for Voice Messages (#4090, #4091, #4092, #4094, #4095, #4096)
* Remove the directory section from the Rooms tab.
* Rooms Tab: Remove the directory section (#4521).
* Notifications: Show decrypted content is enabled by default (#4519).
* People Tab: Remove the local contacts section (#4523).
* Contacts: Delay access to local contacts until they're needed for display (#4616).
* RecentsDataSource: Factorize section reset in one place (target #4591).
* Voice Messages: Tap/hold to send voice messages isn't intuitive (#4601).
* Voice Messages: copy could be improved (#4604).
* Slide to lock should be more generous (#4602).
🐛 Bugfix
* Room: Fixed mentioning users from room info member details (#4583)
* Settings: Disabled autocorrection when entering an identity server (#4593).
* Room Notification Settings: Fix Crash when opening the new Room Notification Settings Screen (Not yet released) (#4599).
* AuthenticationViewController: Fix crash on authentication if an intermediate view was presented (#4606).
* Room: Fixed crash when opening a read-only room (#4620).
* Voice Messages: Tapping on waveform in composer glitches UI (#4603).
⚠️ API Changes
*
@ -26,6 +90,9 @@ Others
* Separated CI jobs into individual actions
* Update Gemfile.lock
Improvements:
* Upgrade MatrixKit version ([v0.15.6](https://github.com/matrix-org/matrix-ios-kit/releases/tag/v0.15.6)).
Changes in 1.4.7 (2021-07-22)
=================================================

View file

@ -22,8 +22,8 @@ APPLICATION_GROUP_IDENTIFIER = group.im.vector
APPLICATION_SCHEME = element
// Version
MARKETING_VERSION = 1.4.8
CURRENT_PROJECT_VERSION = 1.4.8
MARKETING_VERSION = 1.4.10
CURRENT_PROJECT_VERSION = 1.4.10
// Team

View file

@ -11,7 +11,7 @@ use_frameworks!
# - `{ {kit spec hash} => {sdk spec hash}` to depend on specific pod options (:git => …, :podspec => …) for each repo. Used by Fastfile during CI
#
# Warning: our internal tooling depends on the name of this variable name, so be sure not to change it
$matrixKitVersion = '= 0.15.5'
$matrixKitVersion = '= 0.15.6'
# $matrixKitVersion = :local
# $matrixKitVersion = {'develop' => 'develop'}

View file

@ -58,29 +58,29 @@ PODS:
- MatomoTracker (7.4.1):
- MatomoTracker/Core (= 7.4.1)
- MatomoTracker/Core (7.4.1)
- MatrixKit (0.15.5):
- MatrixKit (0.15.6):
- Down (~> 0.11.0)
- DTCoreText (~> 1.6.25)
- HPGrowingTextView (~> 1.1)
- libPhoneNumber-iOS (~> 0.9.13)
- MatrixKit/Core (= 0.15.5)
- MatrixSDK (= 0.19.5)
- MatrixKit/Core (0.15.5):
- MatrixKit/Core (= 0.15.6)
- MatrixSDK (= 0.19.6)
- MatrixKit/Core (0.15.6):
- Down (~> 0.11.0)
- DTCoreText (~> 1.6.25)
- HPGrowingTextView (~> 1.1)
- libPhoneNumber-iOS (~> 0.9.13)
- MatrixSDK (= 0.19.5)
- MatrixSDK (0.19.5):
- MatrixSDK/Core (= 0.19.5)
- MatrixSDK/Core (0.19.5):
- MatrixSDK (= 0.19.6)
- MatrixSDK (0.19.6):
- MatrixSDK/Core (= 0.19.6)
- MatrixSDK/Core (0.19.6):
- AFNetworking (~> 4.0.0)
- GZIP (~> 1.3.0)
- libbase58 (~> 0.1.4)
- OLMKit (~> 3.2.4)
- Realm (= 10.7.6)
- SwiftyBeaver (= 1.9.5)
- MatrixSDK/JingleCallStack (0.19.5):
- MatrixSDK/JingleCallStack (0.19.6):
- JitsiMeetSDK (= 3.5.0)
- MatrixSDK/Core
- OLMKit (3.2.4):
@ -124,7 +124,7 @@ DEPENDENCIES:
- KeychainAccess (~> 4.2.2)
- KTCenterFlowLayout (~> 1.3.1)
- MatomoTracker (~> 7.4.1)
- MatrixKit (= 0.15.5)
- MatrixKit (= 0.15.6)
- MatrixSDK
- MatrixSDK/JingleCallStack
- OLMKit
@ -204,8 +204,8 @@ SPEC CHECKSUMS:
LoggerAPI: ad9c4a6f1e32f518fdb43a1347ac14d765ab5e3d
Logging: beeb016c9c80cf77042d62e83495816847ef108b
MatomoTracker: 24a846c9d3aa76933183fe9d47fd62c9efa863fb
MatrixKit: 7606227237cf58c1a1a2235547222c5d75b464c4
MatrixSDK: 9fa30f9ca2504c4251b99212dcf4ff569bbf45b1
MatrixKit: 740fee40187fe84099678c56b2f080d877dd7a65
MatrixSDK: 04a7f15b03b3def5af644444f3364b8272fb8efc
OLMKit: 2d73cd67d149b5c3e3a8eb8ecae93d0b429d8a02
ReadMoreTextView: 19147adf93abce6d7271e14031a00303fe28720d
Realm: ed860452717c8db8f4bf832b6807f7f2ce708839
@ -219,6 +219,6 @@ SPEC CHECKSUMS:
zxcvbn-ios: fef98b7c80f1512ff0eec47ac1fa399fc00f7e3c
ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb
PODFILE CHECKSUM: c7386ecfb38fc4302613c915aef79eebdb98a53d
PODFILE CHECKSUM: c1f1d1137ebacb6c74706cb28d5c10f26d6fe655
COCOAPODS: 1.10.1

View file

@ -4,8 +4,7 @@
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
runPostActionsOnFailure = "NO">
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"

View file

@ -0,0 +1,23 @@
{
"images" : [
{
"filename" : "Video.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Video@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Video@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 401 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 615 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 771 B

View file

@ -0,0 +1,23 @@
{
"images" : [
{
"filename" : "Voice call.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Voice call@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Voice call@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 644 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 866 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 333 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 498 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 633 B

View file

@ -1,17 +1,17 @@
{
"images" : [
{
"filename" : "24.png",
"filename" : "Video.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "24@2x.png",
"filename" : "Video@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "24@3x.png",
"filename" : "Video@3x.png",
"idiom" : "universal",
"scale" : "3x"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 315 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 419 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 576 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

View file

@ -298,7 +298,6 @@
"settings_global_settings_info" = "Глобални настройки на известия са налични на Вашия %@ уеб клиент";
"settings_pin_rooms_with_missed_notif" = "Закачане на стаи с пропуснати известия";
"settings_pin_rooms_with_unread" = "Закачане на стаи с непрочетени съобщения";
"settings_on_denied_notification" = "Известията са отказани за %@. Моля, включете ги в настройките на устройството";
"settings_enable_callkit" = "Интегрирани разговори";
"settings_callkit_info" = "Получаване на входящи повиквания при заключен екран. Показване на Element разговори в историята на системата. Ако iCloud е включен, историята на разговорите се споделя с Apple.";
"settings_ui_language" = "Език";

View file

@ -295,7 +295,6 @@
"settings_global_settings_info" = "Els paràmetres de notificació globals estan disponibles al teu client web %@";
"settings_pin_rooms_with_missed_notif" = "Fixa sales amb notificacions pendents";
"settings_pin_rooms_with_unread" = "Fixa sales amb missatges pendents";
"settings_on_denied_notification" = "%@ no permet notificacions, si us plau activa-les en els ajustos del teu dispositiu";
"settings_enable_callkit" = "Trucades integrades";
"settings_callkit_info" = "Rep les trucades entrants a la pantalla de bloqueig. Consulta les trucades de Element a l'historial de trucades del sistema. Si està habilitat iCloud, aquest historial de trucades es compartirà amb Apple.";
"settings_ui_language" = "Llenguatge";

View file

@ -0,0 +1 @@

View file

@ -4,11 +4,11 @@
"title_people" = "Lidé";
"title_rooms" = "Místnosti";
"title_groups" = "Komunity";
"warning" = "Varování!";
"warning" = "Varování";
// Actions
"view" = "Zobrazit zdroj";
"view" = "Zobrazit";
"next" = "Další";
"back" = "Pro zabezpečení se při odhlašování odstraní všechny šifrovací klíče typu end-to-end, a i po přihlašení bude historie šifrovaných kanálů nečitelná.\nZvolte export, chcete-li je zálohovat, než se odhlásíte.";
"back" = "Zpět";
"continue" = "Pokračovat";
"create" = "Vytvořit";
"start" = "Spustit";
@ -52,7 +52,7 @@
"auth_repeat_password_placeholder" = "Zopakovat heslo";
"auth_repeat_new_password_placeholder" = "Potvrďte své nové heslo";
"auth_home_server_placeholder" = "URL (např. https://matrix.org)";
"auth_identity_server_placeholder" = "URL (např. https://matrix.org)";
"auth_identity_server_placeholder" = "URL (např. https://vector.im)";
"auth_invalid_login_param" = "Nesprávné uživatelské jméno nebo heslo";
"auth_invalid_user_name" = "Uživatelské jméno může obsahovat pouze písmena, číslice, tečky, pomlčky a podtržítka";
"auth_invalid_password" = "Heslo je velmi krátké (min 6)";
@ -130,7 +130,7 @@
// Directory
"directory_cell_title" = "Procházet adresář";
"directory_cell_description" = "%tu místnosti";
"directory_search_results_title" = "Procházet výsledky výhledávání v adresáři";
"directory_search_results_title" = "Procházet výsledky v adresáři";
"directory_search_results" = "%tu výsledky nalezeny pro %@";
"directory_search_results_more_than" = ">%tu výsledky nalezeny pro %@";
"directory_searching_title" = "Vyhledávání v adresáři…";
@ -139,7 +139,7 @@
"contacts_address_book_section" = "LOKÁLNÍ KONTAKTY";
"contacts_address_book_matrix_users_toggle" = "Pouze Matrix uživatelé";
"contacts_address_book_no_contact" = "Žádné lokální kontakty";
"contacts_address_book_permission_denied" = "Nepovolil jste přístup aplikace Riot k místním kontaktům";
"contacts_address_book_permission_denied" = "Nepovolil jste přístup aplikace Element k místním kontaktům";
"contacts_user_directory_section" = "UŽIVATELSKÝ ADRESÁŘ";
"contacts_user_directory_offline_section" = "UŽIVATELSKÝ ADRESÁŘ (offline)";
// Chat participants
@ -162,8 +162,8 @@
"room_participants_ago" = "před";
"room_participants_action_section_admin_tools" = "Nástroje pro správce";
"room_participants_action_section_direct_chats" = "Přímé chaty";
"room_participants_action_section_devices" = "Zařízení";
"room_participants_action_section_other" = "Další";
"room_participants_action_section_devices" = "Relace";
"room_participants_action_section_other" = "Možnosti";
"room_participants_action_invite" = "Pozvat";
"room_participants_action_leave" = "Odejít z místnosti";
"room_participants_action_remove" = "Odebrat z této místnosti";
@ -178,7 +178,7 @@
"room_participants_action_start_video_call" = "Začít video hovor";
"room_participants_action_mention" = "Zmínka";
// Chat
"room_jump_to_first_unread" = "Přeskočit na první nepřečtenou zprávu";
"room_jump_to_first_unread" = "Přeskočit na nepřečtenou zprávu";
"room_new_message_notification" = "%d nová zpráva";
"room_new_messages_notification" = "%d nových zpráv";
"room_one_user_is_typing" = "%@ právě píše…";
@ -196,7 +196,7 @@
"room_delete_unsent_messages" = "Smazat neodeslané zprávy";
"room_event_action_copy" = "Kopírovat";
"room_event_action_quote" = "Citovat";
"room_event_action_redact" = "Redigovat";
"room_event_action_redact" = "Odstranit";
"room_event_action_more" = "Víc";
"room_event_action_share" = "Sdílet";
"room_event_action_permalink" = "Trvalý odkaz";
@ -211,12 +211,12 @@
"room_event_action_view_encryption" = "Informace o šifrování";
"room_event_failed_to_send" = "Odeslání se nezdařilo";
// Unknown devices
"unknown_devices_alert_title" = "V místnosti jsou neznámá zařízení";
"unknown_devices_alert_title" = "V místnosti jsou neznámé relace";
"unknown_devices_send_anyway" = "Přesto poslat";
"unknown_devices_call_anyway" = "Přesto zavolat";
"unknown_devices_answer_anyway" = "Přesto přijmout";
"unknown_devices_verify" = "Ověřit…";
"unknown_devices_title" = "Neznámá zařízení";
"unknown_devices_title" = "Neznámé relace";
// Room Title
"room_title_new_room" = "Nová místnost";
"room_participants_invite_another_user" = "Hledat / Pozvat podle uživatelského ID, jména nebo emailu";
@ -231,10 +231,117 @@
"settings_title" = "Nastavení";
"account_logout_all" = "Odhlásit všechny účty";
"settings_report_bug" = "Nahlásit chybu";
"settings_config_home_server" = "Domácího serveru je %@";
"settings_config_home_server" = "Domácí server je %@";
"settings_config_identity_server" = "Server identit je %@";
"settings_config_user_id" = "Přihlášen/a jako %@";
"auth_msisdn_validation_message" = "Odeslali jsme vám SMS aktivační kod. Prosím, zadejte jej níže.";
"auth_msisdn_validation_error" = "Nelze ověřit telefonní číslo.";
"auth_reset_password_success_message" = "Vaše heslo bylo úspěšně resetováno.\n\nByl jste právě odhlášen na všech vašich zařízeních a nebudete vánm nadále zasíláno oznámení. Pro znovu povolení zasílání oznámení, přihlašte se znovu na každém zařízení.";
"auth_reset_password_success_message" = "Vaše heslo bylo úspěšně resetováno.\n\nByl jste právě odhlášen na všech vašich relací a nebudete vánm nadále zasíláno oznámení. Pro znovu povolení zasílání oznámení, přihlašte se znovu na každém zařízení.";
"store_full_description" = "Element je novým typem komunikátoru a propojovací aplikace která:\n\n1. Dává vám kontrolu nad vaším soukromím\n2. Vás nechá komunikovat s kýmkoli v Matrix síti a dokonce mimo ni, díky integrace s aplikacemi jako je například Slack\n3. Vás chrání před reklamou, těžbou Vašich dat, nechráněnými přístupy nebo nezdokumentovanými fukncemi\n4. Zabezpečuje Vaši komunikaci pomocí koncového šifrování s distribuovaným ověřením ostatních\n\nElement se liší od ostatních komunikačních řešeních především tím, že je decentralizovaný a open-source\n\nElement vám umožňuje provozovat vlastní server anebo si vybrat nějakyý z veřejných, takže máte controlu nad vašimi konverzacemi a soukromím. Dává vám přístup do otevřené sítě, takže nejste odkázání jen ke komunikaci s ostatními uživateli Elementu. A je vysoce bezpečný.\n\nElement je toho všeho schopen díky svému operačnímu protokolu - Matrix, otevřeného standartu pro decentralizovanou komunikaci.\n\nElement vás nechává vybrat, kdo bude hostovat vaše konverzace. Přímo z aplikace si můžete vybrat několik rozdílných řešení:\n\n1. Účet zdarma na veřejném serveru matrix.org\n2. Vlastní hosting serveru na vlastním hardwaru\n3. Účet na přizpůsobeném serveru jednodýúchým přihlášením na hosting Element Matrix Services\n\nProč Element?\n\nVLASTNĚTE SVÁ DATA: Vy rozhodujete kde jsou vaše data a zprávy uchovávány. Svá data vlastníte a spravujete Vy, ne nějaká obří korporace, která o vás sbírá osobní data nebo poskytuje přístup dalším stranám.\n\nOTEVŘENÁ KOMUNIKACE A SPOLUPRÁCE: Máte možnost spojit se s kýmkoli v síti Matrix bez ohledu na jeho softwarové řešení, a dokonce se můžete připojit i na jiné komunikační protokoly, jako je Slack, IRC nebo XMPP (Jabber). Komunita podporuje i komunikátory jako Whatsapp, Telegram nebo iMessage.\n\nNEPROLOMITELNÉ ŠIFROVÁNÍ: Skutečné koncové šifrování (pouze přímí účastníci konverzace mají možnost rozšifrovat jejich zprávy) a pokročilé ověřování kontaktů.\n\nVŠESTRANNÉ KOMUNIKAČNÍ MOŽNOSTI: Textové zprávy, hlasové nebo videohovory, přenos souborů, sdílení obrazovky a mnoho dalších funkcí a možností pro implementaci. Vytvářejte místnosti a komunity a zůstaňte v kontaktu.\n\nKDEKOLI SE NACHÁZÍTE: Přístup k plně synchronizované historii konverzací máte kdekoli se nacházíte, ať už z aplikace anebo webového rozhraní na https://element.io/app.";
"user_verification_sessions_list_session_untrusted" = "Nedůvěryhodná";
"user_verification_sessions_list_session_trusted" = "Důvěryhodná";
"user_verification_sessions_list_table_title" = "Relace";
"user_verification_sessions_list_information" = "Zprávy s tímto uživatelem v této místnosti jsou šifrovány end-to-end a nemohou je číst třetí strany.";
"user_verification_sessions_list_user_trust_level_unknown_title" = "Není známo";
"user_verification_sessions_list_user_trust_level_warning_title" = "Varování";
// Sessions list
"user_verification_sessions_list_user_trust_level_trusted_title" = "Důvěryhodný";
"user_verification_start_additional_information" = "Abyste byli v bezpečí, dělejte to osobně nebo použijte jiný způsob komunikace.";
"user_verification_start_waiting_partner" = "Čekám na %@…";
"user_verification_start_information_part2" = " kontrolou jednorázového kódu na obou zařízeních.";
"user_verification_start_information_part1" = "Pro větší bezpečnost ověřit ";
// MARK: - User verification
// Start
"user_verification_start_verify_action" = "Spustit ověřování";
"key_verification_user_title" = "Ověřit je";
"room_participants_security_information_room_encrypted" = "Zprávy v této místnosti jsou šifrovány end-to-end.\n\nVaše zprávy jsou zabezpečeny zámky a jedinečnými klíči je můžete odemknout pouze vy a příjemce.";
"room_participants_security_information_room_not_encrypted" = "Zprávy v této místnosti nejsou šifrovány end-to-end.";
"room_participants_security_loading" = "Načítání…";
"room_participants_action_security_status_warning" = "Varování";
"room_participants_action_security_status_verify" = "Ověřit";
"room_participants_action_security_status_verified" = "Ověřeno";
"room_participants_action_section_security" = "Zabezpečení";
"room_participants_action_ban" = "Vyhodit z této místnosti";
"room_creation_error_invite_user_by_email_without_identity_server" = "Není nakonfigurován žádný server identity, takže nemůžete přidat účastníka pomocí e-mailu.";
"auth_softlogout_recover_encryption_keys" = "Přihlaste se a obnovte šifrovací klíče uložené výhradně v tomto zařízení. Potřebujete je ke čtení všech zabezpečených zpráv na jakémkoli zařízení.";
"auth_softlogout_reason" = "Váš správce domovského serveru (%1$@) vás odhlásil z vašeho účtu %2 @ (%3$@).";
"auth_softlogout_signed_out" = "Jste odhlášeni";
"auth_autodiscover_invalid_response" = "Neplatná odpověď na objevení domovského serveru";
"auth_accept_policies" = "Přečtěte si a přijměte zásady tohoto domovského serveru:";
"auth_add_email_and_phone_warning" = "Registrace pomocí e-mailu a telefonního čísla najednou ještě není podporována, dokud neexistuje rozhraní API. Zohledněno bude pouze telefonní číslo. Svůj e-mail můžete přidat do svého profilu v nastavení.";
"auth_reset_password_error_is_required" = "Není nakonfigurován žádný server identity: pro obnovení hesla přidejte jeden server z možností .";
"auth_forgot_password_error_no_configured_identity_server" = "Není nakonfigurován žádný server identity: pro obnovení hesla jeden přidejte .";
"settings_key_backup_button_delete" = "Smazat zálohu";
"settings_key_backup_button_restore" = "Obnovit ze zálohy";
"auth_login_single_sign_on" = "Přihlásit se";
// Accessibility
"accessibility_checkbox_label" = "zaškrtávací políčko";
"callbar_only_single_active_group" = "Klepnutím se připojíte ke skupinovému hovoru (%@)";
"callbar_return" = "Vrátit se";
"callbar_only_multiple_paused" = "%@ pozastavené hovory";
"callbar_only_single_paused" = "Pozastavený hovor";
"callbar_active_and_multiple_paused" = "1 aktivní hovor (%@) · %@ pozastavené hovory";
"callbar_active_and_single_paused" = "1 aktivní hovor (%@) · 1 pozastavený hovor";
// Call Bar
"callbar_only_single_active" = "Klepnutím se vrátíte k hovoru (%@)";
"less" = "Méně";
"more" = "Více";
"switch" = "Přepnout";
"joined" = "Připojil se";
"skip" = "Přeskočit";
"close" = "Zavřít";
"store_promotional_text" = "Aplikace pro chat a spolupráci chránící soukromí v otevřené síti. Žádný sběr dat, žádná zadní vrátka a žádný přístup třetích stran.";
// String for App Store
"store_short_description" = "Zabezpečený decentralizovaný chat/VoIP";
"e2e_key_backup_wrong_version_button_settings" = "Nastavení";
"side_menu_action_settings" = "Nastavení";
"room_details_photo" = "Obrázek místnosti";
"room_details_settings" = "Nastavení";
"room_details_integrations" = "Integrace";
"room_details_search" = "Vyhledat místnost";
"room_details_files" = "Nahrávání";
"room_details_people" = "Členové";
"room_details_title_for_dm" = "Podrobnosti";
"room_avatar_view_accessibility_hint" = "Změnit avatar místnosti";
"room_creation_appearance_picture" = "Obrázek chatu (volitelné)";
// Errors
"error_user_already_logged_in" = "Vypadá to, že se pokoušíte připojit k jinému domovskému serveru. Chcete se odhlásit?";
"social_login_button_title_sign_up" = "Zaregistrovat se pomocí %@";
"social_login_button_title_sign_in" = "Přihlásit se pomocí %@";
"social_login_button_title_continue" = "Pokračovat s %@";
"social_login_list_title_sign_up" = "nebo";
"social_login_list_title_sign_in" = "nebo";
// Social login
"social_login_list_title_continue" = "Pokračovat s";
"auth_softlogout_clear_data_sign_out" = "Odhlásit se";
"auth_softlogout_clear_data_sign_out_msg" = "Opravdu chcete vymazat všechny údaje aktuálně uložené v tomto zařízení? Chcete-li získat přístup k údajům a zprávám svého účtu, znovu se přihlaste.";
"auth_softlogout_clear_data_sign_out_title" = "Jste si jisti?";
"auth_softlogout_clear_data_button" = "Vymazat všechny údaje";
"auth_softlogout_clear_data_message_2" = "Vymažte, pokud jste s tímto zařízením skončili nebo se chcete přihlásit k jinému účtu.";
"auth_softlogout_clear_data_message_1" = "Varování: Vaše osobní údaje (včetně šifrovacích klíčů) jsou stále uloženy v tomto zařízení.";
"auth_softlogout_clear_data" = "Vymazat osobní údaje";
"auth_softlogout_sign_in" = "Přihlásit se";
"biometrics_setup_subtitle" = "Ušetřete si čas";
"biometrics_setup_enable_button_title_x" = "Povolit %@";
"biometrics_setup_title_x" = "Povolit %@";
"biometrics_settings_enable_x" = "Povolit %@";
"biometrics_mode_face_id" = "Face ID";
"biometrics_cant_unlocked_alert_title" = "Aplikaci nelze odemknout";
"biometrics_usage_reason" = "Pro přístup k vaší aplikaci je nutné ověření";
"biometrics_desetup_disable_button_title_x" = "Zakázat %@";
"biometrics_desetup_title_x" = "Zakázat %@";
"public_room_section_title" = "Veřejné místnosti (v %@):";
"homeserver_connection_lost" = "Nelze se připojit k domovskému serveru.";
"network_offline_prompt" = "Zdá se, že připojení k internetu je offline.";
"yesterday" = "Včera";
"today" = "Dnes";

View file

@ -373,7 +373,6 @@
"settings_global_settings_info" = "Mae gosodiadau hysbysu eang ar gael ar eich cleient %@ gwe";
"settings_pin_rooms_with_missed_notif" = "Pinio ystafelloedd gyda hysbysiadau heb eu gweld";
"settings_pin_rooms_with_unread" = "Pinio ystafelloedd gyda negeseuon heb eu darllen";
"settings_on_denied_notification" = "Gwrthodir hysbysiadau i %@, caniatewch nhw yn eich gosodiadau dyfais";
"settings_enable_callkit" = "Galw integredig";
"settings_callkit_info" = "Derbyn galwadau sy'n dod i mewn ar eich sgrin clo. Gwelwch eich galwadau Element yn hanes galwadau'r system. Os yw iCloud wedi'i alluogi, bydd yr hanes galw hwn yn cael ei rannu gydag Apple.";
"settings_calls_stun_server_fallback_button" = "Caniatáu gweinydd cymorth galw wrth gefn";

View file

@ -316,7 +316,6 @@
"settings_global_settings_info" = "Globale Benachrichtigungseinstellungen sind auf deinem %@ web-Client verfügbar";
"settings_pin_rooms_with_missed_notif" = "Pinnen von Räumen mit verpassten Benachrichtigungen";
"settings_pin_rooms_with_unread" = "Pinnen von Räumen mit ungelesenen Nachrichten";
"settings_on_denied_notification" = "Benachrichtigungen verboten für %@, bitte in den Geräte-Einstellungen erlauben";
"settings_contacts_discover_matrix_users" = "Entdecke andere Benutzer mittels E-Mail-Adressen oder Telefonnummern";
"settings_labs_e2e_encryption_prompt_message" = "Zum Fertigstellen der Verschlüsselung bitte neu anmelden.";
"settings_third_party_notices" = "Anmerkungen von Dritten";

View file

@ -492,11 +492,13 @@ Tap the + to start adding people.";
"settings_security" = "SECURITY";
"settings_enable_push_notif" = "Notifications on this device";
"settings_device_notifications" = "Device notifications";
"settings_show_decrypted_content" = "Show decrypted content";
"settings_global_settings_info" = "Global notification settings are available on your %@ web client";
"settings_pin_rooms_with_missed_notif" = "Pin rooms with missed notifications";
"settings_pin_rooms_with_unread" = "Pin rooms with unread messages";
"settings_on_denied_notification" = "Notifications are denied for %@, please allow them in your device settings";
"settings_notifications_disabled_alert_title" = "Notifications disabled";
"settings_notifications_disabled_alert_message" = "To enable notifications, go to your device settings.";
//"settings_enable_all_notif" = "Enable all notifications";
//"settings_messages_my_display_name" = "Msg containing my display name";
//"settings_messages_my_user_name" = "Msg containing my user name";
@ -844,14 +846,16 @@ Tap the + to start adding people.";
"event_formatter_rerequest_keys_part1_link" = "Re-request encryption keys";
"event_formatter_rerequest_keys_part2" = " from your other sessions.";
"event_formatter_message_edited_mention" = "(edited)";
"event_formatter_call_voice" = "Voice call";
"event_formatter_call_video" = "Video call";
"event_formatter_call_connecting" = "Connecting…";
"event_formatter_call_ringing" = "Ringing…";
"event_formatter_call_has_ended" = "Ended %@";
"event_formatter_call_you_currently_in" = "Active call";
"event_formatter_call_you_declined" = "You declined this call";
"event_formatter_call_you_missed" = "You missed this call";
"event_formatter_call_has_ended" = "Call ended %@";
"event_formatter_call_incoming_voice" = "Incoming voice call";
"event_formatter_call_incoming_video" = "Incoming video call";
"event_formatter_call_active_voice" = "Active voice call";
"event_formatter_call_active_video" = "Active video call";
"event_formatter_call_you_declined" = "Call declined";
"event_formatter_call_missed_voice" = "Missed voice call";
"event_formatter_call_missed_video" = "Missed video call";
"event_formatter_call_connection_failed" = "Connection failed";
"event_formatter_call_back" = "Call back";
"event_formatter_call_decline" = "Decline";
@ -1686,4 +1690,4 @@ Tap the + to start adding people.";
"voice_message_release_to_send" = "Hold to record, release to send";
"voice_message_remaining_recording_time" = "%@s left";
"voice_message_stop_locked_mode_recording" = "Tap on the wavelength to stop and playback";
"voice_message_stop_locked_mode_recording" = "Tap on your recording to stop or listen";

View file

@ -975,7 +975,6 @@
"settings_calls_stun_server_fallback_description" = "Permesi repaŝan servilon %@ asistan je vokoj, kiam la hejmservilo ne provizas servilon (via IP-adreso ne doniĝus dum voko).";
"settings_calls_stun_server_fallback_button" = "Permesi repaŝan servilon asistan je vokoj";
"settings_callkit_info" = "Ricevi vokpetojn ĉe via ŝlosa ekrano. Vidi viajn vokojn de Element ĉe la sistema vokhistorio. Se iCloud estas ŝaltita, la vokhistorio doniĝas ankaŭ al Apple.";
"settings_on_denied_notification" = "Sciigo por %s estas malŝaltitaj, bonvole permesu ilin per la agordoj de via aparato";
"settings_pin_rooms_with_unread" = "Alpingli ĉambrojn kun nelegitaj mesaĝoj";
"settings_pin_rooms_with_missed_notif" = "Alpinigli ĉambrojn kun nerimarkitaj sciigoj";
"settings_show_decrypted_content" = "Montri malĉifritajn enhavojn";

View file

@ -321,7 +321,6 @@
"settings_global_settings_info" = "Los ajustes de notificación globales están disponibles en tu cliente web %@";
"settings_pin_rooms_with_missed_notif" = "Fijar salas con notificaciones pendientes";
"settings_pin_rooms_with_unread" = "Fijar salas con mensajes no leídos";
"settings_on_denied_notification" = "Las notificaciones están denegadas para %@, por favor habilita notificaciones en los ajustes de tu dispositivo";
"settings_enable_callkit" = "Integración de llamadas";
"settings_callkit_info" = "Recibe llamadas entrantes en tu pantalla de bloqueo. Ve tus llamadas de Element en el historial de llamadas del sistema. Si iCloud está habilitado, este historial de llamadas se compartirá con Apple.";
"settings_ui_language" = "Idioma";

View file

@ -1,7 +1,7 @@
// Permissions usage explanations
"NSCameraUsageDescription" = "Kaameraga salvestatakse pilte ning videosid ja tehakse videokõnesid.";
"NSPhotoLibraryUsageDescription" = "Fotogaleriid kasutatakse fotode ja videote saatmiseks teistele kasutajatele.";
"NSMicrophoneUsageDescription" = "Mikrofoni kasutatakse videote salvestamisel ning kõnede tegemisel.";
"NSMicrophoneUsageDescription" = "Kõnede tegemiseks, videote ja häälsõnumite salvestamiseks vajab Element ligipääsu sinu seadme mikrofonile.";
"NSCalendarsUsageDescription" = "Vaata päevakavasse lisatud koosolekuid vastvast rakendusest.";
"NSContactsUsageDescription" = "Selleks, et leida Matrixi võrgu kasutajaid, võib Element saata sinu aadressiraamatus leiduvad e-posti aadressid ja telefoninumbrid sinu valitud Matrixi isikutuvastusserverile. Kui server seda toetab, siis andmed muudetakse enne saatmist räsideks - täpsema teabe leiad oma isikutuvastusserveri privaatsuspoliitikast.";
"NSFaceIDUsageDescription" = "Ligipääsuks sinu rakendusele on kasutusel Face ID.";

View file

@ -497,7 +497,6 @@
"settings_global_settings_info" = "Üldised teavituste seadistused leiduvad sinu %@ veebikliendis";
"settings_pin_rooms_with_missed_notif" = "Klammerda jututoad, kus leidub lugemata teavitusi";
"settings_pin_rooms_with_unread" = "Klammerda jututoad, kus leidub lugemata sõnumeid";
"settings_on_denied_notification" = "Teavitused on %@ jaoks keelatud, palun luba nad oma seadme seadistustes";
"settings_enable_callkit" = "Lõimitud helistamine";
"settings_callkit_info" = "Vasta kõnedele lukustuskuvalt. Vaata Element'i kõnesid süsteemi kõnelogist. Kui iCloud on kasutusel, siis kõnede ajalugu jagatakse Applega.";
"settings_calls_stun_server_fallback_button" = "Kasuta kõnehõlbustusserverit";
@ -1096,7 +1095,7 @@
"deactivate_account_informations_part3" = "\n\nSinu konto kustutamine ";
"deactivate_account_informations_part4_emphasize" = "vaikimisi ei tähenda, et unustatakse ka sinu saadetud sõnumid. ";
"deactivate_account_informations_part5" = "Kui sa siiski soovid seda, siis palun tee märge alljärgnevasse kasti.\n\nMatrix'i sõnumite nähtavus on sarnane e-posti kirjadega. Sõnumite unustamine tegelikult tähendab seda, et sinu varemsaadetud sõnumeid ei jagata uute või veel registreerumata kasutajatega, kuid registeerunud kasutajad, kes juba on need sõnumid saanud, võivad neid ka jätkuvalt lugeda.";
"rerequest_keys_alert_message" = "Palun käivita Element mõnes muus seadmes, mis suudab neid sõnumeid dekrüptoda ja seega saata krüptovõtmeid siia sessiooni.";
"rerequest_keys_alert_message" = "Palun käivita Element mõnes muus seadmes, mis suudab neid sõnumeid dekrüptida ja seega saata krüptovõtmeid siia sessiooni.";
"settings_discovery_three_pids_management_information_part1" = "Halda missuguse e-posti aadressi ja telefoninumbri alusel teised kasutajad saavad sind kutsuda jututubadesse. Lisa või eemalda e-posti aadresse ja telefoninumbreid sellest loendist ";
"settings_discovery_three_pid_details_information_email" = "Halda selle e-posti aadressi eelistusi, mille alusel teised kasutajad saavad sind leida ja kutsuda jututubade liikmeks. E-posti aadresse lisada ja muuta saad kasutajakonto seadistustest.";
"settings_discovery_three_pid_details_title_phone_number" = "Halda telefoninumbrit";
@ -1361,3 +1360,10 @@
// Room Notification Settings
"room_notifs_settings_notify_me_for" = "Teavita mind";
"room_details_notifs" = "Teavitused";
"voice_message_stop_locked_mode_recording" = "Salvestuse peatamiseks ja taasesituseks vajuta salvestuse vaadet";
"voice_message_remaining_recording_time" = "salvestusaega jäänud %@s";
// Mark: - Voice Messages
"voice_message_release_to_send" = "Salvestamiseks vajuta nuppu, saatmiseks lase nupp lahti";
"settings_labs_voice_messages" = "Häälsõnumid";

View file

@ -396,7 +396,6 @@
"room_participants_remove_third_party_invite_msg" = "Hirugarrengoen gonbidapenak kentzea ez da onartzen APIa ez dagoen bitartean";
"settings_sign_out_e2e_warn" = "Zure muturretik muturrerako zifratze gakoak galduko dituzu. Horrek esan nahi du ezin izango dituzula mezu zaharrak gehiago irakurri zifratutako geletan gailu honetatik.";
"settings_global_settings_info" = "Jakinarazpen orokorren ezarpenak eskuragarri daude zure %@ web bezeroan";
"settings_on_denied_notification" = "Jakinarazpenak ukatu dira %@(e)n, baimendu zure gailuaren ezarpenetan";
"room_details_history_section_prompt_msg" = "Historiala nork irakurri dezakeen aldatzea gelak honetara aurrerantzean bidalitako mezuei besterik ez zaie aplikatuko. Badagoen historialaren ikusgaitasuna ez da aldatuko.";
// Call
"call_incoming_voice_prompt" = "%@ erabiltzailearen deia jasotzen";

View file

@ -278,7 +278,6 @@
"settings_global_settings_info" = "Les paramètres de notification globaux sont disponibles sur votre client web %@";
"settings_pin_rooms_with_missed_notif" = "Épingler les salons avec des notifications non lues";
"settings_pin_rooms_with_unread" = "Épingler les salons avec des messages non lus";
"settings_on_denied_notification" = "Les notifications sont refusées pour %@, merci de les autoriser dans les paramètres de votre appareil";
"settings_unignore_user" = "Afficher tous les messages de %@ ?";
"settings_contacts_discover_matrix_users" = "Utiliser un e-mail ou un numéro de téléphone pour retrouver des utilisateurs";
"settings_contacts_phonebook_country" = "Pays pour le répertoire téléphonique";

View file

@ -1,7 +1,7 @@
// Permissions usage explanations
"NSCameraUsageDescription" = "A kamera fényképek, videók készítéséhez és videóhívásokhoz lesz használva.";
"NSPhotoLibraryUsageDescription" = "A fénykép galéria fényképek és videók küldéséhez lesz használva.";
"NSMicrophoneUsageDescription" = "A mikrofon videók készítéséhez és hívásokhoz lesz használva.";
"NSMicrophoneUsageDescription" = "A hívás indításához és fogadásához, videó és hangüzenet felvételéhez az Elementnek hozzáférési engedélyre van szüksége a mikrofonhoz.";
"NSContactsUsageDescription" = "Az olyan ismerősök felderítéséhez akik már használják a Matrixot, Elementet el tudja küldeni a címjegyzékben található e-mail címeket és telefonszámokat az általad választott Matrix azonosítási szervernek. Ahol lehetséges a személyes adatok hash-elve lesznek - kérlek ellenőrizd az azonosítási szervered adatvédelmi szabályait.";
"NSCalendarsUsageDescription" = "Nézd meg a találkozóidat az alkalmazásban.";
"NSFaceIDUsageDescription" = "Arc felismerés használata az alkalmazás eléréséhez.";

View file

@ -311,7 +311,6 @@
"settings_global_settings_info" = "Globális értesítési beállításokat a webes kliensedben találod: %@";
"settings_pin_rooms_with_missed_notif" = "Szobák kitűzése elszalasztott értesítésekkel";
"settings_pin_rooms_with_unread" = "Szobák kitűzése olvasatlan üzenetekkel";
"settings_on_denied_notification" = "Az értesítések tiltva vannak ehhez: %@, kérlek engedélyezd az eszköz beállításaiban";
"settings_enable_callkit" = "Beépített hívás";
"settings_callkit_info" = "Hívások fogadása a zárolt képernyőn. Element hívások megjelenítése a rendszer hívás naplójában. Ha az iCloud engedélyezett akkor a hívásnapló az Apple-el megosztásra kerül.";
"settings_ui_language" = "Nyelv";
@ -1411,3 +1410,23 @@
"settings_ui_theme_picker_message_invert_colours" = "„Autó” az eszközöd „invertált színek” beállítását használja";
"room_recents_unknown_room_error_message" = "A szoba nem található. Győződj meg róla, hogy létezik";
"room_creation_dm_error" = "Nem lehet elkészíteni a közvetlen beszélgetést. Ellenőrizd a meghívni kívánt felhasználót és próbáld újra.";
"key_verification_verify_qr_code_scan_code_other_device_action" = "Beolvasás ezzel az eszközzel";
"room_notifs_settings_encrypted_room_notice" = "Megjegyzendő, hogy titkosított szobákban a megemlítésekre és kulcsszavakra való értesítés mobil eszközökön nem érhető el.";
"room_notifs_settings_account_settings" = "Fiók beállítások";
"room_notifs_settings_manage_notifications" = "Értesítések kezelése itt: %@";
"room_notifs_settings_cancel_action" = "Mégsem";
"room_notifs_settings_done_action" = "Kész";
"room_notifs_settings_none" = "Semmi";
"room_notifs_settings_mentions_and_keywords" = "Csak megemlítések és kulcsszavak";
"room_notifs_settings_all_messages" = "Minden üzenet";
// Room Notification Settings
"room_notifs_settings_notify_me_for" = "Értesítés ezért:";
"room_details_notifs" = "Értesítések";
"voice_message_stop_locked_mode_recording" = "Megállításhoz és visszajátszáshoz koppints a felvételre";
"voice_message_remaining_recording_time" = "%@s távozott";
// Mark: - Voice Messages
"voice_message_release_to_send" = "Felvételhez tartsd nyomva, a küldéshez engedd el";
"settings_labs_voice_messages" = "Hang üzenetek";

View file

@ -442,7 +442,6 @@
"settings_fail_to_update_profile" = "Mistókst að uppfæra notandasnið";
"settings_pin_rooms_with_missed_notif" = "Festa spjallrásir með óskoðuðum tilkynningum";
"settings_pin_rooms_with_unread" = "Festa spjallrásir með ólesnum skilaboðum";
"settings_on_denied_notification" = "Tilkynningum er hafnað fyrir %@, leyfðu þær í stillingum tækisins";
"settings_ui_theme_picker_message" = "\"Sjálfvirkt\" notar \"Umsnúa litum\" stillingar tækisins";
"settings_contacts_discover_matrix_users" = "Notaðu tölvupóstföng og símanúmer til að finna notendur";
"settings_labs_e2e_encryption_prompt_message" = "Til að ljúka við uppsetningu á dulritun verðurðu að skrá þig inn aftur.";

View file

@ -1,7 +1,7 @@
// Permissions usage explanations
"NSCameraUsageDescription" = "La fotocamera viene utilizzata per scattare fotografie, registrare video ed eseguire videochiamate.";
"NSPhotoLibraryUsageDescription" = "La libreria fotografica viene utilizzata per inviare foto e video.";
"NSMicrophoneUsageDescription" = "Il microfono viene utilizzato per registrare video ed effettuare chiamate.";
"NSMicrophoneUsageDescription" = "Element ha bisogno di accedere al microfono per effettuare e ricevere chiamate, registrare video e messaggi vocali.";
"NSContactsUsageDescription" = "Per scoprire i contatti che già usano Matrix, Element può inviare gli indirizzi email e i numeri di telefono della tua rubrica al server identità che hai scelto. Se supportato, viene fatto un hash dei dati personali prima dell'invio - controlla la politica sulla privacy del tuo server di identità per maggiori informazioni.";
"NSCalendarsUsageDescription" = "Vedi le tue riunioni programmate nell'app.";
"NSFaceIDUsageDescription" = "Face ID viene usato per accedere all'app.";

View file

@ -327,7 +327,6 @@
"settings_global_settings_info" = "Le impostazioni di notifica avanzate sono disponibili nel tuo %@ web client";
"settings_pin_rooms_with_missed_notif" = "Segna le stanze con notifiche perse";
"settings_pin_rooms_with_unread" = "Segna le stanze con messaggi non letti";
"settings_on_denied_notification" = "Le notifiche non sono permesse per %@, abilitale nelle impostazioni del tuo dispositivo";
"settings_enable_callkit" = "Chiamate integrate";
"settings_callkit_info" = "Ricevi le chiamate in arrivo sul blocca schermo. Mostra le chiamate Element nella cronologia di chiamate del dispositivo. Se iCloud è attivo, questa cronologia sarà condivisa con Apple.";
"settings_ui_language" = "Lingua";
@ -1395,3 +1394,10 @@
// Room Notification Settings
"room_notifs_settings_notify_me_for" = "Inviami notifiche per";
"room_details_notifs" = "Notifiche";
"voice_message_stop_locked_mode_recording" = "Tocca la registrazione per fermare o ascoltare";
"voice_message_remaining_recording_time" = "%@s rimasti";
// Mark: - Voice Messages
"voice_message_release_to_send" = "Tieni premuto per registrare, rilascia per inviare";
"settings_labs_voice_messages" = "Messaggi vocali";

View file

@ -289,7 +289,6 @@
"settings_show_decrypted_content" = "復号化された文章を表示";
"settings_global_settings_info" = "あなたの %@ webクライアント上で、全体の通知設定が可能です";
"settings_pin_rooms_with_missed_notif" = "通知の届かなかった部屋をピン止めする";
"settings_on_denied_notification" = "%@で通知されないように設定されています。あなたの端末設定で許可してください";
"settings_callkit_info" = "画面がロックされているときに着信がありました。Elementの着信はシステムの通話履歴で確認できます。 iCloudが有効になっている場合、この通話履歴はAppleと共有されます。";
"settings_ui_language" = "言語";
"settings_ui_theme" = "外観";

View file

@ -1305,7 +1305,6 @@
"security_settings_blacklist_unverified_devices" = "Ɣur-k·m ad tazneḍ akk iznan ɣer tɣimiyin ur nettwattkal ara";
"security_settings_crypto_sessions_description_2" = "MA yella ur tessineḍ ara anekcum, senfel awal-ik·imuffir, rnu wennez aḥraz aɣellsan.";
"settings_send_crash_report" = "Azen tura tura isefka yerrẓen & useqdec";
"settings_on_denied_notification" = "Ttwagin yilɣa i %@, ttxil-k·m sireg-iten deg yiɣewaren n yibenk-inek·inem";
"settings_global_settings_info" = "Iɣewwaren n yilɣa imatuyen llan ɣef umsaɣ-inek·inem web %@";
"room_participants_start_new_chat_error_using_user_email_without_identity_server" = "Ulac aqeddac n timagit i yettusbadun, ihi ur tezmireḍ ara ad tebduḍ adiwenni akked unermis isseqdacen imayl.";
"contacts_address_book_permission_required" = "Ttusrant tsirag i unekcum ɣer yinermisen idiganen";

View file

@ -857,7 +857,6 @@
"room_details_photo_for_dm" = "Bilde";
"room_details_photo" = "Rombilde";
"settings_flair" = "Vis kobling hvor tillatt";
"settings_on_denied_notification" = "Varsler er ikke tillat for %@, vennligst tillat dem i enhetens innstillinger";
"settings_pin_rooms_with_missed_notif" = "Fest rom med tapte varsler";
"room_info_list_several_members" = "%@ medlemmer";
"pin_protection_not_allowed_pin" = "Av sikkerhetsårsaker er denne PIN-koden ikke tilgjengelig. Prøv en annen PIN-kode";

View file

@ -289,7 +289,6 @@
"settings_global_settings_info" = "Globale meldingsinstellingen zijn beschikbaar op uw %@-webcliënt";
"settings_pin_rooms_with_missed_notif" = "Gesprekken met gemiste meldingen vastprikken";
"settings_pin_rooms_with_unread" = "Gesprekken met ongelezen berichten vastprikken";
"settings_on_denied_notification" = "Meldingen worden geweigerd voor %@, sta ze toe in uw apparaatinstellingen";
//"settings_enable_all_notif" = "Alle notificaties aanzetten";
//"settings_messages_my_display_name" = "Bericht dat mijn naam bevat";
//"settings_messages_my_user_name" = "Bericht dat mijn gebruikersnaam bevat";

View file

@ -503,7 +503,6 @@
"settings_key_backup" = "KOPIA ZAPASOWA KLUCZY";
"settings_enable_push_notif" = "Powiadomienia na tym urządzeniu";
"settings_global_settings_info" = "Globalne i szczegółowe ustawienia powiadomień są dostępne z poziomu klienta webowego: %@";
"settings_on_denied_notification" = "Powiadomienia dla aplikacji %@ są wyłączone. Proszę zezwól na nie w ustawieniach urządzenia";
"settings_callkit_info" = "Odbieraj połączenia przychodzące na ekranie blokady. Zobacz swoje połęczenia Element w historii połączeń w systemie. Jeśli usługa iCloud jest włączona, historia połączeń zostanie udostępniona Apple.";
"settings_ui_theme_picker_message" = "\"Auto\" używa ustawienia \"Odwróć kolory\" Twojego urządzenia";
"close" = "Zamknij";

View file

@ -1,7 +1,7 @@
// Permissions usage explanations
"NSCameraUsageDescription" = "A câmera é usada para tirar fotos e vídeos, fazer chamadas de vídeo.";
"NSPhotoLibraryUsageDescription" = "A biblioteca de fotos é usada para enviar fotos e vídeos.";
"NSMicrophoneUsageDescription" = "O microfone é usado para tirar vídeos, fazer chamadas.";
"NSMicrophoneUsageDescription" = "Element precisa acessar seu microfone para fazer e receber chamadas, tirar vídeos, e gravar mensagens de voz.";
"NSContactsUsageDescription" = "Para descobrir contatos já usando Matrix, Element pode enviar endereços de email e números de telefone em seu livro de endereços para seu servidor de identidade Matrix escolhido. Onde suportado, dados pessoais são hashados antes do envio - por favor cheque a política de privacidade de seu servidor de identidade para mais detalhes.";
"NSCalendarsUsageDescription" = "Ver suas reuniões agendadas no app.";
"NSFaceIDUsageDescription" = "Face ID é usada para acessar seu app.";

View file

@ -317,7 +317,6 @@
"settings_enable_push_notif" = "Notificações neste dispositivo";
"settings_show_decrypted_content" = "Mostrar conteúdo decriptado";
"settings_global_settings_info" = "Configurações de notificação globais estão disponíveis em seu cliente web %@";
"settings_on_denied_notification" = "Notificações são negadas para %@, por favor permita-as nas configurações de seu dispositivo";
"settings_enable_callkit" = "Chamamento integrado";
"settings_callkit_info" = "Receba chamadas chegando em sua tela de bloqueio. Veja suas chamadas Element no histórico de chamadas do sistema. Se iCloud está ativado, este histórico de chamadas vai ser compartilhado com Apple.";
"settings_ui_language" = "Língua";
@ -1392,3 +1391,10 @@
// Room Notification Settings
"room_notifs_settings_notify_me_for" = "Notifique-me para";
"room_details_notifs" = "Notificações";
"voice_message_remaining_recording_time" = "%@s restando";
"voice_message_stop_locked_mode_recording" = "Toque em sua gravação para parar ou escutar";
// Mark: - Voice Messages
"voice_message_release_to_send" = "Segure para gravar, solte para enviar";
"settings_labs_voice_messages" = "Mensagens de voz";

View file

@ -259,7 +259,6 @@
"settings_fail_to_update_profile" = "Не удалось обновить профиль";
"settings_enable_push_notif" = "Уведомления на этом устройстве";
"settings_global_settings_info" = "Глобальные настройки уведомлений доступны в вашем %@ веб-клиенте";
"settings_on_denied_notification" = "Уведомления для %@ запрещены, пожалуйста, разрешите их в настройках вашего устройства";
"settings_ui_language" = "Язык";
"settings_unignore_user" = "Показать все сообщения от %@?";
"settings_labs_e2e_encryption" = "Сквозное шифрование";

View file

@ -493,7 +493,6 @@
"settings_sign_out_e2e_warn" = "Do të humbni kyçet tuaj të fshehtëzimit skaj-më-skaj. Kjo do të thotë se sdo të jeni më në gjendje të lexoni mesazhe të vjetër te dhoma të fshehtëzuara në këtë pajisje.";
"settings_surname" = "Mbiemër";
"settings_global_settings_info" = "Rregullimet globale për njoftime i gjeni te klienti juaj %@ web";
"settings_on_denied_notification" = "Njoftimet për %@ spranohen, ju lutemi, lejojini që nga rregullimet e pajisjes tuaj";
"settings_enable_callkit" = "Thirrje të integruara";
"settings_callkit_info" = "Merrini thirrjet ardhëse edhe me ekran të kyçur. Shihni thirrjet tuaja nën Element te historiku i thirrjeve të sistemit. Nëse iCloud është i aktivizuar, ky historik thirrjesh do ti jepet kompanisë Apple.";
"settings_ui_theme_picker_message" = "\"Auto\" përdor rregullimet \"Përmbysi Ngjyrat\" të pajisjes tuaj";

View file

@ -309,7 +309,6 @@
"settings_show_decrypted_content" = "Visa avkrypterat innehåll";
"settings_pin_rooms_with_missed_notif" = "Fäst rum med missade aviseringar";
"settings_pin_rooms_with_unread" = "Fäst rum med olästa meddelanden";
"settings_on_denied_notification" = "Aviseringar har nekats för %@, vänligen tillåt dem i dina enhetsinställningar";
"settings_enable_callkit" = "Integrerade samtal";
"settings_integrations_allow_button" = "Hantera integrationer";
"settings_ui_language" = "Språk";

View file

@ -1,7 +1,7 @@
// Permissions usage explanations
"NSCameraUsageDescription" = "Камера використовується для знімків фото і відео, а також для відео-викликів.";
"NSPhotoLibraryUsageDescription" = "Фотографії використовуються для надсилання фото і відео.";
"NSMicrophoneUsageDescription" = "Мікрофон використовується для відео і викликів.";
"NSMicrophoneUsageDescription" = "Element потребує доступу до вашого мікрофона, щоб здійснювати та отримувати виклики, знімати відео та записувати голосові повідомлення.";
"NSContactsUsageDescription" = "Щоб показати, які з ваших контактів вже використовують Matrix, Element може надіслати адреси електронної пошти і номери телефонів з вашої адресної книги до вашого ідентифікаційного сервера Matrix. При наявності підтримки, перед надсиланням створюється хеш особистих даних. Для докладних відомостей ознайомтеся з політикою приватності свого ідентифікаційного сервера.";
"NSCalendarsUsageDescription" = "Переглядайте свої заплановані зустрічі в додатку.";
"NSFaceIDUsageDescription" = "Face ID використовується для доступу до вашого додатку.";

View file

@ -290,7 +290,6 @@
"settings_global_settings_info" = "Cài đặt thông báo toàn cầu khả dụng trên %@ trình duyệt khách của bạn";
"settings_pin_rooms_with_missed_notif" = "Neo phòng có thông báo bỏ lỡ";
"settings_pin_rooms_with_unread" = "Neo phòng có tin nhắn chưa đọc";
"settings_on_denied_notification" = "Thông báo bị từ chối cho %@, vui lòng cho phép trong cài đặt thiết bị của bạn";
"settings_enable_callkit" = "Cuộc gọi tích hợp";
"settings_callkit_info" = "Nhận cuộc gọi tới trên màn hình khóa. Xem lịch sử cuộc gọi trong lịch sử cuộc gọi của hệ thống. Nếu iCloud được kích hoạt, lịch sử cuộc gọi sẽ được chia sẻ với Apple.";
"settings_ui_language" = "Ngôn ngữ";

View file

@ -1,7 +1,7 @@
// Permissions usage explanations
"NSCameraUsageDescription" = "摄像头权限用于拍摄照片、录制视频或进行视频聊天。";
"NSPhotoLibraryUsageDescription" = "照片库访问权限用于发送图片与视频。";
"NSMicrophoneUsageDescription" = "麦克风权限用于录制视频或进行通话。";
"NSMicrophoneUsageDescription" = "Element 需要访问您的麦克风才能拨打和接听电话、拍摄视频和录制语音消息。";
"NSContactsUsageDescription" = "为了发现已在使用 Matrix 的联系人Element 可以把你地址簿里的邮箱地址和电话号码发送到你所选择的 Matrix 身份认证服务器。如果支持的话,个人数据在发送前会被哈希处理——请检查你的身份认证服务器的隐私政策以获取详细信息。";
"NSCalendarsUsageDescription" = "在此应用中查看你计划的会议。";
"NSFaceIDUsageDescription" = "Face ID 权限用于访问您的应用。";

View file

@ -272,7 +272,6 @@
"settings_global_settings_info" = "全局通知设置可在 %@ 的网页客户端中修改";
"settings_pin_rooms_with_missed_notif" = "置顶含有错过的通知的聊天室";
"settings_pin_rooms_with_unread" = "置顶含有未读消息的聊天室";
"settings_on_denied_notification" = "%@ 的通知请求被拒绝,请在系统设置中允许";
"settings_unignore_user" = "显示所有来自 %@ 的消息?";
"settings_contacts_discover_matrix_users" = "使用电子邮件和手机号码来发现用户";
"settings_contacts_phonebook_country" = "电话本国家";
@ -1426,3 +1425,23 @@
"settings_ui_theme_picker_message_invert_colours" = "“自动”使用您设备的“反转颜色”设置";
"room_recents_unknown_room_error_message" = "找不到这个房间。 确保它存在";
"room_creation_dm_error" = "我们无法创建您的 DM。 请检查您要邀请的用户,然后重试。";
"voice_message_stop_locked_mode_recording" = "轻按录音停止或收听";
"voice_message_remaining_recording_time" = "剩 %@s";
// Mark: - Voice Messages
"voice_message_release_to_send" = "按住录音,松开发送";
"key_verification_verify_qr_code_scan_code_other_device_action" = "用这部设备扫描";
"room_notifs_settings_encrypted_room_notice" = "请注意,移动设备上的加密聊天室不提供提及和关键字通知。";
"room_notifs_settings_account_settings" = "账户设置";
"room_notifs_settings_manage_notifications" = "你可以管理 %@ 中的消息";
"room_notifs_settings_cancel_action" = "取消";
"room_notifs_settings_done_action" = "完成";
"room_notifs_settings_none" = "无";
"room_notifs_settings_mentions_and_keywords" = "仅提及和关键词";
"room_notifs_settings_all_messages" = "所有消息";
// Room Notification Settings
"room_notifs_settings_notify_me_for" = "通知内容";
"room_details_notifs" = "通知";
"settings_labs_voice_messages" = "语音消息";

View file

@ -344,7 +344,6 @@
"room_ongoing_conference_call_with_close" = "群組通話進行中。 以 %@ 或 %@ 加入。%@ 該通話。";
"settings_pin_rooms_with_missed_notif" = "釘選含有錯過的通知的聊天室";
"settings_pin_rooms_with_unread" = "釘選含有未讀訊息的聊天室";
"settings_on_denied_notification" = "因 %@ 的通知不被允許,請在裝置設定中允許";
"settings_enable_callkit" = "整合式通話";
"settings_callkit_info" = "在鎖定畫面接聽 Element 來電、在通話紀錄中顯示 Element 通話。 如果您已啟用 iCloud ,則這些通話紀錄會與蘋果公司共享。";
"settings_ui_language" = "語言";

View file

@ -36,6 +36,8 @@ internal enum Asset {
internal static let callDialpadCallIcon = ImageAsset(name: "call_dialpad_call_icon")
internal static let callGoToChatIcon = ImageAsset(name: "call_go_to_chat_icon")
internal static let callHangupLarge = ImageAsset(name: "call_hangup_large")
internal static let callMissedVideo = ImageAsset(name: "call_missed_video")
internal static let callMissedVoice = ImageAsset(name: "call_missed_voice")
internal static let callMoreIcon = ImageAsset(name: "call_more_icon")
internal static let callPausedIcon = ImageAsset(name: "call_paused_icon")
internal static let callPausedWhiteIcon = ImageAsset(name: "call_paused_white_icon")

View file

@ -1242,6 +1242,14 @@ internal enum VectorL10n {
internal static var errorUserAlreadyLoggedIn: String {
return VectorL10n.tr("Vector", "error_user_already_logged_in")
}
/// Active video call
internal static var eventFormatterCallActiveVideo: String {
return VectorL10n.tr("Vector", "event_formatter_call_active_video")
}
/// Active voice call
internal static var eventFormatterCallActiveVoice: String {
return VectorL10n.tr("Vector", "event_formatter_call_active_voice")
}
/// Answer
internal static var eventFormatterCallAnswer: String {
return VectorL10n.tr("Vector", "event_formatter_call_answer")
@ -1266,10 +1274,26 @@ internal enum VectorL10n {
internal static var eventFormatterCallEndCall: String {
return VectorL10n.tr("Vector", "event_formatter_call_end_call")
}
/// Ended %@
/// Call ended %@
internal static func eventFormatterCallHasEnded(_ p1: String) -> String {
return VectorL10n.tr("Vector", "event_formatter_call_has_ended", p1)
}
/// Incoming video call
internal static var eventFormatterCallIncomingVideo: String {
return VectorL10n.tr("Vector", "event_formatter_call_incoming_video")
}
/// Incoming voice call
internal static var eventFormatterCallIncomingVoice: String {
return VectorL10n.tr("Vector", "event_formatter_call_incoming_voice")
}
/// Missed video call
internal static var eventFormatterCallMissedVideo: String {
return VectorL10n.tr("Vector", "event_formatter_call_missed_video")
}
/// Missed voice call
internal static var eventFormatterCallMissedVoice: String {
return VectorL10n.tr("Vector", "event_formatter_call_missed_voice")
}
/// Retry
internal static var eventFormatterCallRetry: String {
return VectorL10n.tr("Vector", "event_formatter_call_retry")
@ -1278,26 +1302,10 @@ internal enum VectorL10n {
internal static var eventFormatterCallRinging: String {
return VectorL10n.tr("Vector", "event_formatter_call_ringing")
}
/// Video call
internal static var eventFormatterCallVideo: String {
return VectorL10n.tr("Vector", "event_formatter_call_video")
}
/// Voice call
internal static var eventFormatterCallVoice: String {
return VectorL10n.tr("Vector", "event_formatter_call_voice")
}
/// Active call
internal static var eventFormatterCallYouCurrentlyIn: String {
return VectorL10n.tr("Vector", "event_formatter_call_you_currently_in")
}
/// You declined this call
/// Call declined
internal static var eventFormatterCallYouDeclined: String {
return VectorL10n.tr("Vector", "event_formatter_call_you_declined")
}
/// You missed this call
internal static var eventFormatterCallYouMissed: String {
return VectorL10n.tr("Vector", "event_formatter_call_you_missed")
}
/// Group call
internal static var eventFormatterGroupCall: String {
return VectorL10n.tr("Vector", "event_formatter_group_call")
@ -4114,6 +4122,10 @@ internal enum VectorL10n {
internal static var settingsDeactivateMyAccount: String {
return VectorL10n.tr("Vector", "settings_deactivate_my_account")
}
/// Device notifications
internal static var settingsDeviceNotifications: String {
return VectorL10n.tr("Vector", "settings_device_notifications")
}
/// SESSIONS
internal static var settingsDevices: String {
return VectorL10n.tr("Vector", "settings_devices")
@ -4390,6 +4402,14 @@ internal enum VectorL10n {
internal static var settingsNightMode: String {
return VectorL10n.tr("Vector", "settings_night_mode")
}
/// To enable notifications, go to your device settings.
internal static var settingsNotificationsDisabledAlertMessage: String {
return VectorL10n.tr("Vector", "settings_notifications_disabled_alert_message")
}
/// Notifications disabled
internal static var settingsNotificationsDisabledAlertTitle: String {
return VectorL10n.tr("Vector", "settings_notifications_disabled_alert_title")
}
/// NOTIFICATION SETTINGS
internal static var settingsNotificationsSettings: String {
return VectorL10n.tr("Vector", "settings_notifications_settings")
@ -4402,10 +4422,6 @@ internal enum VectorL10n {
internal static func settingsOlmVersion(_ p1: String) -> String {
return VectorL10n.tr("Vector", "settings_olm_version", p1)
}
/// Notifications are denied for %@, please allow them in your device settings
internal static func settingsOnDeniedNotification(_ p1: String) -> String {
return VectorL10n.tr("Vector", "settings_on_denied_notification", p1)
}
/// OTHER
internal static var settingsOther: String {
return VectorL10n.tr("Vector", "settings_other")
@ -4890,7 +4906,7 @@ internal enum VectorL10n {
internal static func voiceMessageRemainingRecordingTime(_ p1: String) -> String {
return VectorL10n.tr("Vector", "voice_message_remaining_recording_time", p1)
}
/// Tap on the wavelength to stop and playback
/// Tap on your recording to stop or listen
internal static var voiceMessageStopLockedModeRecording: String {
return VectorL10n.tr("Vector", "voice_message_stop_locked_mode_recording")
}

View file

@ -1854,16 +1854,6 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
[account addObserver:self forKeyPath:@"enableInAppNotifications" options:0 context:nil];
}
// Load the local contacts on first account creation.
if ([MXKAccountManager sharedManager].accounts.count == 1)
{
dispatch_async(dispatch_get_main_queue(), ^{
[self refreshLocalContacts];
});
}
[self.delegate legacyAppDelegate:self didAddAccount:account];
}];
@ -1976,14 +1966,6 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
// during this blocking task.
dispatch_after(dispatch_walltime(DISPATCH_TIME_NOW, 0.3 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[[MXKContactManager sharedManager] addMatrixSession:mxSession];
// Load the local contacts on first account
if ([MXKAccountManager sharedManager].accounts.count == 1)
{
dispatch_async(dispatch_get_main_queue(), ^{
[self refreshLocalContacts];
});
}
});
// Register the session to the widgets manager
@ -2939,54 +2921,6 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
}];
}
- (void)refreshLocalContacts
{
if (!BuildSettings.allowLocalContactsAccess)
{
return;
}
// Do not scan local contacts in background if the user has not decided yet about using
// an identity server
BOOL doRefreshLocalContacts = NO;
for (MXSession *session in mxSessionArray)
{
if (session.hasAccountDataIdentityServerValue)
{
doRefreshLocalContacts = YES;
break;
}
}
// Check whether the application is allowed to access the local contacts.
if (doRefreshLocalContacts
&& [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts] == CNAuthorizationStatusAuthorized)
{
// Check the user permission for syncing local contacts. This permission was handled independently on previous application version.
if (![MXKAppSettings standardAppSettings].syncLocalContacts)
{
// Check whether it was not requested yet.
if (![MXKAppSettings standardAppSettings].syncLocalContactsPermissionRequested)
{
[MXKAppSettings standardAppSettings].syncLocalContactsPermissionRequested = YES;
[MXKContactManager requestUserConfirmationForLocalContactsSyncInViewController:self.presentedViewController completionHandler:^(BOOL granted) {
if (granted)
{
// Allow local contacts sync in order to discover matrix users.
[MXKAppSettings standardAppSettings].syncLocalContacts = YES;
}
}];
}
}
// Refresh the local contacts list.
[[MXKContactManager sharedManager] refreshLocalContacts];
}
}
#pragma mark - Matrix Groups handling
- (void)showGroup:(MXGroup*)group withMatrixSession:(MXSession*)mxSession

View file

@ -354,8 +354,6 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0;
{
[_keyboardAvoider stopAvoiding];
[self.authenticationActivityIndicator removeObserver:self forKeyPath:@"hidden"];
[super viewDidDisappear:animated];
}
@ -384,6 +382,8 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0;
[[NSNotificationCenter defaultCenter] removeObserver:universalLinkDidChangeNotificationObserver];
universalLinkDidChangeNotificationObserver = nil;
}
[self.authenticationActivityIndicator removeObserver:self forKeyPath:@"hidden"];
autoDiscovery = nil;
_keyVerificationCoordinatorBridgePresenter = nil;

View file

@ -71,17 +71,9 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou
processingQueue = dispatch_queue_create("RecentsDataSource", DISPATCH_QUEUE_SERIAL);
_crossSigningBannerDisplay = CrossSigningBannerDisplayNone;
crossSigningBannerSection = -1;
_secureBackupBannerDisplay = SecureBackupBannerDisplayNone;
secureBackupBannerSection = -1;
directorySection = -1;
invitesSection = -1;
favoritesSection = -1;
peopleSection = -1;
conversationSection = -1;
lowPrioritySection = -1;
serverNoticeSection = -1;
[self resetSectionIndexes];
_areSectionsShrinkable = NO;
shrinkedSectionsBitMask = 0;
@ -96,6 +88,19 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou
return self;
}
- (void)resetSectionIndexes
{
crossSigningBannerSection = -1;
secureBackupBannerSection = -1;
directorySection = -1;
invitesSection = -1;
favoritesSection = -1;
peopleSection = -1;
conversationSection = -1;
lowPrioritySection = -1;
serverNoticeSection = -1;
}
#pragma mark - Properties
@ -445,7 +450,7 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou
// Check whether all data sources are ready before rendering recents
if (self.state == MXKDataSourceStateReady)
{
crossSigningBannerSection = secureBackupBannerSection = directorySection = favoritesSection = peopleSection = conversationSection = lowPrioritySection = invitesSection = serverNoticeSection = -1;
[self resetSectionIndexes];
if (self.crossSigningBannerDisplay != CrossSigningBannerDisplayNone)
{
@ -1119,10 +1124,8 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou
#pragma mark - MXKDataSourceDelegate
- (void)refreshRoomsSection:(void (^)(void))onComplete;
- (void)refreshRoomsSection:(void (^)(void))onComplete
{
secureBackupBannerSection = directorySection = favoritesSection = peopleSection = conversationSection = lowPrioritySection = serverNoticeSection = invitesSection = -1;
if (displayedRecentsDataSourceArray.count > 0)
{
// FIXME manage multi accounts

View file

@ -174,6 +174,15 @@
[self refreshContactsTable];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
// Load the local contacts for display.
// In viewDidAppear as it may trigger a request for contacts access.
[self refreshLocalContacts];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
@ -212,6 +221,54 @@
}
}
- (void)refreshLocalContacts
{
if (!BuildSettings.allowLocalContactsAccess)
{
return;
}
// Do not scan local contacts in background if the user has not decided yet about using
// an identity server
BOOL doRefreshLocalContacts = NO;
for (MXSession *session in self.mxSessions)
{
if (session.hasAccountDataIdentityServerValue)
{
doRefreshLocalContacts = YES;
break;
}
}
// Check whether the application is allowed to access the local contacts.
if (doRefreshLocalContacts
&& [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts] == CNAuthorizationStatusAuthorized)
{
// Check the user permission for syncing local contacts. This permission was handled independently on previous application version.
if (![MXKAppSettings standardAppSettings].syncLocalContacts)
{
// Check whether it was not requested yet.
if (![MXKAppSettings standardAppSettings].syncLocalContactsPermissionRequested)
{
[MXKAppSettings standardAppSettings].syncLocalContactsPermissionRequested = YES;
[MXKContactManager requestUserConfirmationForLocalContactsSyncInViewController:self completionHandler:^(BOOL granted) {
if (granted)
{
// Allow local contacts sync in order to discover matrix users.
[MXKAppSettings standardAppSettings].syncLocalContacts = YES;
}
}];
}
}
// Refresh the local contacts list.
[[MXKContactManager sharedManager] refreshLocalContacts];
}
}
- (void)refreshContactsTable
{
[self.contactsTableView reloadData];

View file

@ -20,7 +20,7 @@
/**
'PeopleViewController' instance is used to display/filter the direct rooms and a list of contacts.
*/
@interface PeopleViewController : RecentsViewController <UITableViewDataSource, MXKDataSourceDelegate>
@interface PeopleViewController : RecentsViewController
+ (instancetype)instantiate;

View file

@ -14,7 +14,6 @@
limitations under the License.
*/
#import <Contacts/Contacts.h>
#import "PeopleViewController.h"
#import "UIViewController+RiotSearch.h"
@ -25,17 +24,11 @@
#import "RecentTableViewCell.h"
#import "InviteRecentTableViewCell.h"
#import "ContactTableViewCell.h"
#import "Riot-Swift.h"
@interface PeopleViewController ()
{
NSInteger directRoomsSectionNumber;
ContactsDataSource *contactsDataSource;
NSInteger contactsSectionNumber;
RecentsDataSource *recentsDataSource;
}
@ -55,7 +48,6 @@
[super finalizeInit];
directRoomsSectionNumber = 0;
contactsSectionNumber = 0;
self.screenName = @"People";
}
@ -76,14 +68,6 @@
plusButtonImageView = [self vc_addFABWithImage:[UIImage imageNamed:@"people_floating_action"]
target:self
action:@selector(onPlusButtonPressed)];
// Register table view cell for contacts.
[self.recentsTableView registerClass:ContactTableViewCell.class forCellReuseIdentifier:ContactTableViewCell.defaultReuseIdentifier];
// Change the table data source. It must be the people view controller itself.
self.recentsTableView.dataSource = self;
self.enableStickyHeaders = YES;
}
- (void)didReceiveMemoryWarning
@ -92,302 +76,36 @@
// Dispose of any resources that can be recreated.
}
- (void)destroy
{
contactsDataSource.delegate = nil;
[contactsDataSource destroy];
contactsDataSource = nil;
[super destroy];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (BuildSettings.allowLocalContactsAccess)
{
// Check whether the access to the local contacts has not been already asked
// and check that the user has decided to use or not to use an identity server
if ([CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts] == CNAuthorizationStatusNotDetermined
|| !contactsDataSource.mxSession.hasAccountDataIdentityServerValue)
{
// Allow by default the local contacts sync in order to discover matrix users.
// This setting change will trigger the loading of the local contacts, which will automatically
// ask user permission to access their local contacts.
[MXKAppSettings standardAppSettings].syncLocalContacts = YES;
}
// Refresh the local contacts list.
[[MXKContactManager sharedManager] refreshLocalContacts];
}
[AppDelegate theDelegate].masterTabBarController.navigationItem.title = NSLocalizedStringFromTable(@"title_people", @"Vector", nil);
[AppDelegate theDelegate].masterTabBarController.tabBar.tintColor = ThemeService.shared.theme.tintColor;
if (recentsDataSource)
if ([self.dataSource isKindOfClass:RecentsDataSource.class])
{
// Take the lead on the shared data source.
recentsDataSource = (RecentsDataSource*)self.dataSource;
recentsDataSource.areSectionsShrinkable = NO;
[recentsDataSource setDelegate:self andRecentsDataSourceMode:RecentsDataSourceModePeople];
}
}
#pragma mark -
- (void)displayList:(MXKRecentsDataSource *)listDataSource
{
[super displayList:listDataSource];
// Change the table data source. It must be the people view controller itself.
self.recentsTableView.dataSource = self;
// Keep a ref on the recents data source
if ([listDataSource isKindOfClass:RecentsDataSource.class])
{
recentsDataSource = (RecentsDataSource*)listDataSource;
}
if (BuildSettings.allowLocalContactsAccess)
{
if (!contactsDataSource)
{
// Prepare its contacts data source
contactsDataSource = [[ContactsDataSource alloc] initWithMatrixSession:listDataSource.mxSession];
contactsDataSource.contactCellAccessoryImage = [[UIImage imageNamed: @"disclosure_icon"] vc_tintedImageUsingColor:ThemeService.shared.theme.textSecondaryColor];
contactsDataSource.delegate = self;
}
}
}
#pragma mark - MXKDataSourceDelegate
- (Class<MXKCellRendering>)cellViewClassForCellData:(MXKCellData*)cellData
{
if ([cellData isKindOfClass:MXKContact.class])
{
return ContactTableViewCell.class;
}
return [super cellViewClassForCellData:cellData];
}
#pragma mark - UITableView data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Retrieve the current number of sections related to the direct rooms.
// Sanity check: check whether the recents data source is correctly configured.
directRoomsSectionNumber = 0;
if (recentsDataSource.recentsDataSourceMode == RecentsDataSourceModePeople)
{
directRoomsSectionNumber = [self.dataSource numberOfSectionsInTableView:self.recentsTableView];
}
// Retrieve the current number of sections related to the contacts
contactsSectionNumber = [contactsDataSource numberOfSectionsInTableView:self.recentsTableView];
return (directRoomsSectionNumber + contactsSectionNumber);
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSInteger count = 0;
if (section < directRoomsSectionNumber)
{
count = [self.dataSource tableView:tableView numberOfRowsInSection:section];
}
else
{
section -= directRoomsSectionNumber;
if (section < contactsSectionNumber)
{
count = [contactsDataSource tableView:tableView numberOfRowsInSection:section];
}
}
return count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSInteger section = indexPath.section;
if (section < directRoomsSectionNumber)
{
return [self.dataSource tableView:tableView cellForRowAtIndexPath:indexPath];
}
else
{
section -= directRoomsSectionNumber;
if (section < contactsSectionNumber)
{
return [contactsDataSource tableView:tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:indexPath.row inSection:section]];
}
}
// Return a fake cell to prevent app from crashing.
return [[UITableViewCell alloc] init];
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
NSInteger section = indexPath.section;
if (section < directRoomsSectionNumber)
{
return [self.dataSource tableView:tableView canEditRowAtIndexPath:indexPath];
}
else
{
section -= directRoomsSectionNumber;
if (section < contactsSectionNumber)
{
return [contactsDataSource tableView:tableView canEditRowAtIndexPath:[NSIndexPath indexPathForRow:indexPath.row inSection:section]];
}
}
return NO;
}
#pragma mark - UITableView delegate
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
if (section >= directRoomsSectionNumber)
{
// Let the contact dataSource provide the height of the section header.
section -= directRoomsSectionNumber;
if (section < contactsSectionNumber)
{
return [contactsDataSource heightForHeaderInSection:section];
}
else
{
return 0.0;
}
}
return [super tableView:tableView heightForHeaderInSection:section];
return 0.0;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
if (section >= directRoomsSectionNumber)
{
// Let the contact dataSource provide the section header.
CGRect frame = [tableView rectForHeaderInSection:section];
section -= directRoomsSectionNumber;
if (section < contactsSectionNumber)
{
UIView *sectionHeader = [contactsDataSource viewForHeaderInSection:section withFrame:frame];
sectionHeader.tag = section + directRoomsSectionNumber;
if (self.enableStickyHeaders)
{
while (sectionHeader.gestureRecognizers.count)
{
UIGestureRecognizer *gestureRecognizer = sectionHeader.gestureRecognizers.lastObject;
[sectionHeader removeGestureRecognizer:gestureRecognizer];
}
// Handle tap gesture
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTapOnSectionHeader:)];
[tap setNumberOfTouchesRequired:1];
[tap setNumberOfTapsRequired:1];
[sectionHeader addGestureRecognizer:tap];
}
return sectionHeader;
}
else
{
return nil;
}
}
return [super tableView:tableView viewForHeaderInSection:section];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSInteger section = indexPath.section;
if (section >= directRoomsSectionNumber)
{
section -= directRoomsSectionNumber;
if (section < contactsSectionNumber)
{
if ([contactsDataSource contactAtIndexPath:[NSIndexPath indexPathForRow:indexPath.row inSection:section]])
{
// Return the default height of the contact cell
return 74.0;
}
return 50;
}
else
{
return 0.0;
}
}
return [super tableView:tableView heightForRowAtIndexPath:indexPath];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSInteger section = indexPath.section;
if (section >= directRoomsSectionNumber)
{
section -= directRoomsSectionNumber;
if (section < contactsSectionNumber)
{
MXKContact *mxkContact = [contactsDataSource contactAtIndexPath:[NSIndexPath indexPathForRow:indexPath.row inSection:section]];
if (mxkContact)
{
[[AppDelegate theDelegate].masterTabBarController selectContact:mxkContact];
// Keep selected the cell by default.
return;
}
}
else
{
[tableView deselectRowAtIndexPath:indexPath animated:NO];
return;
}
}
return [super tableView:tableView didSelectRowAtIndexPath:indexPath];
return nil;
}
#pragma mark - Override RecentsViewController
- (UIView *)tableView:(UITableView *)tableView viewForStickyHeaderInSection:(NSInteger)section
{
CGRect frame = [tableView rectForHeaderInSection:section];
frame.size.height = self.stickyHeaderHeight;
if (section >= directRoomsSectionNumber)
{
// Let the contact dataSource provide this header.
section -= directRoomsSectionNumber;
if (section < contactsSectionNumber)
{
return [contactsDataSource viewForStickyHeaderInSection:section withFrame:frame];
}
}
else if (recentsDataSource)
{
return [recentsDataSource viewForStickyHeaderInSection:section withFrame:frame];
}
return nil;
}
- (void)refreshCurrentSelectedCell:(BOOL)forceVisible
{
// Check whether the recents data source is correctly configured.
@ -396,41 +114,7 @@
return;
}
// Update here the index of the current selected cell (if any) - Useful in landscape mode with split view controller.
NSIndexPath *currentSelectedCellIndexPath = nil;
MasterTabBarController *masterTabBarController = [AppDelegate theDelegate].masterTabBarController;
if (masterTabBarController.currentContactDetailViewController)
{
// Look for the rank of this selected contact
currentSelectedCellIndexPath = [contactsDataSource cellIndexPathWithContact:masterTabBarController.selectedContact];
if (currentSelectedCellIndexPath)
{
// Select the right row
currentSelectedCellIndexPath = [NSIndexPath indexPathForRow:currentSelectedCellIndexPath.row inSection:(directRoomsSectionNumber + currentSelectedCellIndexPath.section)];
[self.recentsTableView selectRowAtIndexPath:currentSelectedCellIndexPath animated:YES scrollPosition:UITableViewScrollPositionNone];
if (forceVisible)
{
// Scroll table view to make the selected row appear at second position
NSInteger topCellIndexPathRow = currentSelectedCellIndexPath.row ? currentSelectedCellIndexPath.row - 1: currentSelectedCellIndexPath.row;
NSIndexPath* indexPath = [NSIndexPath indexPathForRow:topCellIndexPathRow inSection:currentSelectedCellIndexPath.section];
[self.recentsTableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:NO];
}
}
else
{
NSIndexPath *indexPath = [self.recentsTableView indexPathForSelectedRow];
if (indexPath)
{
[self.recentsTableView deselectRowAtIndexPath:indexPath animated:NO];
}
}
}
else
{
[super refreshCurrentSelectedCell:forceVisible];
}
[super refreshCurrentSelectedCell:forceVisible];
}
- (void)onPlusButtonPressed
@ -449,24 +133,6 @@
}
}
#pragma mark - UISearchBarDelegate
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
// Apply filter on contact source
[contactsDataSource searchWithPattern:searchText forceReset:NO];
[super searchBar:searchBar textDidChange:searchText];
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar
{
// Reset filtering
[contactsDataSource searchWithPattern:nil forceReset:NO];
[super searchBarCancelButtonClicked:searchBar];
}
#pragma mark - Empty view management
- (void)updateEmptyView
@ -503,35 +169,7 @@
- (NSUInteger)totalItemCounts
{
return recentsDataSource.invitesCellDataArray.count
+ recentsDataSource.conversationCellDataArray.count
+ recentsDataSource.peopleCellDataArray.count
+ [self numberOfContactsInContactsDataSource];
}
- (NSUInteger)numberOfContactsInContactsDataSource
{
BOOL areLocalContactsAccessAuthorized = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts] == CNAuthorizationStatusAuthorized;
NSInteger nbOfItemsInContactDataSource = 0;
for (NSInteger i = 0; i < contactsSectionNumber; i++)
{
nbOfItemsInContactDataSource += [contactsDataSource tableView:self.recentsTableView numberOfRowsInSection:i];
}
NSInteger numberOfContactsInContactsDataSource;
// No local contacts to show and no search in directory
if (!areLocalContactsAccessAuthorized && contactsSectionNumber == 1 && nbOfItemsInContactDataSource <= 1)
{
numberOfContactsInContactsDataSource = 0;
}
else
{
numberOfContactsInContactsDataSource = nbOfItemsInContactDataSource;
}
return numberOfContactsInContactsDataSource;
+ recentsDataSource.conversationCellDataArray.count;
}
@end

View file

@ -264,11 +264,17 @@ fileprivate extension MXRoom {
}
var overridePushRule: MXPushRule? {
getRoomRule(from: mxSession.notificationCenter.rules.global.override)
guard let overrideRules = mxSession.notificationCenter.rules.global.override else {
return nil
}
return getRoomRule(from: overrideRules)
}
var roomPushRule: MXPushRule? {
getRoomRule(from: mxSession.notificationCenter.rules.global.room)
guard let roomRules = mxSession.notificationCenter.rules.global.room else {
return nil
}
return getRoomRule(from: roomRules)
}
var notificationState: RoomNotificationState {

View file

@ -1126,7 +1126,11 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
{
[super setRoomInputToolbarViewClass:roomInputToolbarViewClass];
[(RoomInputToolbarView *)self.inputToolbarView setVoiceMessageToolbarView:self.voiceMessageController.voiceMessageToolbarView];
// The voice message toolbar cannot be set on DisabledInputToolbarView.
if ([self.inputToolbarView isKindOfClass:RoomInputToolbarView.class])
{
[(RoomInputToolbarView *)self.inputToolbarView setVoiceMessageToolbarView:self.voiceMessageController.voiceMessageToolbarView];
}
[self updateInputToolBarViewHeight];
}

View file

@ -32,8 +32,6 @@ class CallBubbleCellBaseContentView: UIView {
@IBOutlet weak var avatarImageView: MXKImageView!
@IBOutlet weak var callerNameLabel: UILabel!
@IBOutlet weak var callIconView: UIImageView!
@IBOutlet weak var callTypeLabel: UILabel!
@IBOutlet weak var dotLabel: UILabel!
@IBOutlet private weak var callStatusLabel: UILabel!
@IBOutlet private weak var callSummaryHeightConstraint: NSLayoutConstraint!
@ -51,7 +49,6 @@ class CallBubbleCellBaseContentView: UIView {
var statusText: String? {
didSet {
dotLabel.isHidden = statusText == nil
callStatusLabel.text = statusText
}
}
@ -105,9 +102,7 @@ extension CallBubbleCellBaseContentView: Themable {
bgView.backgroundColor = theme.colors.tile
callerNameLabel.textColor = theme.textPrimaryColor
callIconView.tintColor = theme.textTertiaryColor
callTypeLabel.textColor = theme.textSecondaryColor
dotLabel.textColor = theme.textSecondaryColor
callIconView.tintColor = theme.textSecondaryColor
callStatusLabel.textColor = theme.textSecondaryColor
if let bottomContainerView = bottomContainerView as? Themable {

View file

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="17701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@ -78,7 +78,7 @@
<nil key="highlightedColor"/>
</label>
<stackView opaque="NO" contentMode="scaleToFill" alignment="center" spacing="6" translatesAutoresizingMaskIntoConstraints="NO" id="kdm-vj-rsY">
<rect key="frame" x="92" y="98" width="160.5" height="20"/>
<rect key="frame" x="130" y="98" width="84" height="20"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="voice_call_hangon_icon" translatesAutoresizingMaskIntoConstraints="NO" id="iQL-Bn-D9b">
<rect key="frame" x="0.0" y="2" width="16" height="16"/>
@ -87,20 +87,8 @@
<constraint firstAttribute="height" constant="16" id="OdD-1h-kkV"/>
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Voice call" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="alq-3S-yHg">
<rect key="frame" x="22" y="2" width="58" height="16"/>
<fontDescription key="fontDescription" type="system" pointSize="13"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="•" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="6xJ-gU-ls1">
<rect key="frame" x="86" y="2" width="6.5" height="16"/>
<fontDescription key="fontDescription" type="system" pointSize="13"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Active call" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="A3h-4o-nXF">
<rect key="frame" x="98.5" y="2" width="62" height="16"/>
<rect key="frame" x="22" y="2" width="62" height="16"/>
<fontDescription key="fontDescription" type="system" pointSize="13"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
@ -197,9 +185,7 @@
<outlet property="callIconView" destination="iQL-Bn-D9b" id="Cih-PG-EM2"/>
<outlet property="callStatusLabel" destination="A3h-4o-nXF" id="9Zf-aC-DAq"/>
<outlet property="callSummaryHeightConstraint" destination="xI1-oH-QU6" id="EnC-Bt-TkY"/>
<outlet property="callTypeLabel" destination="alq-3S-yHg" id="lgA-Dw-PH2"/>
<outlet property="callerNameLabel" destination="ONW-WU-t2g" id="0Vj-Xn-CLD"/>
<outlet property="dotLabel" destination="6xJ-gU-ls1" id="8ln-dF-l7V"/>
<outlet property="paginationLabel" destination="GQH-kh-LXA" id="Mww-Ps-yv1"/>
<outlet property="paginationSeparatorView" destination="YdR-PH-eGM" id="aK7-LF-awm"/>
<outlet property="paginationTitleView" destination="H6S-AE-DTm" id="yep-a0-QYq"/>

View file

@ -57,6 +57,7 @@ class RoomDirectCallStatusBubbleCell: RoomBaseCallBubbleCell {
private var viewState: ViewState = .unknown {
didSet {
updateBottomContentView()
updateCallIcon()
}
}
@ -78,11 +79,24 @@ class RoomDirectCallStatusBubbleCell: RoomBaseCallBubbleCell {
return formatter
}
private func updateCallIcon() {
switch viewState {
case .missed:
innerContentView.callIconView.image = isVideoCall ?
Asset.Images.callMissedVideo.image :
Asset.Images.callMissedVoice.image
default:
innerContentView.callIconView.image = isVideoCall ?
Asset.Images.callVideoIcon.image :
Asset.Images.voiceCallHangonIcon.image
}
}
private func updateBottomContentView() {
bottomContentView = bottomView(for: viewState)
}
private var callTypeIcon: UIImage {
private var callButtonIcon: UIImage {
if isVideoCall {
return Asset.Images.callVideoIcon.image
} else {
@ -112,7 +126,7 @@ class RoomDirectCallStatusBubbleCell: RoomBaseCallBubbleCell {
view.secondButton.style = .positive
view.secondButton.setTitle(VectorL10n.eventFormatterCallAnswer, for: .normal)
view.secondButton.setImage(callTypeIcon, for: .normal)
view.secondButton.setImage(callButtonIcon, for: .normal)
view.secondButton.removeTarget(nil, action: nil, for: .touchUpInside)
view.secondButton.addTarget(self, action: #selector(answerCallAction(_:)), for: .touchUpInside)
@ -134,7 +148,7 @@ class RoomDirectCallStatusBubbleCell: RoomBaseCallBubbleCell {
view.firstButton.style = .positive
view.firstButton.setTitle(VectorL10n.eventFormatterCallBack, for: .normal)
view.firstButton.setImage(callTypeIcon, for: .normal)
view.firstButton.setImage(callButtonIcon, for: .normal)
view.firstButton.removeTarget(nil, action: nil, for: .touchUpInside)
view.firstButton.addTarget(self, action: #selector(callBackAction(_:)), for: .touchUpInside)
@ -145,7 +159,7 @@ class RoomDirectCallStatusBubbleCell: RoomBaseCallBubbleCell {
view.firstButton.style = .positive
view.firstButton.setTitle(VectorL10n.eventFormatterCallBack, for: .normal)
view.firstButton.setImage(callTypeIcon, for: .normal)
view.firstButton.setImage(callButtonIcon, for: .normal)
view.firstButton.removeTarget(nil, action: nil, for: .touchUpInside)
view.firstButton.addTarget(self, action: #selector(callBackAction(_:)), for: .touchUpInside)
@ -158,7 +172,7 @@ class RoomDirectCallStatusBubbleCell: RoomBaseCallBubbleCell {
view.firstButton.style = .positive
view.firstButton.setTitle(VectorL10n.eventFormatterCallRetry, for: .normal)
view.firstButton.setImage(callTypeIcon, for: .normal)
view.firstButton.setImage(callButtonIcon, for: .normal)
view.firstButton.removeTarget(nil, action: nil, for: .touchUpInside)
view.firstButton.addTarget(self, action: #selector(callBackAction(_:)), for: .touchUpInside)
@ -174,13 +188,13 @@ class RoomDirectCallStatusBubbleCell: RoomBaseCallBubbleCell {
.connecting:
viewState = .active
if call.isIncoming {
statusText = VectorL10n.eventFormatterCallYouCurrentlyIn
statusText = isVideoCall ? VectorL10n.eventFormatterCallActiveVideo : VectorL10n.eventFormatterCallActiveVoice
} else {
statusText = VectorL10n.eventFormatterCallConnecting
}
case .inviteSent:
if call.isIncoming {
statusText = VectorL10n.eventFormatterCallYouCurrentlyIn
statusText = isVideoCall ? VectorL10n.eventFormatterCallActiveVideo : VectorL10n.eventFormatterCallActiveVoice
} else {
statusText = VectorL10n.eventFormatterCallRinging
}
@ -189,14 +203,14 @@ class RoomDirectCallStatusBubbleCell: RoomBaseCallBubbleCell {
.onHold,
.remotelyOnHold:
viewState = .active
statusText = VectorL10n.eventFormatterCallYouCurrentlyIn
statusText = isVideoCall ? VectorL10n.eventFormatterCallActiveVideo : VectorL10n.eventFormatterCallActiveVoice
case .ringing:
if call.isIncoming {
viewState = .ringing
statusText = nil
statusText = isVideoCall ? VectorL10n.eventFormatterCallIncomingVideo : VectorL10n.eventFormatterCallIncomingVoice
} else {
viewState = .active
statusText = VectorL10n.eventFormatterCallYouCurrentlyIn
statusText = isVideoCall ? VectorL10n.eventFormatterCallActiveVideo : VectorL10n.eventFormatterCallActiveVoice
}
case .ended:
switch call.endReason {
@ -210,7 +224,7 @@ class RoomDirectCallStatusBubbleCell: RoomBaseCallBubbleCell {
case .missed:
if call.isIncoming {
viewState = .missed
statusText = VectorL10n.eventFormatterCallYouMissed
statusText = isVideoCall ? VectorL10n.eventFormatterCallMissedVideo : VectorL10n.eventFormatterCallMissedVoice
} else {
statusText = VectorL10n.eventFormatterCallHasEnded(callDurationString)
}
@ -272,7 +286,7 @@ class RoomDirectCallStatusBubbleCell: RoomBaseCallBubbleCell {
if isIncoming {
// missed call
viewState = .missed
statusText = VectorL10n.eventFormatterCallYouMissed
statusText = isVideoCall ? VectorL10n.eventFormatterCallMissedVideo : VectorL10n.eventFormatterCallMissedVoice
} else {
// outgoing unanswered call
viewState = .ended
@ -366,11 +380,7 @@ class RoomDirectCallStatusBubbleCell: RoomBaseCallBubbleCell {
callDurationString = readableCallDuration(from: events)
isIncoming = inviteEvent.sender != bubbleCellData.mxSession.myUserId
callInviteEvent = inviteEvent
innerContentView.callIconView.image = self.callTypeIcon
innerContentView.callTypeLabel.text = isVideoCall ?
VectorL10n.eventFormatterCallVideo :
VectorL10n.eventFormatterCallVoice
updateCallIcon()
let callId = callInviteEventContent.callId
guard let call = bubbleCellData.mxSession.callManager.call(withCallId: callId) else {

View file

@ -224,7 +224,6 @@ class RoomGroupCallStatusBubbleCell: RoomBaseCallBubbleCell {
self.widgetEvent = widgetEvent
self.widgetId = widgetId
innerContentView.callIconView.image = Asset.Images.callVideoIcon.image
innerContentView.callTypeLabel.text = VectorL10n.eventFormatterCallVideo
if isIncoming && !isJoined &&
TimeInterval(widgetEvent.age)/MSEC_PER_SEC < Constants.secondsToDisplayAnswerDeclineOptions {
@ -285,7 +284,7 @@ class RoomGroupCallStatusBubbleCell: RoomBaseCallBubbleCell {
if widget.isActive {
if !self.isIncoming {
self.viewState = .active
self.statusText = VectorL10n.eventFormatterCallYouCurrentlyIn
self.statusText = VectorL10n.eventFormatterCallActiveVideo
} else if !self.isJoined &&
TimeInterval(widgetEvent.age)/MSEC_PER_SEC < Constants.secondsToDisplayAnswerDeclineOptions {
@ -298,7 +297,7 @@ class RoomGroupCallStatusBubbleCell: RoomBaseCallBubbleCell {
}
} else {
self.viewState = .active
self.statusText = VectorL10n.eventFormatterCallYouCurrentlyIn
self.statusText = VectorL10n.eventFormatterCallActiveVideo
}
} else {
self.viewState = .ended

View file

@ -84,6 +84,7 @@ class VoiceMessageAttachmentCacheManager {
workQueue.async {
// Run this in the work queue to preserve order
if let finalURL = self.finalURLs[identifier], let duration = self.durations[identifier], let samples = self.samples[identifier]?[numberOfSamples] {
MXLog.debug("[VoiceMessageAttachmentCacheManager] Finished task - using cached results")
let result = VoiceMessageAttachmentCacheManagerLoadResult(eventIdentifier: identifier, url: finalURL, duration: duration, samples: samples)
DispatchQueue.main.async {
completion(Result.success(result))
@ -109,22 +110,93 @@ class VoiceMessageAttachmentCacheManager {
completionCallbacks[callbackKey] = [CompletionWrapper(completion)]
}
let dispatchGroup = DispatchGroup()
if let finalURL = finalURLs[identifier], let duration = durations[identifier] {
sampleFileAtURL(finalURL, duration: duration, numberOfSamples: numberOfSamples, identifier: identifier)
return
}
func sampleFileAtURL(_ url: URL, duration: TimeInterval) {
let analyser = WaveformAnalyzer(audioAssetURL: url)
dispatchGroup.enter()
analyser?.samples(count: numberOfSamples, completionHandler: { samples in
MXLog.debug("[VoiceMessageAttachmentCacheManager] Finished sampling voice message")
dispatchGroup.leave()
DispatchQueue.main.async { // These don't behave accordingly if called from a background thread
if attachment.isEncrypted {
attachment.decrypt(toTempFile: { filePath in
self.workQueue.async {
self.convertFileAtPath(filePath, numberOfSamples: numberOfSamples, identifier: identifier)
}
}, failure: { error in
// A nil error in this case is a cancellation on the MXMediaLoader
if let error = error {
MXLog.error("Failed decrypting attachment with error: \(String(describing: error))")
self.invokeFailureCallbacksForIdentifier(identifier, error: VoiceMessageAttachmentCacheManagerError.decryptionError(error))
}
})
} else {
attachment.prepare({
self.workQueue.async {
self.convertFileAtPath(attachment.cacheFilePath, numberOfSamples: numberOfSamples, identifier: identifier)
}
}, failure: { error in
// A nil error in this case is a cancellation on the MXMediaLoader
if let error = error {
MXLog.error("Failed preparing attachment with error: \(String(describing: error))")
self.invokeFailureCallbacksForIdentifier(identifier, error: VoiceMessageAttachmentCacheManagerError.preparationError(error))
}
})
}
}
}
private func convertFileAtPath(_ path: String?, numberOfSamples: Int, identifier: String) {
guard let filePath = path else {
return
}
let temporaryDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
let newURL = temporaryDirectoryURL.appendingPathComponent(ProcessInfo().globallyUniqueString).appendingPathExtension("m4a")
VoiceMessageAudioConverter.convertToMPEG4AAC(sourceURL: URL(fileURLWithPath: filePath), destinationURL: newURL) { result in
MXLog.debug("[VoiceMessageAttachmentCacheManager] Finished converting voice message")
self.workQueue.async {
switch result {
case .success:
self.finalURLs[identifier] = newURL
VoiceMessageAudioConverter.mediaDurationAt(newURL) { result in
self.workQueue.async {
MXLog.debug("[VoiceMessageAttachmentCacheManager] Finished retrieving media duration")
switch result {
case .success:
if let duration = try? result.get() {
self.durations[identifier] = duration
self.sampleFileAtURL(newURL, duration: duration, numberOfSamples: numberOfSamples, identifier: identifier)
} else {
MXLog.error("[VoiceMessageAttachmentCacheManager] Failed retrieving media duration")
}
case .failure(let error):
MXLog.error("[VoiceMessageAttachmentCacheManager] Failed retrieving audio duration with error: \(error)")
}
}
}
case .failure(let error):
MXLog.error("[VoiceMessageAttachmentCacheManager] Failed decoding audio message with error: \(error)")
self.invokeFailureCallbacksForIdentifier(identifier, error: VoiceMessageAttachmentCacheManagerError.conversionError(error))
}
}
}
}
private func sampleFileAtURL(_ url: URL, duration: TimeInterval, numberOfSamples: Int, identifier: String) {
let analyser = WaveformAnalyzer(audioAssetURL: url)
analyser?.samples(count: numberOfSamples, completionHandler: { samples in
self.workQueue.async {
guard let samples = samples else {
MXLog.debug("[VoiceMessageAttachmentCacheManager] Failed sampling voice message")
self.invokeFailureCallbacksForIdentifier(identifier, error: VoiceMessageAttachmentCacheManagerError.samplingError)
return
}
MXLog.debug("[VoiceMessageAttachmentCacheManager] Finished sampling voice message")
if var existingSamples = self.samples[identifier] {
existingSamples[numberOfSamples] = samples
self.samples[identifier] = existingSamples
@ -133,86 +205,8 @@ class VoiceMessageAttachmentCacheManager {
}
self.invokeSuccessCallbacksForIdentifier(identifier, url: url, duration: duration, samples: samples)
})
}
if let finalURL = finalURLs[identifier], let duration = durations[identifier] {
sampleFileAtURL(finalURL, duration: duration)
dispatchGroup.wait()
MXLog.debug("[VoiceMessageAttachmentCacheManager] Finished task")
return
}
func convertFileAtPath(_ path: String?) {
guard let filePath = path else {
return
}
let temporaryDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
let newURL = temporaryDirectoryURL.appendingPathComponent(ProcessInfo().globallyUniqueString).appendingPathExtension("m4a")
dispatchGroup.enter()
VoiceMessageAudioConverter.convertToMPEG4AAC(sourceURL: URL(fileURLWithPath: filePath), destinationURL: newURL) { result in
switch result {
case .success:
self.finalURLs[identifier] = newURL
VoiceMessageAudioConverter.mediaDurationAt(newURL) { result in
MXLog.debug("[VoiceMessageAttachmentCacheManager] Finished converting voice message")
switch result {
case .success:
if let duration = try? result.get() {
self.durations[identifier] = duration
sampleFileAtURL(newURL, duration: duration)
} else {
MXLog.error("[VoiceMessageAttachmentCacheManager] enqueueLoadAttachment: Failed to retrieve media duration")
}
case .failure(let error):
MXLog.error("[VoiceMessageAttachmentCacheManager] enqueueLoadAttachment: failed getting audio duration with: \(error)")
}
dispatchGroup.leave()
}
case .failure(let error):
self.invokeFailureCallbacksForIdentifier(identifier, error: VoiceMessageAttachmentCacheManagerError.conversionError(error))
MXLog.error("[VoiceMessageAttachmentCacheManager] enqueueLoadAttachment: failed decoding audio message with: \(error)")
dispatchGroup.leave()
}
}
}
dispatchGroup.enter()
DispatchQueue.main.async { // These don't behave accordingly if called from a background thread
if attachment.isEncrypted {
attachment.decrypt(toTempFile: { filePath in
convertFileAtPath(filePath)
dispatchGroup.leave()
}, failure: { error in
// A nil error in this case is a cancellation on the MXMediaLoader
if let error = error {
MXLog.error("Failed decrypting attachment with error: \(String(describing: error))")
self.invokeFailureCallbacksForIdentifier(identifier, error: VoiceMessageAttachmentCacheManagerError.decryptionError(error))
}
dispatchGroup.leave()
})
} else {
attachment.prepare({
convertFileAtPath(attachment.cacheFilePath)
dispatchGroup.leave()
}, failure: { error in
// A nil error in this case is a cancellation on the MXMediaLoader
if let error = error {
MXLog.error("Failed preparing attachment with error: \(String(describing: error))")
self.invokeFailureCallbacksForIdentifier(identifier, error: VoiceMessageAttachmentCacheManagerError.preparationError(error))
}
dispatchGroup.leave()
})
}
}
dispatchGroup.wait()
MXLog.debug("[VoiceMessageAttachmentCacheManager] Finished task")
})
}
private func invokeSuccessCallbacksForIdentifier(_ identifier: String, url: URL, duration: TimeInterval, samples: [Float]) {
@ -232,6 +226,8 @@ class VoiceMessageAttachmentCacheManager {
}
self.completionCallbacks[callbackKey] = nil
MXLog.debug("[VoiceMessageAttachmentCacheManager] Successfully finished task")
}
private func invokeFailureCallbacksForIdentifier(_ identifier: String, error: Error) {
@ -249,5 +245,7 @@ class VoiceMessageAttachmentCacheManager {
}
self.completionCallbacks[callbackKey] = nil
MXLog.debug("[VoiceMessageAttachmentCacheManager] Failed task with error: \(error)")
}
}

View file

@ -29,12 +29,13 @@ public class VoiceMessageController: NSObject, VoiceMessageToolbarViewDelegate,
static let maximumAudioRecordingDuration: TimeInterval = 120.0
static let maximumAudioRecordingLengthReachedThreshold: TimeInterval = 10.0
static let elapsedTimeFormat = "m:ss"
static let fileNameFormat = "'Voice message - 'MM.dd.yyyy HH.mm.ss"
static let minimumRecordingDuration = 1.0
}
private let themeService: ThemeService
private let mediaServiceProvider: VoiceMessageMediaServiceProvider
private let temporaryFileURL: URL
private var temporaryFileURL: URL!
private let _voiceMessageToolbarView: VoiceMessageToolbarView
private var displayLink: CADisplayLink!
@ -48,12 +49,18 @@ public class VoiceMessageController: NSObject, VoiceMessageToolbarViewDelegate,
private var isInLockedMode: Bool = false
private var notifiedRemainingTime = false
private static let timeFormatter: DateFormatter = {
private static let elapsedTimeFormatter: DateFormatter = {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = Constants.elapsedTimeFormat
return dateFormatter
}()
private static let fileNameDateFormatter: DateFormatter = {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = Constants.fileNameFormat
return dateFormatter
}()
@objc public weak var delegate: VoiceMessageControllerDelegate?
@objc public var isRecordingAudio: Bool {
@ -68,9 +75,6 @@ public class VoiceMessageController: NSObject, VoiceMessageToolbarViewDelegate,
self.themeService = themeService
self.mediaServiceProvider = mediaServiceProvider
let temporaryDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
temporaryFileURL = temporaryDirectoryURL.appendingPathComponent(ProcessInfo().globallyUniqueString).appendingPathExtension("m4a")
_voiceMessageToolbarView = VoiceMessageToolbarView.loadFromNib()
super.init()
@ -107,6 +111,11 @@ public class VoiceMessageController: NSObject, VoiceMessageToolbarViewDelegate,
audioRecorder = mediaServiceProvider.audioRecorder()
audioRecorder?.registerDelegate(self)
let temporaryDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
let fileName = VoiceMessageController.fileNameDateFormatter.string(from: Date())
temporaryFileURL = temporaryDirectoryURL.appendingPathComponent(fileName).appendingPathExtension("m4a")
audioRecorder?.recordWithOutputURL(temporaryFileURL)
}
@ -260,7 +269,7 @@ public class VoiceMessageController: NSObject, VoiceMessageToolbarViewDelegate,
})
dispatchGroup.enter()
let destinationURL = sourceURL.deletingPathExtension().appendingPathExtension("opus")
let destinationURL = sourceURL.deletingPathExtension().appendingPathExtension("ogg")
VoiceMessageAudioConverter.convertToOpusOgg(sourceURL: sourceURL, destinationURL: destinationURL) { result in
switch result {
case .success:
@ -342,7 +351,7 @@ public class VoiceMessageController: NSObject, VoiceMessageToolbarViewDelegate,
var details = VoiceMessageToolbarViewDetails()
details.state = (isRecording ? (isInLockedMode ? .lockedModeRecord : .record) : (isInLockedMode ? .lockedModePlayback : .idle))
details.elapsedTime = VoiceMessageController.timeFormatter.string(from: Date(timeIntervalSinceReferenceDate: currentTime))
details.elapsedTime = VoiceMessageController.elapsedTimeFormatter.string(from: Date(timeIntervalSinceReferenceDate: currentTime))
details.audioSamples = audioSamples
if isRecording {
@ -391,7 +400,7 @@ public class VoiceMessageController: NSObject, VoiceMessageToolbarViewDelegate,
var details = VoiceMessageToolbarViewDetails()
details.state = (audioRecorder?.isRecording ?? false ? (isInLockedMode ? .lockedModeRecord : .record) : (isInLockedMode ? .lockedModePlayback : .idle))
details.elapsedTime = VoiceMessageController.timeFormatter.string(from: Date(timeIntervalSinceReferenceDate: (audioPlayer.isPlaying ? audioPlayer.currentTime : audioPlayer.duration)))
details.elapsedTime = VoiceMessageController.elapsedTimeFormatter.string(from: Date(timeIntervalSinceReferenceDate: (audioPlayer.isPlaying ? audioPlayer.currentTime : audioPlayer.duration)))
details.audioSamples = audioSamples
details.isPlaying = audioPlayer.isPlaying
details.progress = (audioPlayer.isPlaying ? (audioPlayer.duration > 0.0 ? audioPlayer.currentTime / audioPlayer.duration : 0.0) : 0.0)

View file

@ -45,7 +45,7 @@ struct VoiceMessageToolbarViewDetails {
class VoiceMessageToolbarView: PassthroughView, NibLoadable, Themable, UIGestureRecognizerDelegate, VoiceMessagePlaybackViewDelegate {
private enum Constants {
static let longPressMinimumDuration: TimeInterval = 1.0
static let longPressMinimumDuration: TimeInterval = 0.0
static let animationDuration: TimeInterval = 0.25
static let lockModeTransitionAnimationDuration: TimeInterval = 0.5
static let panDirectionChangeThreshold: CGFloat = 20.0
@ -145,7 +145,7 @@ class VoiceMessageToolbarView: PassthroughView, NibLoadable, Themable, UIGesture
convertedFrame = self.convert(lockChevron.frame, from: lockContainerView)
lockChevronToRecordButtonDistance = recordButtonsContainerView.frame.midY + convertedFrame.maxY
lockChevronToLockButtonDistance = lockChevron.frame.minY - lockButtonsContainerView.frame.midY
lockChevronToLockButtonDistance = (lockChevron.frame.minY - lockButtonsContainerView.frame.midY) / 2
startAnimatingRecordingIndicator()
default:
@ -393,6 +393,10 @@ class VoiceMessageToolbarView: PassthroughView, NibLoadable, Themable, UIGesture
}
@objc private func handleWaveformTap(_ gestureRecognizer: UITapGestureRecognizer) {
guard self.lastUIState == .lockedModeRecord else {
return
}
delegate?.voiceMessageToolbarViewDidRequestRecordingFinish(self)
}
}

View file

@ -23,7 +23,7 @@
<rect key="frame" x="0.0" y="0.0" width="544" height="72"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="8fP-9K-WTa">
<rect key="frame" x="492" y="-90" width="44" height="152"/>
<rect key="frame" x="492" y="-80" width="44" height="152"/>
<subviews>
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="kvc-OZ-peC">
<rect key="frame" x="0.0" y="0.0" width="44" height="152"/>
@ -120,15 +120,15 @@
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="7OQ-1F-5qT">
<rect key="frame" x="488" y="10" width="52" height="52"/>
<rect key="frame" x="478" y="0.0" width="72" height="72"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="BDj-Sw-VQ5">
<rect key="frame" x="0.0" y="0.0" width="52" height="52"/>
<rect key="frame" x="0.0" y="0.0" width="72" height="72"/>
<state key="normal" image="voice_message_record_button_default"/>
</button>
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="rel-Fo-ROL">
<rect key="frame" x="0.0" y="0.0" width="52" height="52"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<rect key="frame" x="0.0" y="0.0" width="72" height="72"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<state key="normal" image="voice_message_record_button_recording"/>
</button>
</subviews>
@ -136,14 +136,14 @@
<constraints>
<constraint firstItem="BDj-Sw-VQ5" firstAttribute="leading" secondItem="7OQ-1F-5qT" secondAttribute="leading" id="2Xv-EI-etf"/>
<constraint firstAttribute="trailing" secondItem="BDj-Sw-VQ5" secondAttribute="trailing" id="2ZQ-3v-0W7"/>
<constraint firstAttribute="height" constant="52" id="4XA-Gb-5NO"/>
<constraint firstAttribute="height" constant="72" id="4XA-Gb-5NO"/>
<constraint firstAttribute="trailing" secondItem="BDj-Sw-VQ5" secondAttribute="trailing" id="Dki-cT-7xX"/>
<constraint firstAttribute="bottom" secondItem="BDj-Sw-VQ5" secondAttribute="bottom" id="fzv-iX-c1Y"/>
<constraint firstItem="BDj-Sw-VQ5" firstAttribute="top" secondItem="7OQ-1F-5qT" secondAttribute="top" id="mNa-EU-ZKQ"/>
<constraint firstAttribute="bottom" secondItem="BDj-Sw-VQ5" secondAttribute="bottom" id="phX-gD-B2H"/>
<constraint firstItem="BDj-Sw-VQ5" firstAttribute="top" secondItem="7OQ-1F-5qT" secondAttribute="top" id="pv8-li-wP8"/>
<constraint firstItem="BDj-Sw-VQ5" firstAttribute="leading" secondItem="7OQ-1F-5qT" secondAttribute="leading" id="ynJ-4x-1jv"/>
<constraint firstAttribute="width" constant="52" id="zPb-1B-JyA"/>
<constraint firstAttribute="width" constant="72" id="zPb-1B-JyA"/>
</constraints>
</view>
</subviews>
@ -152,7 +152,7 @@
<constraint firstItem="dyu-ha-046" firstAttribute="leading" secondItem="XRB-CY-ijK" secondAttribute="leading" id="BoC-Ut-chI"/>
<constraint firstAttribute="bottom" secondItem="dyu-ha-046" secondAttribute="bottom" id="U4h-FY-D3W"/>
<constraint firstItem="8fP-9K-WTa" firstAttribute="bottom" secondItem="7OQ-1F-5qT" secondAttribute="bottom" id="X4v-7T-LgP"/>
<constraint firstAttribute="trailing" secondItem="7OQ-1F-5qT" secondAttribute="trailing" constant="4" id="giC-4J-EUL"/>
<constraint firstAttribute="trailing" secondItem="7OQ-1F-5qT" secondAttribute="trailing" constant="-6" id="giC-4J-EUL"/>
<constraint firstItem="dyu-ha-046" firstAttribute="top" secondItem="XRB-CY-ijK" secondAttribute="top" id="ra2-Me-23b"/>
<constraint firstItem="8fP-9K-WTa" firstAttribute="centerX" secondItem="7OQ-1F-5qT" secondAttribute="centerX" id="xL5-g3-aHb"/>
<constraint firstAttribute="trailing" secondItem="dyu-ha-046" secondAttribute="trailing" id="xME-WZ-OMX"/>
@ -280,7 +280,7 @@
<image name="voice_message_lock_icon_locked" width="24" height="24"/>
<image name="voice_message_lock_icon_unlocked" width="16" height="16"/>
<image name="voice_message_record_button_default" width="22" height="26.5"/>
<image name="voice_message_record_button_recording" width="52" height="52"/>
<image name="voice_message_record_button_recording" width="72" height="72"/>
<image name="voice_message_record_icon" width="10" height="10"/>
</resources>
</document>

View file

@ -89,6 +89,7 @@ enum
enum
{
NOTIFICATION_SETTINGS_ENABLE_PUSH_INDEX = 0,
NOTIFICATION_SETTINGS_SYSTEM_SETTINGS,
NOTIFICATION_SETTINGS_SHOW_DECODED_CONTENT,
NOTIFICATION_SETTINGS_GLOBAL_SETTINGS_INDEX,
NOTIFICATION_SETTINGS_PIN_MISSED_NOTIFICATIONS_INDEX,
@ -235,6 +236,11 @@ TableViewSectionsDelegate>
*/
@property (nonatomic) BOOL newPhoneEditingEnabled;
/**
The current `UNUserNotificationCenter` notification settings for the app.
*/
@property (nonatomic) UNNotificationSettings *systemNotificationSettings;
@property (nonatomic, weak) DeactivateAccountViewController *deactivateAccountViewController;
@property (nonatomic, strong) SignOutAlertPresenter *signOutAlertPresenter;
@property (nonatomic, weak) UIButton *signOutButton;
@ -351,6 +357,7 @@ TableViewSectionsDelegate>
Section *sectionNotificationSettings = [Section sectionWithTag:SECTION_TAG_NOTIFICATIONS];
[sectionNotificationSettings addRowWithTag:NOTIFICATION_SETTINGS_ENABLE_PUSH_INDEX];
[sectionNotificationSettings addRowWithTag:NOTIFICATION_SETTINGS_SYSTEM_SETTINGS];
if (RiotSettings.shared.settingsScreenShowNotificationDecodedContentOption)
{
[sectionNotificationSettings addRowWithTag:NOTIFICATION_SETTINGS_SHOW_DECODED_CONTENT];
@ -1231,6 +1238,24 @@ TableViewSectionsDelegate>
[self editNewPhoneNumberTextField];
keepNewPhoneNumberEditing = NO;
}
// Update notification access
[self refreshSystemNotificationSettings];
}
- (void)refreshSystemNotificationSettings
{
MXWeakify(self);
// Get the system notification settings to check authorization status and configuration.
[UNUserNotificationCenter.currentNotificationCenter getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
dispatch_async(dispatch_get_main_queue(), ^{
MXStrongifyAndReturnIfNil(self);
self.systemNotificationSettings = settings;
[self.tableView reloadData];
});
}];
}
- (void)formatNewPhoneNumber
@ -1791,13 +1816,38 @@ TableViewSectionsDelegate>
MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
labelAndSwitchCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_enable_push_notif", @"Vector", nil);
labelAndSwitchCell.mxkSwitch.on = account.pushNotificationServiceIsActive;
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
labelAndSwitchCell.mxkSwitch.enabled = YES;
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(togglePushNotifications:) forControlEvents:UIControlEventTouchUpInside];
BOOL isPushEnabled = account.pushNotificationServiceIsActive;
// Even if push is enabled for the account, the user may have turned off notifications in system settings
if (isPushEnabled && self.systemNotificationSettings)
{
isPushEnabled = self.systemNotificationSettings.authorizationStatus == UNAuthorizationStatusAuthorized;
}
labelAndSwitchCell.mxkSwitch.on = isPushEnabled;
cell = labelAndSwitchCell;
}
else if (row == NOTIFICATION_SETTINGS_SYSTEM_SETTINGS)
{
cell = [tableView dequeueReusableCellWithIdentifier:kSettingsViewControllerPhoneBookCountryCellId];
if (!cell)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:kSettingsViewControllerPhoneBookCountryCellId];
}
cell.textLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
cell.textLabel.text = NSLocalizedStringFromTable(@"settings_device_notifications", @"Vector", nil);
cell.detailTextLabel.text = @"";
[cell vc_setAccessoryDisclosureIndicatorWithCurrentTheme];
cell.selectionStyle = UITableViewCellSelectionStyleDefault;
}
else if (row == NOTIFICATION_SETTINGS_SHOW_DECODED_CONTENT)
{
MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
@ -2501,6 +2551,10 @@ TableViewSectionsDelegate>
UITableViewCell *selectedCell = [tableView cellForRowAtIndexPath:indexPath];
[self showInviteFriendsFromSourceView:selectedCell];
}
else if (section == SECTION_TAG_NOTIFICATIONS && row == NOTIFICATION_SETTINGS_SYSTEM_SETTINGS)
{
[self openSystemSettingsApp];
}
else if (section == SECTION_TAG_DISCOVERY)
{
[self.settingsDiscoveryTableViewSection selectRow:row];
@ -2804,20 +2858,20 @@ TableViewSectionsDelegate>
- (void)togglePushNotifications:(UISwitch *)sender
{
// Check first whether the user allow notification from device settings
UIUserNotificationType currentUserNotificationTypes = UIApplication.sharedApplication.currentUserNotificationSettings.types;
if (currentUserNotificationTypes == UIUserNotificationTypeNone)
// Check first whether the user allow notification from system settings
if (self.systemNotificationSettings.authorizationStatus == UNAuthorizationStatusDenied)
{
[currentAlert dismissViewControllerAnimated:NO completion:nil];
__weak typeof(self) weakSelf = self;
NSString *appDisplayName = [[NSBundle mainBundle] infoDictionary][@"CFBundleDisplayName"];
currentAlert = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:NSLocalizedStringFromTable(@"settings_on_denied_notification", @"Vector", nil), appDisplayName] message:nil preferredStyle:UIAlertControllerStyleAlert];
NSString *title = NSLocalizedStringFromTable(@"settings_notifications_disabled_alert_title", @"Vector", nil);
NSString *message = NSLocalizedStringFromTable(@"settings_notifications_disabled_alert_message", @"Vector", nil);
[currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"ok"]
style:UIAlertActionStyleDefault
currentAlert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
[currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"]
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * action) {
if (weakSelf)
@ -2828,6 +2882,21 @@ TableViewSectionsDelegate>
}]];
UIAlertAction *settingsAction = [UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"settings"]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
self->currentAlert = nil;
[self openSystemSettingsApp];
}
}];
[currentAlert addAction:settingsAction];
currentAlert.preferredAction = settingsAction;
[currentAlert mxk_setAccessibilityIdentifier: @"SettingsVCPushNotificationsAlert"];
[self presentViewController:currentAlert animated:YES completion:nil];
@ -2871,6 +2940,12 @@ TableViewSectionsDelegate>
}
}
- (void)openSystemSettingsApp
{
NSURL *settingsAppURL = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
[[UIApplication sharedApplication] openURL:settingsAppURL options:@{} completionHandler:nil];
}
- (void)toggleCallKit:(UISwitch *)sender
{
[MXKAppSettings standardAppSettings].enableCallKit = sender.isOn;