diff --git a/CHANGES.md b/CHANGES.md index c03227f73d..13f2ec231c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,10 +8,12 @@ Improvements 🙌: - VoIP : new tiles in timeline - Improve room profile UX - Upgrade Jitsi library from 2.9.3 to 3.1.0 + - a11y improvements Bugfix 🐛: - VoIP : fix audio devices output - Fix crash after initial sync on Dendrite + - Fix crash reported by PlayStore (#2707) Translations 🗣: - @@ -26,6 +28,7 @@ Test: - Other changes: + - New Dev Tools panel for developers - Fix typos in CHANGES.md (#2811) Changes in Element 1.0.17 (2021-02-09) diff --git a/fastlane/metadata/android/ar/changelogs/40100100.txt b/fastlane/metadata/android/ar/changelogs/40100100.txt new file mode 100644 index 0000000000..25fc9c9058 --- /dev/null +++ b/fastlane/metadata/android/ar/changelogs/40100100.txt @@ -0,0 +1,2 @@ +يحتوي هذا الإصدار الجديد بشكل أساسي على إصلاحات للأخطاء وتحسينات. إرسال الرسالة أصبح الآن أسرع بكثير. +سجل التعديل الكامل: https://github.com/vector-im/element-android/releases/tag/v1.0.10 diff --git a/fastlane/metadata/android/ar/changelogs/40100110.txt b/fastlane/metadata/android/ar/changelogs/40100110.txt new file mode 100644 index 0000000000..1258ba323a --- /dev/null +++ b/fastlane/metadata/android/ar/changelogs/40100110.txt @@ -0,0 +1,2 @@ +يحتوي هذا الإصدار الجديد بشكل أساسي على تحسينات في واجهة المستخدم وتجربة المستخدم. يُمكنك الآن دعوة الأصدقاء وإنشاء رسالة مُباشرة بسرعة كبيرة عن طريق مسح رموز الاستجابة السريعة. +سجل التعديل الكامل: https://github.com/vector-im/element-android/releases/tag/v1.0.11 diff --git a/fastlane/metadata/android/ar/changelogs/40100120.txt b/fastlane/metadata/android/ar/changelogs/40100120.txt new file mode 100644 index 0000000000..18abe163dd --- /dev/null +++ b/fastlane/metadata/android/ar/changelogs/40100120.txt @@ -0,0 +1,2 @@ +التغييرات الرئيسة في هذا الإصدار: مُعاينة URL، لوحة مفاتيح Emoji جديدة، إمكانيات جديدة لإعدادات الغرفة والثلج لميلاد المسيح! +سجل التعديل الكامل: https://github.com/vector-im/element-android/releases/tag/v1.0.12 diff --git a/fastlane/metadata/android/ar/changelogs/40100130.txt b/fastlane/metadata/android/ar/changelogs/40100130.txt new file mode 100644 index 0000000000..eb50cdd31a --- /dev/null +++ b/fastlane/metadata/android/ar/changelogs/40100130.txt @@ -0,0 +1,2 @@ +التغييرات الرئيسة في هذا الإصدار: مُعاينة URL، لوحة مفاتيح Emoji جديدة، إمكانيات جديدة لإعدادات الغرفة والثلج لميلاد المسيح! +سجل التعديل الكامل: https://github.com/vector-im/element-android/releases/tag/v1.0.13 diff --git a/fastlane/metadata/android/ar/changelogs/40100140.txt b/fastlane/metadata/android/ar/changelogs/40100140.txt new file mode 100644 index 0000000000..d3b0dc2dbc --- /dev/null +++ b/fastlane/metadata/android/ar/changelogs/40100140.txt @@ -0,0 +1,2 @@ +التغييرات الرئيسة في هذا الإصدار: تحرير أذونات الغُرفة، السِّمة التلقائية الفاتحة/الداكنة، ومجموعة من إصلاحات الأخطاء. +سجل التعديل الكامل: https://github.com/vector-im/element-android/releases/tag/v1.0.14 diff --git a/fastlane/metadata/android/ar/changelogs/40100150.txt b/fastlane/metadata/android/ar/changelogs/40100150.txt new file mode 100644 index 0000000000..ba77aa0b1c --- /dev/null +++ b/fastlane/metadata/android/ar/changelogs/40100150.txt @@ -0,0 +1,2 @@ +التغييرات الرئيسة في هذا الإصدار: دعم تسجيل الدخول الاجتماعي. +سجل التعديل الكامل: https://github.com/vector-im/element-android/releases/tag/v1.0.15 diff --git a/fastlane/metadata/android/ar/changelogs/40100160.txt b/fastlane/metadata/android/ar/changelogs/40100160.txt new file mode 100644 index 0000000000..b52a902cfa --- /dev/null +++ b/fastlane/metadata/android/ar/changelogs/40100160.txt @@ -0,0 +1,2 @@ +التغييرات الرئيسة في هذا الإصدار: دعم تسجيل الدخول الاجتماعي. +سجل التعديل الكامل: https://github.com/vector-im/element-android/releases/tag/v1.0.15 and https://github.com/vector-im/element-android/releases/tag/v1.0.16 diff --git a/fastlane/metadata/android/ar/changelogs/40100170.txt b/fastlane/metadata/android/ar/changelogs/40100170.txt new file mode 100644 index 0000000000..8f2226ae0e --- /dev/null +++ b/fastlane/metadata/android/ar/changelogs/40100170.txt @@ -0,0 +1,2 @@ +التغييرات الرئيسة في هذا الإصدار: إصلاحات الأخطاء! +سجل التعديل الكامل: https://github.com/vector-im/element-android/releases/tag/v1.0.17 diff --git a/fastlane/metadata/android/ar/full_description.txt b/fastlane/metadata/android/ar/full_description.txt new file mode 100644 index 0000000000..c0baa7d709 --- /dev/null +++ b/fastlane/metadata/android/ar/full_description.txt @@ -0,0 +1,31 @@ +Element هو نوع جديد من تطبيقات المُراسلة والتعاون الذي: + +1. يمنحك التحكم في المُحافضة على خصوصيتك +2. يُتيح لك التواصل مع أي شخص على شبكة Matrix ، وحتى خارجها من خلال التكامل مع التطبيقات مثل Slack +3. يحميك من الإعلانات والتنقيب عن البيانات وعمليات الحدائق المُسورة +4. يؤمنك من خلال تعمية النهاية-إلى-النهاية، مع التوقيع المُتبادل للتحقق من الآخرين + +يختلف Element تمامًا عن تطبيقات المُراسلة والتعاون الأُخرى لأنه لا مركزي ومفتوح المصدر. + +يُتيح لك Element إمكانية الاستضافة الذاتية -أو اختيار مُضيف- بحيث تتمتع بالخصوصية والمُلكية والتحكم في بياناتك ومُحادثاتك. يُتيح لك الوصول إلى شبكة مفتوحة؛ لذلك لا يقتصر الأمر على التحدث إلى مستخدمي Element الآخرين فقط. كما انه آمن للغاية. + +Element قادر على القيام بكل ذلك لأنه يعمل على Matrix -مِعيار التواصل المفتوح اللامركزي. + +Element يمنحك زمام التحكم من خلال السماح لك باختيار من يستضيف المُحادثات الخاصة بك. من تطبيق Element، يُمكنك اختيار الاستضافة بطرق مختلفة: + +1. الحُصول على حساب مجاني على الخادِم العام matrix.org الذي يستضيفه مطورو Matrix، أو اختر من بين آلاف الخوادِم العامة التي يستضيفها متطوعون +2. استضافة حسابك بنفسك عن طريق تشغيل خادِم على أجهزتك الخاصة +3. التسجيل للحصول على حساب على خادِم مُخصص بمُجرد الاشتراك في منصة استضافة Element Matrix Services + + لماذا تختار Element؟ + +تملَّك بياناتك: أنت من تُقرر أين تحتفظ ببياناتك ورسائلك. أنت تمتلكها وتتحكم فيها، وليس بعض الشركات الكُبرى الإحتكارية التي تُنقِّب عن بياناتك أو تُتيح الوصول إلى أطراف ثالثة. + + +تراسُل وتعاون مفتوح: يُمكنك مُحادثة أي شخص آخر على شبكة Matrix، سواء كانوا يستخدمون Element أو تطبيق Matrix آخر، وحتى إذا كانوا يستخدمون نظام مُراسلة مُختلف مثل Slack أو IRC أو XMPP. + +الأمان-الخارق: تشفير حقيقي من النهاية إلى النهاية (فقط أطراف المُحادثة مَن يُمكنهم فك تشفير الرسائل)، والتوقيع المُتبادل للتحقق من أجهزة المُشاركين في المُحادثة. + +التواصل الكامل: المُراسلة، المُكالمات الصوتية والمرئية، مُشاركة الملفات، مُشاركة الشاشة، مجموعة كاملة وكبيرة من عمليات التكامُل، الروبوتات والأدوات. بناء الغُرف، المُجتمعات، ابق على اتصال وأنجز المهام. + +أين ما كُنت: ابق على اتصال أينما كنت مع سجل الرسائل المتزامن بالكامل عبر جميع أجهزتك وفي الويب على https://app.element.io. diff --git a/fastlane/metadata/android/ar/short_description.txt b/fastlane/metadata/android/ar/short_description.txt new file mode 100644 index 0000000000..48df6f2b0c --- /dev/null +++ b/fastlane/metadata/android/ar/short_description.txt @@ -0,0 +1 @@ +مُحادثة آمنة لا مركزية و VoIP. حافظ على بياناتك آمنة من الأطراف الثالثة. diff --git a/fastlane/metadata/android/ar/title.txt b/fastlane/metadata/android/ar/title.txt new file mode 100644 index 0000000000..9b382729c8 --- /dev/null +++ b/fastlane/metadata/android/ar/title.txt @@ -0,0 +1 @@ +Element (سابقاً Riot.im) diff --git a/fastlane/metadata/android/ca/changelogs/40100170.txt b/fastlane/metadata/android/ca/changelogs/40100170.txt new file mode 100644 index 0000000000..8a208b66f7 --- /dev/null +++ b/fastlane/metadata/android/ca/changelogs/40100170.txt @@ -0,0 +1,2 @@ +Canvis principals d'aquesta versió: correcció d'errors! +Registre de canvis complet: https://github.com/vector-im/element-android/releases/tag/v1.0.17 diff --git a/fastlane/metadata/android/de/changelogs/40100170.txt b/fastlane/metadata/android/de/changelogs/40100170.txt new file mode 100644 index 0000000000..2ea20cf3ce --- /dev/null +++ b/fastlane/metadata/android/de/changelogs/40100170.txt @@ -0,0 +1,2 @@ +Hauptänderungen in dieser Version: Fehlerkorrekturen +Vollständiges Änderungsprotokoll: https://github.com/vector-im/element-android/releases/tag/v1.0.17 diff --git a/fastlane/metadata/android/et/changelogs/40100170.txt b/fastlane/metadata/android/et/changelogs/40100170.txt new file mode 100644 index 0000000000..3c565c011c --- /dev/null +++ b/fastlane/metadata/android/et/changelogs/40100170.txt @@ -0,0 +1,2 @@ +Olulisemad muutused selles versioonis: Veaparandused! +Muudatuste logi täismahus: https://github.com/vector-im/element-android/releases/tag/v1.0.17 diff --git a/fastlane/metadata/android/it/changelogs/40100130.txt b/fastlane/metadata/android/it/changelogs/40100130.txt index fcf8ca0f4e..ef27be9e1e 100644 --- a/fastlane/metadata/android/it/changelogs/40100130.txt +++ b/fastlane/metadata/android/it/changelogs/40100130.txt @@ -1,2 +1,2 @@ Modifiche principali in questa versione: anteprima URL, nuova tastiera emoji, nuove impostazioni stanza e neve per Natale! -Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.0.12 +Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.0.13 diff --git a/fastlane/metadata/android/it/changelogs/40100140.txt b/fastlane/metadata/android/it/changelogs/40100140.txt new file mode 100644 index 0000000000..0bf5c1b085 --- /dev/null +++ b/fastlane/metadata/android/it/changelogs/40100140.txt @@ -0,0 +1,2 @@ +Modifiche principali in questa versione: modifica autorizzazioni stanza, tema chiaro/scuro automatico e varie correzioni di errori. +Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.0.14 diff --git a/fastlane/metadata/android/it/changelogs/40100150.txt b/fastlane/metadata/android/it/changelogs/40100150.txt new file mode 100644 index 0000000000..5df2a0d650 --- /dev/null +++ b/fastlane/metadata/android/it/changelogs/40100150.txt @@ -0,0 +1,2 @@ +Modifiche principali in questa versione: supporto all'accesso dai social. +Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.0.15 diff --git a/fastlane/metadata/android/it/changelogs/40100160.txt b/fastlane/metadata/android/it/changelogs/40100160.txt new file mode 100644 index 0000000000..9177421e44 --- /dev/null +++ b/fastlane/metadata/android/it/changelogs/40100160.txt @@ -0,0 +1,2 @@ +Modifiche principali in questa versione: supporto all'accesso dai social. +Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.0.15 and https://github.com/vector-im/element-android/releases/tag/v1.0.16 diff --git a/fastlane/metadata/android/it/changelogs/40100170.txt b/fastlane/metadata/android/it/changelogs/40100170.txt new file mode 100644 index 0000000000..b2a8424497 --- /dev/null +++ b/fastlane/metadata/android/it/changelogs/40100170.txt @@ -0,0 +1,2 @@ +Modifiche principali in questa versione: correzioni di errori! +Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.0.17 diff --git a/fastlane/metadata/android/sk/changelogs/40100120.txt b/fastlane/metadata/android/sk/changelogs/40100120.txt new file mode 100644 index 0000000000..46d39ad9e7 --- /dev/null +++ b/fastlane/metadata/android/sk/changelogs/40100120.txt @@ -0,0 +1,2 @@ +Hlavné zmeny v tejto verzii: Ukážka URL, nová klávesnica Emoji, nové možnosti nastavenia miestnosti a sneh na Vianoce! +Celý zoznam zmien: https://github.com/vector-im/element-android/releases/tag/v1.0.12 diff --git a/fastlane/metadata/android/sk/changelogs/40100130.txt b/fastlane/metadata/android/sk/changelogs/40100130.txt new file mode 100644 index 0000000000..6dc39b44f1 --- /dev/null +++ b/fastlane/metadata/android/sk/changelogs/40100130.txt @@ -0,0 +1,2 @@ +Hlavné zmeny v tejto verzii: Ukážka URL, nová klávesnica Emoji, nové možnosti nastavenia miestnosti a sneh na Vianoce! +Celý zoznam zmien: https://github.com/vector-im/element-android/releases/tag/v1.0.13 diff --git a/fastlane/metadata/android/sk/changelogs/40100140.txt b/fastlane/metadata/android/sk/changelogs/40100140.txt new file mode 100644 index 0000000000..22db5f972f --- /dev/null +++ b/fastlane/metadata/android/sk/changelogs/40100140.txt @@ -0,0 +1,2 @@ +Hlavné zmeny v tejto verzii: Úpravy povolení miestnosti, automatický svetlý / tmavý motív a veľa opráv chýb. +Celý zoznam zmien: https://github.com/vector-im/element-android/releases/tag/v1.0.14 diff --git a/fastlane/metadata/android/sk/changelogs/40100150.txt b/fastlane/metadata/android/sk/changelogs/40100150.txt new file mode 100644 index 0000000000..ca26356e8a --- /dev/null +++ b/fastlane/metadata/android/sk/changelogs/40100150.txt @@ -0,0 +1,2 @@ +Hlavné zmeny v tejto verzii: Podpora sociálneho prihlásenia. +Celý zoznam zmien: https://github.com/vector-im/element-android/releases/tag/v1.0.15 diff --git a/fastlane/metadata/android/sk/changelogs/40100160.txt b/fastlane/metadata/android/sk/changelogs/40100160.txt new file mode 100644 index 0000000000..28cd4b52c9 --- /dev/null +++ b/fastlane/metadata/android/sk/changelogs/40100160.txt @@ -0,0 +1,2 @@ +Hlavné zmeny v tejto verzii: Podpora sociálneho prihlásenia. +Celý zoznam zmien: https://github.com/vector-im/element-android/releases/tag/v1.0.15 and https://github.com/vector-im/element-android/releases/tag/v1.0.16 diff --git a/fastlane/metadata/android/sk/changelogs/40100170.txt b/fastlane/metadata/android/sk/changelogs/40100170.txt new file mode 100644 index 0000000000..41336e7700 --- /dev/null +++ b/fastlane/metadata/android/sk/changelogs/40100170.txt @@ -0,0 +1,2 @@ +Hlavné zmeny v tejto verzii: Opravy chýb! +Celý zoznam zmien: https://github.com/vector-im/element-android/releases/tag/v1.0.17 diff --git a/fastlane/metadata/android/sk/short_description.txt b/fastlane/metadata/android/sk/short_description.txt index 7d352942bf..0744f4a617 100644 --- a/fastlane/metadata/android/sk/short_description.txt +++ b/fastlane/metadata/android/sk/short_description.txt @@ -1 +1 @@ -Zabezpečené konverzácie a VoIP. Ochráňte vaše údaje pred zhromažďovaním. +Zabezpečené konverzácie a VoIP. Ochráňte vaše údaje pred tretími stranami. diff --git a/fastlane/metadata/android/sr/changelogs/40100170.txt b/fastlane/metadata/android/sr/changelogs/40100170.txt new file mode 100644 index 0000000000..51d332a5ea --- /dev/null +++ b/fastlane/metadata/android/sr/changelogs/40100170.txt @@ -0,0 +1,2 @@ +Главна измена у овој верзији: сређене грешке! +Цео дневник измена: https://github.com/vector-im/element-android/releases/tag/v1.0.15 и https://github.com/vector-im/element-android/releases/tag/v1.0.17 diff --git a/fastlane/metadata/android/uk/changelogs/40100170.txt b/fastlane/metadata/android/uk/changelogs/40100170.txt new file mode 100644 index 0000000000..c4b1bcc8e0 --- /dev/null +++ b/fastlane/metadata/android/uk/changelogs/40100170.txt @@ -0,0 +1,2 @@ +Основні зміни у цій версії: Виправлення помилок! +Повний перелік змін: https://github.com/vector-im/element-android/releases/tag/v1.0.17 diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomThirdPartyInviteContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomThirdPartyInviteContent.kt index 776acbd8ea..56503e3e35 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomThirdPartyInviteContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomThirdPartyInviteContent.kt @@ -30,24 +30,24 @@ data class RoomThirdPartyInviteContent( * This should not contain the user's third party ID, as otherwise when the invite * is accepted it would leak the association between the matrix ID and the third party ID. */ - @Json(name = "display_name") val displayName: String, + @Json(name = "display_name") val displayName: String?, /** * Required. A URL which can be fetched, with querystring public_key=public_key, to validate * whether the key has been revoked. The URL must return a JSON object containing a boolean property named 'valid'. */ - @Json(name = "key_validity_url") val keyValidityUrl: String, + @Json(name = "key_validity_url") val keyValidityUrl: String?, /** * Required. A base64-encoded ed25519 key with which token must be signed (though a signature from any entry in * public_keys is also sufficient). This exists for backwards compatibility. */ - @Json(name = "public_key") val publicKey: String, + @Json(name = "public_key") val publicKey: String?, /** * Keys with which the token may be signed. */ - @Json(name = "public_keys") val publicKeys: List = emptyList() + @Json(name = "public_keys") val publicKeys: List? = emptyList() ) @JsonClass(generateAdapter = true) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt index 444366e912..e614ea91d6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt @@ -65,13 +65,30 @@ interface StateService { */ suspend fun deleteAvatar() + /** + * Send a state event to the room + */ suspend fun sendStateEvent(eventType: String, stateKey: String?, body: JsonDict) + /** + * Get a state event of the room + */ fun getStateEvent(eventType: String, stateKey: QueryStringValue = QueryStringValue.NoCondition): Event? + /** + * Get a live state event of the room + */ fun getStateEventLive(eventType: String, stateKey: QueryStringValue = QueryStringValue.NoCondition): LiveData> + /** + * Get state events of the room + * @param eventTypes Set of eventType. If empty, all state events will be returned + */ fun getStateEvents(eventTypes: Set, stateKey: QueryStringValue = QueryStringValue.NoCondition): List + /** + * Get live state events of the room + * @param eventTypes Set of eventType to observe. If empty, all state events will be observed + */ fun getStateEventsLive(eventTypes: Set, stateKey: QueryStringValue = QueryStringValue.NoCondition): LiveData> } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessor.kt index 5014d94558..62338a1d07 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessor.kt @@ -196,6 +196,7 @@ internal class EventSenderProcessor @Inject constructor( else -> { Timber.v("## SendThread retryLoop Un-Retryable error, try next task") // this task is in error, check next one? + task.onTaskFailed() break@retryLoop } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/StateEventDataSource.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/StateEventDataSource.kt index d0f6f8050e..a25a362bfa 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/StateEventDataSource.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/StateEventDataSource.kt @@ -80,7 +80,11 @@ internal class StateEventDataSource @Inject constructor(@SessionDatabase private ): RealmQuery { return realm.where() .equalTo(CurrentStateEventEntityFields.ROOM_ID, roomId) - .`in`(CurrentStateEventEntityFields.TYPE, eventTypes.toTypedArray()) + .apply { + if (eventTypes.isNotEmpty()) { + `in`(CurrentStateEventEntityFields.TYPE, eventTypes.toTypedArray()) + } + } .process(CurrentStateEventEntityFields.STATE_KEY, stateKey) } } diff --git a/matrix-sdk-android/src/main/res/values-ar/strings.xml b/matrix-sdk-android/src/main/res/values-ar/strings.xml index 0fc7bd1b49..0da4dc8f8e 100644 --- a/matrix-sdk-android/src/main/res/values-ar/strings.xml +++ b/matrix-sdk-android/src/main/res/values-ar/strings.xml @@ -1,147 +1,150 @@ - + - - أرسل ⁨%1$s⁩ صورة. - - دعوة من ⁨%s⁩ - دعى ⁨%1$s⁩ ⁨%2$s⁩ - دعاك ⁨%1$s⁩ - انضمّ ⁨%1$s⁩ إلى الغرفة - غادر ⁨%1$s⁩ الغرفة - رفض ⁨%1$s⁩ الدعوة - طرد ⁨%1$s⁩ ⁨%2$s⁩ - رفع ⁨%1$s⁩ المنع عن ⁨%2$s⁩ - منع ⁨%1$s⁩ ⁨%2$s⁩ - غيّر ⁨%1$s⁩ صورته - ضبط ⁨%1$s⁩ اسم العرض على ⁨%2$s⁩ - غيّر ⁨%1$s⁩ اسم العرض من ⁨%2$s⁩ إلى ⁨%3$s⁩ - أزال ⁨%1$s⁩ اسم العرض (⁨كان ⁨%2$s⁩) - غيّر ⁨%1$s⁩ الموضوع إلى: ⁨%2$s⁩ - غيّر ⁨%1$s⁩ اسم الغرفة إلى: ⁨%2$s⁩ - ردّ ⁨%s⁩ على المكالمة. - أنهى ⁨%s⁩ المكالمة. - جعل ⁨%1$s⁩ تأريخ الغرفة مستقبلًا ظاهرًا على ⁨%2$s⁩ - كل أعضاء الغرفة من لحظة دعوتهم. - كل أعضاء الغرفة من لحظة انضمامهم. - كل أعضاء الغرفة. - الكل. - المجهول (⁨%s⁩). - فعّل ⁨%1$s⁩ تعمية الطرفين (⁨%2$s⁩) - - طلب ⁨%1$s⁩ اجتماع VoIP - بدأ اجتماع VoIP - انتهى اجتماع VoIP - - أزال ⁨%1$s⁩ اسم الغرفة - أزال ⁨%1$s⁩ موضوع الغرفة + %1$s قد أرسل صورة. + دعوة من %s + %1$s قد دعى %2$s + %1$s قد دعاك أنت + %1$s قد إنضّم إلى الغرفة + %1$s قد غادر الغرفة + %1$s قد رفض الدعوة + %1$s قد طرد %2$s + %1$s قد رفع الحظر عن %2$s + %1$s قد حظر %2$s + %1$s قد غيّر صورته الشخصية + %1$s قد عيّن اسمه الظاهر إلى %2$s + %1$s قد غيّر اسمه الظاهر من %2$s إلى %3$s + %1$s قد أزال اسمه الظاهر (لقد كان %2$s) + %1$s قد غيّر الموضوع إلى: %2$s + %1$s قد غيّر اسم الغرفة إلى: %2$s + %s قد أجاب على المُكالمة. + %s قد أنهى المُكالمة. + %1$s قد جعل التأريخ المُستقبلي للغرفة مرئيًا لـ %2$s + جميع أعضاء الغرفة، من اللحظة التي تمت دعوتهم. + جميع أعضاء الغرفة، من لحظة انضمامهم. + جميع أعضاء الغرفة. + أيُّ شخص. + غير معروف (%s). + %1$s قد فعّل تعمية النهاية-إلى-النهاية (%2$s) + %1$s قد طلب اجتماع VoIP + اجتماع VoIP قد بدأ + اجتماع VoIP قد انتهى + %1$s قد أزال اسم الغرفة + %1$s قد أزال موضوع الغرفة حدّث ⁨%1$s⁩ اللاحة ⁨%2$s⁩ أرسل ⁨%1$s⁩ دعوة إلى ⁨%2$s⁩ للانضمام إلى الغرفة ** تعذّر فك التعمية: ⁨%s⁩ ** لم يُرسل جهاز المرسل مفاتيح هذه الرسالة. - تعذّر إرسال الرسالة - فشل رفع الصورة - خطأ في الشبكة خطأ في «ماترِكس» - لا يمكنك حاليًا الانضمام ثانيةً إلى غرفة فارغة. - رسالة معمّاة - عنوان البريد الإلكتروني رقم الهاتف - ‏‏⁨%1$s⁩: ‏⁨%2$s⁩ - انسحب ⁨%1$s⁩ من دعوة ⁨%2$s⁩ - أجرى ⁨%s⁩ مكالمة مرئية. - أجرى ⁨%s⁩ مكالمة صوتية. + %1$s قد سحب دعوة %2$s + %s قد أجرى مُكالمة مرئية. + %s قد أجرى مُكالمة صوتية. قَبِل ⁨%1$s⁩ دعوة ⁨%2$s⁩ - تعذر التهذيب - أرسل ⁨%1$s⁩ ملصقًا. - - (تغيّرت الصورة أيضا) - + %1$s قد أرسل مُلصقًا. + (تمَّ تغيير الصورة أيضًا) دعوة من ⁨%s⁩ غرفة فارغة - ‏⁨%1$s⁩ و ⁨%2$s⁩ دعوة إلى غرفة - - - - - - - + + + + + + - - أرسلت صورة. - أرسلت ملصقًا. - + أنت قد أرسلت صورة. + أنت قد أرسلت مُلصقًا. دعوة منك أنت - أنشأ ⁨%1$s⁩ الغرفة - أنشأت الغرفة - دعوت ⁨%1$s⁩ - انضممت إلى الغرفة - غادرت الغرفة - رفضت الدعوة - طردت ⁨%1$s⁩ - رفعت المنع عن ⁨%1$s⁩ - منعت ⁨%1$s⁩ - انسحبت من دعوة ⁨%1$s⁩ - غيّرت صورتك - ضبطت اسم العرض على ⁨%1$s⁩ - غيّرت اسم العرض من ⁨%1$s⁩ إلى ⁨%2$s⁩ - أزلت اسم العرض (كان ⁨%1$s⁩) - غيّرت الموضوع إلى: ⁨%1$s⁩ - غيّر ⁨%1$s⁩ صورة الغرفة - غيّرت صورة الغرفة - غيّرت اسم الغرفة إلى: ⁨%1$s⁩ - أجريت مكالمة مرئية. - أجريت مكالمة صوتية. - أرسل ⁨%s⁩ البيانات لإعداد المكالمة. - أرسلت البيانات لإعداد المكالمة. - رددت على المكالمة. - أنهيت المكالمة. - جعلت تأريخ الغرفة مستقبلًا ظاهرًا على ⁨%1$s⁩ - فعّلت تعمية الطرفين (⁨%1$s⁩) - رقّى ⁨%s⁩ هذه الغرفة. - رقّيت هذه الغرفة. - - طلبت اجتماع VoIP - أزلت اسم الغرفة - أزلت موضوع الغرفة - أزال ⁨%1$s⁩ صورة الغرفة - أزلت صورة الغرفة - أُزيلت الرسالة - أزال ⁨%1$s⁩ الرسالة + %1$s قد أنشأ الغرفة + أنت قد أنشأت الغرفة + أنت قد دعوت %1$s + أنت قد انضممت إلى الغرفة + أنت قد غادرت الغرفة + أنت قد رفضت الدعوة + أنت قد طردت %1$s + أنت قد رفعت الحظر عن %1$s + أنت قد حظرت %1$s + أنت قد سحبت دعوة %1$s + أنت قد غيّرت صورتك الشخصية + أنت قد عيّنت اسمك الظاهر إلى %1$s + أنت قد غيّرت اسمك الظاهر من ⁨%1$s⁩ إلى ⁨%2$s⁩ + أنت قد أزلت اسمك الظاهر (لقد كان ⁨%1$s⁩) + أنت قد غيّرت الموضوع إلى: ⁨%1$s⁩ + %1$s قد غيّر صورة الغرفة + أنت قد غيّرت صورة الغرفة + أنت قد غيّرت اسم الغرفة إلى: %1$s + أنت قد أجريت مُكالمة مرئية. + أنت قد أجريت مُكالمة صوتية. + %s قد أرسل بيانات لإعداد مُكالمة. + أنت قد أرسلت بيانات لإعداد مُكالمة. + أنت قد أجبت على المُكالمة. + أنت قد أنهيت المُكالمة. + أنت قد جعلت التأريخ المُستقبلي للغرفة مرئيًا لـ %1$s + أنت قد فعّلت تعيمية النهاية-إلى-النهاية (%1$s) + %s قد قام بترقية هذه الغرفة. + أنت قد رقّيتَ هذه الغرفة. + أنت قد طلبت اجتماع VoIP + أنت قد أزلت اسم الغرفة + أنت قد أزلت موضوع الغرفة + %1$s قد أزال صورة الغرفة + أنت قد أزلت صورة الغرفة + تمت إزالة الرسالة + الرسالة قد أُزيلت بواسطة %1$s أُزيلت الرسالة [السبب: ⁨%1$s⁩] أزال ⁨%1$s⁩ الرسالة [السبب: ⁨%2$s⁩] أرسلت دعوة إلى ⁨%1$s⁩ للانضمام إلى الغرفة سحب ⁨%1$s⁩ دعوة ⁨%2$s⁩ للانضمام إلى الغرفة سحبت دعوة ⁨%1$s⁩ للانضمام إلى الغرفة قَبِلت دعوة ⁨%1$s⁩ - أضاف ⁨%1$s⁩ الودجة ⁨%2$s⁩ أضفت الودجة ⁨%1$s⁩ أزال ⁨%1$s⁩ الودجة ⁨%2$s⁩ أزلت الودجة ⁨%1$s⁩ عدّل ⁨%1$s⁩ الودجة ⁨%2$s⁩ عدّلت الودجة ⁨%1$s⁩ - مدير المبدئي مخصّص (⁨%1$d⁩) مخصّص - غيّرت مستوى قوّة %1$s⁩. غيّر ⁨%1$s⁩ مستوى قوّة %2$s⁩. ‏⁨%1$s⁩ من ⁨%2$s⁩ إلى ⁨%3$s⁩ - المزامنة الأولية: \nيستورد الحساب… - + 🎉 جميع الخوادم محظورة من المُشاركة! لم يعُد من الممكن استخدام هذه الغرفة. + لا تغيير. + • خوادم مُطابقة IP الحرفية محظورة الآن. + • الخادم المُطابق لـ %s قد أُزيل من قائمة السماح. + • الخادم المُطابق لـ %s مسموح الآن. + • الخادم المُطابق لـ %s قد أُزيل من قائمة الحظر. + • الخادم المُطابق لـ %s محظور الآن. + • خوادم مُطابقة IP الحرفية مسموحة الآن. + أنت قد غيّرت خادم الـACLs لهذه الغرفة. + %s قد غيّر خادم الـACLs لهذه الغرفة. + • الخادم يحظر مُطابقة القيم الحرفية للـIP. + • الخادم المُطابق لـ %s مسموح. + • الخادم المُطابق لـ %s محظور. + • الخادم يسمح بمُطابقة القيم الحرفية للـIP. + أنت قد عيّنت خادم الـACLs لهذه الغرفة. + %s قد عيّن خادم الـACLs لهذه الغرفة. + أنت قد قمت بالترقية هُنا. + %s قد قام بالترقية هُنا. + أنت قد جعلت الرسائل المُستقبلية مرئية لـ %1$s + %1$s قد جعل الرسائل المُستقبلية مرئية لـ %2$s + أنت قد غادرت الغرفة + %1$s قد غادر الغرفة + أنت قد انضممت + %1$s قد انضم + أنت قد أنشأت المُناقشة + %1$s قد أنشأ المُناقشة + \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-ca/strings.xml b/matrix-sdk-android/src/main/res/values-ca/strings.xml index 728c4f6f60..98bda2c4ed 100644 --- a/matrix-sdk-android/src/main/res/values-ca/strings.xml +++ b/matrix-sdk-android/src/main/res/values-ca/strings.xml @@ -259,4 +259,10 @@ %s ha canviat les ACLs de servidor d\'aquesta sala. Has establert les ACLs de servidor per aquesta sala. %s ha establert les ACLs de servidor d\'aquesta sala. + Has modificat la videoconferència + %1$s ha modificat la videoconferència + Has finalitzat la videoconferència + %1$s ha iniciat una videoconferència + Has iniciat una videoconferència + %1$s ha finalitzat la videoconferència \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-de/strings.xml b/matrix-sdk-android/src/main/res/values-de/strings.xml index dfe29100c4..81cf01983d 100644 --- a/matrix-sdk-android/src/main/res/values-de/strings.xml +++ b/matrix-sdk-android/src/main/res/values-de/strings.xml @@ -266,4 +266,10 @@ • Server, die mit %s übereinstimmen, sind gesperrt. Du hast die Server-ACL für diesen Raum gesetzt. %s hat die Server-Zugriffssteuerungsliste (ACL) für diesen Raum gesetzt. + Du hast eine Videokonferenz geändert + Videokonferenz von %1$s geändert + Videokonferenz von %1$s beendet + Du hast eine Videokonferenz beendet + Du hast eine Videokonferenz gestartet + Videokonferenz von %1$s gestartet \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-et/strings.xml b/matrix-sdk-android/src/main/res/values-et/strings.xml index af2cc33b99..0cf5c3e438 100644 --- a/matrix-sdk-android/src/main/res/values-et/strings.xml +++ b/matrix-sdk-android/src/main/res/values-et/strings.xml @@ -258,4 +258,10 @@ %1$s lisas sellele jututoale täiendavad aadressid %2$s. Sa muutsid selle jututoa aadresse. + Sina muutsid videokoosolekut + Sina lõpetasid videokoosoleku + %1$s lõpetas videokoosoleku + Sina algatasid videokoosoleku + %1$s algatas videokoosoleku + %1$s muutis videokoosolekut \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-it/strings.xml b/matrix-sdk-android/src/main/res/values-it/strings.xml index ec19cd5c17..e1f57ddac0 100644 --- a/matrix-sdk-android/src/main/res/values-it/strings.xml +++ b/matrix-sdk-android/src/main/res/values-it/strings.xml @@ -259,4 +259,10 @@ %1$s ha aggiunto l\'indirizzo alternativo %2$s per questa stanza. %1$s ha aggiunto gli indirizzi alternativi %2$s per questa stanza. + Hai modificato la video conferenza + Video conferenza modificata da %1$s + Hai iniziato la video conferenza + Hai terminato la video conferenza + Video conferenza terminata da %1$s + Video conferenza iniziata da %1$s \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-sr/strings.xml b/matrix-sdk-android/src/main/res/values-sr/strings.xml index 5e0a72a4ea..c55c92d58a 100644 --- a/matrix-sdk-android/src/main/res/values-sr/strings.xml +++ b/matrix-sdk-android/src/main/res/values-sr/strings.xml @@ -268,4 +268,10 @@ %1$s уклони %2$s као адресе ове собе. %1$s уклони %2$s као адресе ове собе. + Изменили сте видео конференцију + %1$s измени видео конференцију + Завршили сте видео конференцију + %1$s заврши видео конференцију + Покренули сте видео конференцију + %1$s покрену видео конференцију \ No newline at end of file diff --git a/vector/lint.xml b/vector/lint.xml index 572f937406..28da13ca8d 100644 --- a/vector/lint.xml +++ b/vector/lint.xml @@ -17,6 +17,10 @@ + + + + diff --git a/vector/src/debug/res/layout/activity_test_material_theme.xml b/vector/src/debug/res/layout/activity_test_material_theme.xml index f6f12942fe..21a2d3fbf5 100644 --- a/vector/src/debug/res/layout/activity_test_material_theme.xml +++ b/vector/src/debug/res/layout/activity_test_material_theme.xml @@ -159,6 +159,7 @@ diff --git a/vector/src/main/AndroidManifest.xml b/vector/src/main/AndroidManifest.xml index 6af3ea6733..52932920d4 100644 --- a/vector/src/main/AndroidManifest.xml +++ b/vector/src/main/AndroidManifest.xml @@ -267,6 +267,7 @@ + @@ -388,7 +389,7 @@ SOFTWARE.
  • dialogs / android-dialer
    - Copyright (c) 2017-present, dialog LLC + Copyright (c) 2017-present, dialog LLC <info@dlg.im>
  • @@ -570,20 +571,24 @@ Apache License
     
     
         CC-BY 4.0
    +
    +
    • Twitter/twemoji Graphics
    • -
    +
         ISC License
    +
    +
    • DanielMartinus / Konfetti
      Copyright (c) 2017 Dion Segijn
    • - +
    diff --git a/vector/src/main/java/im/vector/app/core/di/ActiveSessionHolder.kt b/vector/src/main/java/im/vector/app/core/di/ActiveSessionHolder.kt index 77ca68fcf1..e739cac95f 100644 --- a/vector/src/main/java/im/vector/app/core/di/ActiveSessionHolder.kt +++ b/vector/src/main/java/im/vector/app/core/di/ActiveSessionHolder.kt @@ -23,7 +23,6 @@ import im.vector.app.features.crypto.keysrequest.KeyRequestHandler import im.vector.app.features.crypto.verification.IncomingVerificationRequestHandler import im.vector.app.features.notifications.PushRuleTriggerListener import im.vector.app.features.session.SessionListener -import org.matrix.android.sdk.api.auth.AuthenticationService import org.matrix.android.sdk.api.session.Session import timber.log.Timber import java.util.concurrent.atomic.AtomicReference @@ -31,8 +30,7 @@ import javax.inject.Inject import javax.inject.Singleton @Singleton -class ActiveSessionHolder @Inject constructor(private val authenticationService: AuthenticationService, - private val sessionObservableStore: ActiveSessionDataSource, +class ActiveSessionHolder @Inject constructor(private val sessionObservableStore: ActiveSessionDataSource, private val keyRequestHandler: KeyRequestHandler, private val incomingVerificationRequestHandler: IncomingVerificationRequestHandler, private val callManager: WebRtcCallManager, diff --git a/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt b/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt index 5e4137053e..eff484fb9a 100644 --- a/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt +++ b/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt @@ -45,6 +45,10 @@ import im.vector.app.features.crypto.verification.emoji.VerificationEmojiCodeFra import im.vector.app.features.crypto.verification.qrconfirmation.VerificationQRWaitingFragment import im.vector.app.features.crypto.verification.qrconfirmation.VerificationQrScannedByOtherFragment import im.vector.app.features.crypto.verification.request.VerificationRequestFragment +import im.vector.app.features.devtools.RoomDevToolEditFragment +import im.vector.app.features.devtools.RoomDevToolFragment +import im.vector.app.features.devtools.RoomDevToolSendFormFragment +import im.vector.app.features.devtools.RoomDevToolStateEventListFragment import im.vector.app.features.discovery.DiscoverySettingsFragment import im.vector.app.features.discovery.change.SetIdentityServerFragment import im.vector.app.features.grouplist.GroupListFragment @@ -594,4 +598,24 @@ interface FragmentModule { @IntoMap @FragmentKey(ShowUserCodeFragment::class) fun bindShowUserCodeFragment(fragment: ShowUserCodeFragment): Fragment + + @Binds + @IntoMap + @FragmentKey(RoomDevToolFragment::class) + fun bindRoomDevToolFragment(fragment: RoomDevToolFragment): Fragment + + @Binds + @IntoMap + @FragmentKey(RoomDevToolStateEventListFragment::class) + fun bindRoomDevToolStateEventListFragment(fragment: RoomDevToolStateEventListFragment): Fragment + + @Binds + @IntoMap + @FragmentKey(RoomDevToolEditFragment::class) + fun bindRoomDevToolEditFragment(fragment: RoomDevToolEditFragment): Fragment + + @Binds + @IntoMap + @FragmentKey(RoomDevToolSendFormFragment::class) + fun bindRoomDevToolSendFormFragment(fragment: RoomDevToolSendFormFragment): Fragment } diff --git a/vector/src/main/java/im/vector/app/core/di/ScreenComponent.kt b/vector/src/main/java/im/vector/app/core/di/ScreenComponent.kt index 1a4ddd0c83..a1de892c4e 100644 --- a/vector/src/main/java/im/vector/app/core/di/ScreenComponent.kt +++ b/vector/src/main/java/im/vector/app/core/di/ScreenComponent.kt @@ -36,6 +36,7 @@ import im.vector.app.features.crypto.quads.SharedSecureStorageActivity import im.vector.app.features.crypto.recover.BootstrapBottomSheet import im.vector.app.features.crypto.verification.VerificationBottomSheet import im.vector.app.features.debug.DebugMenuActivity +import im.vector.app.features.devtools.RoomDevToolActivity import im.vector.app.features.home.HomeActivity import im.vector.app.features.home.HomeModule import im.vector.app.features.home.room.detail.RoomDetailActivity @@ -149,6 +150,7 @@ interface ScreenComponent { fun inject(activity: UserCodeActivity) fun inject(activity: CallTransferActivity) fun inject(activity: ReAuthActivity) + fun inject(activity: RoomDevToolActivity) /* ========================================================================================== * BottomSheets diff --git a/vector/src/main/java/im/vector/app/core/dialogs/ExportKeysDialog.kt b/vector/src/main/java/im/vector/app/core/dialogs/ExportKeysDialog.kt index e137eb1b70..23018fe758 100644 --- a/vector/src/main/java/im/vector/app/core/dialogs/ExportKeysDialog.kt +++ b/vector/src/main/java/im/vector/app/core/dialogs/ExportKeysDialog.kt @@ -61,7 +61,7 @@ class ExportKeysDialog { passwordVisible = !passwordVisible views.exportDialogEt.showPassword(passwordVisible) views.exportDialogEtConfirm.showPassword(passwordVisible) - views.exportDialogShowPassword.setImageResource(if (passwordVisible) R.drawable.ic_eye_closed else R.drawable.ic_eye) + views.exportDialogShowPassword.render(passwordVisible) } val exportDialog = builder.show() diff --git a/vector/src/main/java/im/vector/app/core/dialogs/PromptPasswordDialog.kt b/vector/src/main/java/im/vector/app/core/dialogs/PromptPasswordDialog.kt index 6d7b721976..1839a8b11c 100644 --- a/vector/src/main/java/im/vector/app/core/dialogs/PromptPasswordDialog.kt +++ b/vector/src/main/java/im/vector/app/core/dialogs/PromptPasswordDialog.kt @@ -44,7 +44,7 @@ class PromptPasswordDialog { views.promptPasswordPasswordReveal.setOnClickListener { passwordVisible = !passwordVisible views.promptPassword.showPassword(passwordVisible) - views.promptPasswordPasswordReveal.setImageResource(if (passwordVisible) R.drawable.ic_eye_closed else R.drawable.ic_eye) + views.promptPasswordPasswordReveal.render(passwordVisible) } AlertDialog.Builder(activity) diff --git a/vector/src/main/java/im/vector/app/core/epoxy/profiles/BaseProfileMatrixItem.kt b/vector/src/main/java/im/vector/app/core/epoxy/profiles/BaseProfileMatrixItem.kt index aa8075819e..ccb3bea25a 100644 --- a/vector/src/main/java/im/vector/app/core/epoxy/profiles/BaseProfileMatrixItem.kt +++ b/vector/src/main/java/im/vector/app/core/epoxy/profiles/BaseProfileMatrixItem.kt @@ -21,7 +21,6 @@ import androidx.core.view.isVisible import com.airbnb.epoxy.EpoxyAttribute import im.vector.app.core.epoxy.VectorEpoxyModel import im.vector.app.core.extensions.setTextOrHide -import im.vector.app.features.crypto.util.toImageRes import im.vector.app.features.home.AvatarRenderer import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel import org.matrix.android.sdk.api.util.MatrixItem @@ -47,6 +46,6 @@ abstract class BaseProfileMatrixItem : VectorEpoxy holder.subtitleView.setTextOrHide(matrixId) holder.editableView.isVisible = editable avatarRenderer.render(matrixItem, holder.avatarImageView) - holder.avatarDecorationImageView.setImageResource(userEncryptionTrustLevel.toImageRes()) + holder.avatarDecorationImageView.render(userEncryptionTrustLevel) } } diff --git a/vector/src/main/java/im/vector/app/core/epoxy/profiles/ProfileMatrixItem.kt b/vector/src/main/java/im/vector/app/core/epoxy/profiles/ProfileMatrixItem.kt index e8fc671d33..6eb06722b9 100644 --- a/vector/src/main/java/im/vector/app/core/epoxy/profiles/ProfileMatrixItem.kt +++ b/vector/src/main/java/im/vector/app/core/epoxy/profiles/ProfileMatrixItem.kt @@ -23,6 +23,7 @@ import android.widget.TextView import com.airbnb.epoxy.EpoxyModelClass import im.vector.app.R import im.vector.app.core.epoxy.VectorEpoxyHolder +import im.vector.app.core.ui.views.ShieldImageView @EpoxyModelClass(layout = R.layout.item_profile_matrix_item) abstract class ProfileMatrixItem : BaseProfileMatrixItem() { @@ -31,7 +32,7 @@ abstract class ProfileMatrixItem : BaseProfileMatrixItem(R.id.matrixItemTitle) val subtitleView by bind(R.id.matrixItemSubtitle) val avatarImageView by bind(R.id.matrixItemAvatar) - val avatarDecorationImageView by bind(R.id.matrixItemAvatarDecoration) + val avatarDecorationImageView by bind(R.id.matrixItemAvatarDecoration) val editableView by bind(R.id.matrixItemEditable) } } diff --git a/vector/src/main/java/im/vector/app/core/extensions/LiveData.kt b/vector/src/main/java/im/vector/app/core/extensions/LiveData.kt index 192953f609..588063e2a4 100644 --- a/vector/src/main/java/im/vector/app/core/extensions/LiveData.kt +++ b/vector/src/main/java/im/vector/app/core/extensions/LiveData.kt @@ -19,17 +19,16 @@ package im.vector.app.core.extensions import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.Observer import im.vector.app.core.utils.EventObserver import im.vector.app.core.utils.FirstThrottler import im.vector.app.core.utils.LiveEvent inline fun LiveData.observeK(owner: LifecycleOwner, crossinline observer: (T?) -> Unit) { - this.observe(owner, Observer { observer(it) }) + this.observe(owner, { observer(it) }) } inline fun LiveData.observeNotNull(owner: LifecycleOwner, crossinline observer: (T) -> Unit) { - this.observe(owner, Observer { it?.run(observer) }) + this.observe(owner, { it?.run(observer) }) } inline fun LiveData>.observeEvent(owner: LifecycleOwner, crossinline observer: (T) -> Unit) { diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt b/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt index 5747f5158d..43c96d2468 100644 --- a/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt +++ b/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt @@ -40,7 +40,6 @@ import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentFactory import androidx.fragment.app.FragmentManager -import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProvider import androidx.viewbinding.ViewBinding import com.bumptech.glide.util.Util @@ -208,12 +207,12 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScr navigator = screenComponent.navigator() activeSessionHolder = screenComponent.activeSessionHolder() vectorPreferences = vectorComponent.vectorPreferences() - configurationViewModel.activityRestarter.observe(this, Observer { + configurationViewModel.activityRestarter.observe(this) { if (!it.hasBeenHandled) { // Recreate the Activity because configuration has changed restart() } - }) + } pinLocker.getLiveState().observeNotNull(this) { if (this@VectorBaseActivity !is UnlockedActivity && it == PinLocker.State.LOCKED) { navigator.openPinCode(this, pinStartForActivityResult, PinMode.AUTH) diff --git a/vector/src/main/java/im/vector/app/core/ui/list/GenericItem.kt b/vector/src/main/java/im/vector/app/core/ui/list/GenericItem.kt index 492df9eb00..3a1337e78c 100644 --- a/vector/src/main/java/im/vector/app/core/ui/list/GenericItem.kt +++ b/vector/src/main/java/im/vector/app/core/ui/list/GenericItem.kt @@ -48,7 +48,7 @@ abstract class GenericItem : VectorEpoxyModel() { } @EpoxyAttribute - var title: String? = null + var title: CharSequence? = null @EpoxyAttribute var description: CharSequence? = null diff --git a/vector/src/main/java/im/vector/app/core/ui/views/KnownCallsViewHolder.kt b/vector/src/main/java/im/vector/app/core/ui/views/KnownCallsViewHolder.kt index 3bd9ce713d..628356fcad 100644 --- a/vector/src/main/java/im/vector/app/core/ui/views/KnownCallsViewHolder.kt +++ b/vector/src/main/java/im/vector/app/core/ui/views/KnownCallsViewHolder.kt @@ -89,7 +89,7 @@ class KnownCallsViewHolder { this.pipWrapper = pipWrapper this.currentCallsView?.callback = interactionListener pipWrapper.setOnClickListener( - DebouncedClickListener({ _ -> + DebouncedClickListener({ interactionListener.onTapToReturnToCall() }) ) diff --git a/vector/src/main/java/im/vector/app/core/ui/views/ReadReceiptsView.kt b/vector/src/main/java/im/vector/app/core/ui/views/ReadReceiptsView.kt index 5fb0e5907d..8f8919fd47 100644 --- a/vector/src/main/java/im/vector/app/core/ui/views/ReadReceiptsView.kt +++ b/vector/src/main/java/im/vector/app/core/ui/views/ReadReceiptsView.kt @@ -56,6 +56,7 @@ class ReadReceiptsView @JvmOverloads constructor( private fun setupView() { inflate(context, R.layout.view_read_receipts, this) + contentDescription = context.getString(R.string.a11y_view_read_receipts) } fun render(readReceipts: List, avatarRenderer: AvatarRenderer, clickListener: OnClickListener) { diff --git a/vector/src/main/java/im/vector/app/core/ui/views/RevealPasswordImageView.kt b/vector/src/main/java/im/vector/app/core/ui/views/RevealPasswordImageView.kt new file mode 100644 index 0000000000..b4bcecad16 --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/ui/views/RevealPasswordImageView.kt @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.core.ui.views + +import android.content.Context +import android.util.AttributeSet +import androidx.appcompat.widget.AppCompatImageView +import im.vector.app.R + +class RevealPasswordImageView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : AppCompatImageView(context, attrs, defStyleAttr) { + + init { + render(false) + } + + fun render(isPasswordShown: Boolean) { + if (isPasswordShown) { + contentDescription = context.getString(R.string.a11y_hide_password) + setImageResource(R.drawable.ic_eye_closed) + } else { + contentDescription = context.getString(R.string.a11y_show_password) + setImageResource(R.drawable.ic_eye) + } + } +} diff --git a/vector/src/main/java/im/vector/app/core/ui/views/ShieldImageView.kt b/vector/src/main/java/im/vector/app/core/ui/views/ShieldImageView.kt new file mode 100644 index 0000000000..8b127307ce --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/ui/views/ShieldImageView.kt @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.core.ui.views + +import android.content.Context +import android.util.AttributeSet +import androidx.annotation.DrawableRes +import androidx.appcompat.widget.AppCompatImageView +import androidx.core.view.isVisible +import im.vector.app.R +import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel + +class ShieldImageView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : AppCompatImageView(context, attrs, defStyleAttr) { + + init { + if (isInEditMode) { + render(RoomEncryptionTrustLevel.Trusted) + } + } + + fun render(roomEncryptionTrustLevel: RoomEncryptionTrustLevel?) { + isVisible = roomEncryptionTrustLevel != null + + when (roomEncryptionTrustLevel) { + RoomEncryptionTrustLevel.Default -> { + contentDescription = context.getString(R.string.a11y_trust_level_default) + setImageResource(R.drawable.ic_shield_black) + } + RoomEncryptionTrustLevel.Warning -> { + contentDescription = context.getString(R.string.a11y_trust_level_warning) + setImageResource(R.drawable.ic_shield_warning) + } + RoomEncryptionTrustLevel.Trusted -> { + contentDescription = context.getString(R.string.a11y_trust_level_trusted) + setImageResource(R.drawable.ic_shield_trusted) + } + } + } +} + +@DrawableRes +fun RoomEncryptionTrustLevel.toDrawableRes(): Int { + return when (this) { + RoomEncryptionTrustLevel.Default -> R.drawable.ic_shield_black + RoomEncryptionTrustLevel.Warning -> R.drawable.ic_shield_warning + RoomEncryptionTrustLevel.Trusted -> R.drawable.ic_shield_trusted + } +} diff --git a/vector/src/main/java/im/vector/app/core/utils/CountUpTimer.kt b/vector/src/main/java/im/vector/app/core/utils/CountUpTimer.kt index 2a9482765c..c812d8d644 100644 --- a/vector/src/main/java/im/vector/app/core/utils/CountUpTimer.kt +++ b/vector/src/main/java/im/vector/app/core/utils/CountUpTimer.kt @@ -27,8 +27,8 @@ class CountUpTimer(private val intervalInMs: Long) { private val resumed: AtomicBoolean = AtomicBoolean(false) private val disposable = Observable.interval(intervalInMs, TimeUnit.MILLISECONDS) - .filter { _ -> resumed.get() } - .doOnNext { _ -> elapsedTime.addAndGet(intervalInMs) } + .filter { resumed.get() } + .doOnNext { elapsedTime.addAndGet(intervalInMs) } .subscribe { tickListener?.onTick(elapsedTime.get()) } diff --git a/vector/src/main/java/im/vector/app/core/utils/DataSource.kt b/vector/src/main/java/im/vector/app/core/utils/DataSource.kt index 06bdeb9277..fc4ee330bb 100644 --- a/vector/src/main/java/im/vector/app/core/utils/DataSource.kt +++ b/vector/src/main/java/im/vector/app/core/utils/DataSource.kt @@ -49,7 +49,7 @@ open class BehaviorDataSource(private val defaultValue: T? = null) : MutableD private fun createRelay(): BehaviorRelay { return if (defaultValue == null) { - BehaviorRelay.create() + BehaviorRelay.create() } else { BehaviorRelay.createDefault(defaultValue) } diff --git a/vector/src/main/java/im/vector/app/core/utils/DefaultSubscriber.kt b/vector/src/main/java/im/vector/app/core/utils/DefaultSubscriber.kt index 99c28becbc..a82e5a4e03 100644 --- a/vector/src/main/java/im/vector/app/core/utils/DefaultSubscriber.kt +++ b/vector/src/main/java/im/vector/app/core/utils/DefaultSubscriber.kt @@ -19,12 +19,11 @@ package im.vector.app.core.utils import io.reactivex.Completable import io.reactivex.Single import io.reactivex.disposables.Disposable -import io.reactivex.functions.Consumer import io.reactivex.internal.functions.Functions import timber.log.Timber fun Single.subscribeLogError(): Disposable { - return subscribe(Functions.emptyConsumer(), Consumer { Timber.e(it) }) + return subscribe(Functions.emptyConsumer(), { Timber.e(it) }) } fun Completable.subscribeLogError(): Disposable { diff --git a/vector/src/main/java/im/vector/app/features/auth/PromptFragment.kt b/vector/src/main/java/im/vector/app/features/auth/PromptFragment.kt index 917f60dacb..4bdd54ae45 100644 --- a/vector/src/main/java/im/vector/app/features/auth/PromptFragment.kt +++ b/vector/src/main/java/im/vector/app/features/auth/PromptFragment.kt @@ -87,14 +87,7 @@ class PromptFragment : VectorBaseFragment() { } views.passwordField.showPassword(it.passwordVisible) - - if (it.passwordVisible) { - views.passwordReveal.setImageResource(R.drawable.ic_eye_closed) - views.passwordReveal.contentDescription = getString(R.string.a11y_hide_password) - } else { - views.passwordReveal.setImageResource(R.drawable.ic_eye) - views.passwordReveal.contentDescription = getString(R.string.a11y_show_password) - } + views.passwordReveal.render(it.passwordVisible) if (it.lastErrorCode != null) { when (it.flowType) { diff --git a/vector/src/main/java/im/vector/app/features/autocomplete/RecyclerViewPresenter.kt b/vector/src/main/java/im/vector/app/features/autocomplete/RecyclerViewPresenter.kt index c4631300f1..7625eb6216 100644 --- a/vector/src/main/java/im/vector/app/features/autocomplete/RecyclerViewPresenter.kt +++ b/vector/src/main/java/im/vector/app/features/autocomplete/RecyclerViewPresenter.kt @@ -102,7 +102,7 @@ abstract class RecyclerViewPresenter(context: Context?) : AutocompletePresent return LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) } - private class Observer internal constructor(private val root: DataSetObserver) : RecyclerView.AdapterDataObserver() { + private class Observer constructor(private val root: DataSetObserver) : RecyclerView.AdapterDataObserver() { override fun onChanged() { root.onChanged() } diff --git a/vector/src/main/java/im/vector/app/features/autocomplete/member/AutocompleteMemberPresenter.kt b/vector/src/main/java/im/vector/app/features/autocomplete/member/AutocompleteMemberPresenter.kt index b2ad0a7688..ecc607f08d 100644 --- a/vector/src/main/java/im/vector/app/features/autocomplete/member/AutocompleteMemberPresenter.kt +++ b/vector/src/main/java/im/vector/app/features/autocomplete/member/AutocompleteMemberPresenter.kt @@ -31,7 +31,7 @@ import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary class AutocompleteMemberPresenter @AssistedInject constructor(context: Context, @Assisted val roomId: String, - private val session: Session, + session: Session, private val controller: AutocompleteMemberController ) : RecyclerViewPresenter(context), AutocompleteClickListener { diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt index ab3769d1f3..091dbeec24 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt @@ -383,7 +383,7 @@ class VectorCallActivity : VectorBaseActivity(), CallContro mode: String?): Intent { return Intent(context, VectorCallActivity::class.java).apply { // what could be the best flags? - flags = Intent.FLAG_ACTIVITY_CLEAR_TOP + flags = FLAG_ACTIVITY_CLEAR_TOP putExtra(MvRx.KEY_ARG, CallArgs(roomId, callId, otherUserId, isIncomingCall, isVideoCall)) putExtra(EXTRA_MODE, mode) } diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreActivity.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreActivity.kt index 80ae46262d..20ecbb4e5a 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreActivity.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreActivity.kt @@ -19,7 +19,6 @@ import android.app.Activity import android.content.Context import android.content.Intent import androidx.appcompat.app.AlertDialog -import androidx.lifecycle.Observer import im.vector.app.R import im.vector.app.core.extensions.addFragmentToBackstack import im.vector.app.core.extensions.observeEvent @@ -54,7 +53,7 @@ class KeysBackupRestoreActivity : SimpleFragmentActivity() { viewModel = viewModelProvider.get(KeysBackupRestoreSharedViewModel::class.java) viewModel.initSession(session) - viewModel.keySourceModel.observe(this, Observer { keySource -> + viewModel.keySourceModel.observe(this) { keySource -> if (keySource != null && !keySource.isInQuadS && supportFragmentManager.fragments.isEmpty()) { val isBackupCreatedFromPassphrase = viewModel.keyVersionResult.value?.getAuthDataAsMegolmBackupAuthData()?.privateKeySalt != null @@ -64,7 +63,7 @@ class KeysBackupRestoreActivity : SimpleFragmentActivity() { replaceFragment(R.id.container, KeysBackupRestoreFromKeyFragment::class.java) } } - }) + } viewModel.keyVersionResultError.observeEvent(this) { message -> AlertDialog.Builder(this) @@ -111,9 +110,9 @@ class KeysBackupRestoreActivity : SimpleFragmentActivity() { } } - viewModel.loadingEvent.observe(this, Observer { + viewModel.loadingEvent.observe(this) { updateWaitingView(it) - }) + } viewModel.importRoomKeysFinishWithResult.observeEvent(this) { // set data? diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyFragment.kt index 2887a29776..1f8cf4d3a0 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyFragment.kt @@ -22,7 +22,6 @@ import android.view.View import android.view.ViewGroup import android.view.inputmethod.EditorInfo import androidx.core.widget.doOnTextChanged -import androidx.lifecycle.Observer import im.vector.app.R import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.platform.VectorBaseFragment @@ -56,9 +55,9 @@ class KeysBackupRestoreFromKeyFragment @Inject constructor() } views.keyInputLayout.error = viewModel.recoveryCodeErrorText.value - viewModel.recoveryCodeErrorText.observe(viewLifecycleOwner, Observer { newValue -> + viewModel.recoveryCodeErrorText.observe(viewLifecycleOwner) { newValue -> views.keyInputLayout.error = newValue - }) + } views.keysRestoreButton.setOnClickListener { onRestoreFromKey() } views.keysBackupImport.setOnClickListener { onImport() } diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseFragment.kt index 6c139aaf45..0f0c9a23c7 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseFragment.kt @@ -24,7 +24,6 @@ import android.view.ViewGroup import android.view.inputmethod.EditorInfo import androidx.core.text.set import androidx.core.widget.doOnTextChanged -import androidx.lifecycle.Observer import im.vector.app.R import im.vector.app.core.extensions.showPassword import im.vector.app.core.platform.VectorBaseFragment @@ -51,17 +50,17 @@ class KeysBackupRestoreFromPassphraseFragment @Inject constructor() : VectorBase viewModel = fragmentViewModelProvider.get(KeysBackupRestoreFromPassphraseViewModel::class.java) sharedViewModel = activityViewModelProvider.get(KeysBackupRestoreSharedViewModel::class.java) - viewModel.passphraseErrorText.observe(viewLifecycleOwner, Observer { newValue -> + viewModel.passphraseErrorText.observe(viewLifecycleOwner) { newValue -> views.keysBackupPassphraseEnterTil.error = newValue - }) + } views.helperTextWithLink.text = spannableStringForHelperText() - viewModel.showPasswordMode.observe(viewLifecycleOwner, Observer { + viewModel.showPasswordMode.observe(viewLifecycleOwner) { val shouldBeVisible = it ?: false views.keysBackupPassphraseEnterEdittext.showPassword(shouldBeVisible) - views.keysBackupViewShowPassword.setImageResource(if (shouldBeVisible) R.drawable.ic_eye_closed else R.drawable.ic_eye) - }) + views.keysBackupViewShowPassword.render(shouldBeVisible) + } views.keysBackupPassphraseEnterEdittext.setOnEditorActionListener { _, actionId, _ -> if (actionId == EditorInfo.IME_ACTION_DONE) { diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupActivity.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupActivity.kt index ab8e725959..e042459437 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupActivity.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupActivity.kt @@ -21,7 +21,6 @@ import android.content.Intent import androidx.appcompat.app.AlertDialog import androidx.core.view.isVisible import androidx.fragment.app.FragmentManager -import androidx.lifecycle.Observer import im.vector.app.R import im.vector.app.core.dialogs.ExportKeysDialog import im.vector.app.core.extensions.observeEvent @@ -49,20 +48,20 @@ class KeysBackupSetupActivity : SimpleFragmentActivity() { viewModel.showManualExport.value = intent.getBooleanExtra(EXTRA_SHOW_MANUAL_EXPORT, false) viewModel.initSession(session) - viewModel.isCreatingBackupVersion.observe(this, Observer { + viewModel.isCreatingBackupVersion.observe(this) { val isCreating = it ?: false if (isCreating) { showWaitingView() } else { hideWaitingView() } - }) + } - viewModel.loadingStatus.observe(this, Observer { + viewModel.loadingStatus.observe(this) { it?.let { updateWaitingView(it) } - }) + } viewModel.navigateEvent.observeEvent(this) { uxStateEvent -> when (uxStateEvent) { @@ -99,7 +98,7 @@ class KeysBackupSetupActivity : SimpleFragmentActivity() { } } - viewModel.prepareRecoverFailError.observe(this, Observer { error -> + viewModel.prepareRecoverFailError.observe(this) { error -> if (error != null) { AlertDialog.Builder(this) .setTitle(R.string.unknown_error) @@ -110,9 +109,9 @@ class KeysBackupSetupActivity : SimpleFragmentActivity() { } .show() } - }) + } - viewModel.creatingBackupError.observe(this, Observer { error -> + viewModel.creatingBackupError.observe(this) { error -> if (error != null) { AlertDialog.Builder(this) .setTitle(R.string.unexpected_error) @@ -123,7 +122,7 @@ class KeysBackupSetupActivity : SimpleFragmentActivity() { } .show() } - }) + } } private val saveStartForActivityResult = registerStartForActivityResult { activityResult -> diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep1Fragment.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep1Fragment.kt index 44996a159c..cf65a9942c 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep1Fragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep1Fragment.kt @@ -20,7 +20,6 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.lifecycle.Observer import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.LiveEvent import im.vector.app.databinding.FragmentKeysBackupSetupStep1Binding @@ -40,12 +39,12 @@ class KeysBackupSetupStep1Fragment @Inject constructor() : VectorBaseFragment + viewModel.passwordStrength.observe(viewLifecycleOwner) { strength -> if (strength == null) { views.keysBackupSetupStep2PassphraseStrengthLevel.strength = 0 views.keysBackupSetupStep2PassphraseEnterTil.error = null @@ -91,9 +89,9 @@ class KeysBackupSetupStep2Fragment @Inject constructor() : VectorBaseFragment { newValue -> + viewModel.passphrase.observe(viewLifecycleOwner) { newValue -> if (newValue.isEmpty()) { viewModel.passwordStrength.value = null } else { @@ -104,28 +102,28 @@ class KeysBackupSetupStep2Fragment @Inject constructor() : VectorBaseFragment if (actionId == EditorInfo.IME_ACTION_DONE) { @@ -141,8 +139,8 @@ class KeysBackupSetupStep2Fragment @Inject constructor() : VectorBaseFragment onPassphraseChanged() } - views.keysBackupSetupStep2PassphraseConfirmEditText.doOnTextChanged { _, _, _, _ -> onConfirmPassphraseChanged() } + views.keysBackupSetupStep2PassphraseEnterEdittext.doOnTextChanged { _, _, _, _ -> onPassphraseChanged() } + views.keysBackupSetupStep2PassphraseConfirmEditText.doOnTextChanged { _, _, _, _ -> onConfirmPassphraseChanged() } } private fun toggleVisibilityMode() { diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep3Fragment.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep3Fragment.kt index a41de109b0..9a3b5fa874 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep3Fragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep3Fragment.kt @@ -25,7 +25,6 @@ import android.widget.TextView import android.widget.Toast import androidx.appcompat.app.AlertDialog import androidx.core.view.isVisible -import androidx.lifecycle.Observer import arrow.core.Try import com.google.android.material.bottomsheet.BottomSheetDialog import im.vector.app.R @@ -61,7 +60,7 @@ class KeysBackupSetupStep3Fragment @Inject constructor() : VectorBaseFragment val shouldBeVisible = state.passphraseVisible views.ssssPassphraseEnterEdittext.showPassword(shouldBeVisible) - views.ssssViewShowPassword.setImageResource(if (shouldBeVisible) R.drawable.ic_eye_closed else R.drawable.ic_eye) + views.ssssViewShowPassword.render(shouldBeVisible) } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BackupToQuadSMigrationTask.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BackupToQuadSMigrationTask.kt index bbdd0fd5c5..8fbef016cf 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BackupToQuadSMigrationTask.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BackupToQuadSMigrationTask.kt @@ -97,7 +97,7 @@ class BackupToQuadSMigrationTask @Inject constructor( when { params.passphrase?.isNotEmpty() == true -> { reportProgress(params, R.string.bootstrap_progress_generating_ssss) - awaitCallback { + awaitCallback { quadS.generateKeyWithPassphrase( UUID.randomUUID().toString(), "ssss_key", diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapConfirmPassphraseFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapConfirmPassphraseFragment.kt index 9862a97db2..2d26436556 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapConfirmPassphraseFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapConfirmPassphraseFragment.kt @@ -109,7 +109,7 @@ class BootstrapConfirmPassphraseFragment @Inject constructor() if (state.step is BootstrapStep.ConfirmPassphrase) { val isPasswordVisible = state.step.isPasswordVisible views.ssssPassphraseEnterEdittext.showPassword(isPasswordVisible, updateCursor = false) - views.ssssViewShowPassword.setImageResource(if (isPasswordVisible) R.drawable.ic_eye_closed else R.drawable.ic_eye) + views.ssssViewShowPassword.render(isPasswordVisible) } } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapEnterPassphraseFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapEnterPassphraseFragment.kt index 9d3f26cba8..7a5d6e5fd7 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapEnterPassphraseFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapEnterPassphraseFragment.kt @@ -103,7 +103,7 @@ class BootstrapEnterPassphraseFragment @Inject constructor() if (state.step is BootstrapStep.SetupPassphrase) { val isPasswordVisible = state.step.isPasswordVisible views.ssssPassphraseEnterEdittext.showPassword(isPasswordVisible, updateCursor = false) - views.ssssViewShowPassword.setImageResource(if (isPasswordVisible) R.drawable.ic_eye_closed else R.drawable.ic_eye) + views.ssssViewShowPassword.render(isPasswordVisible) state.passphraseStrength.invoke()?.let { strength -> val score = strength.score diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapMigrateBackupFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapMigrateBackupFragment.kt index 99507fd1c7..ca0942f59a 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapMigrateBackupFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapMigrateBackupFragment.kt @@ -133,7 +133,7 @@ class BootstrapMigrateBackupFragment @Inject constructor( if (state.step is BootstrapStep.GetBackupSecretPassForMigration) { val isPasswordVisible = state.step.isPasswordVisible views.bootstrapMigrateEditText.showPassword(isPasswordVisible, updateCursor = false) - views.bootstrapMigrateShowPassword.setImageResource(if (isPasswordVisible) R.drawable.ic_eye_closed else R.drawable.ic_eye) + views.bootstrapMigrateShowPassword.render(isPasswordVisible) } views.bootstrapDescriptionText.text = getString(R.string.bootstrap_migration_enter_backup_password) diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSaveRecoveryKeyFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSaveRecoveryKeyFragment.kt index f587f5e58f..d5e7305e11 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSaveRecoveryKeyFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSaveRecoveryKeyFragment.kt @@ -62,8 +62,7 @@ class BootstrapSaveRecoveryKeyFragment @Inject constructor( } } - private fun downloadRecoveryKey() = withState(sharedViewModel) { _ -> - + private fun downloadRecoveryKey() { val intent = Intent(Intent.ACTION_CREATE_DOCUMENT) intent.addCategory(Intent.CATEGORY_OPENABLE) intent.type = "text/plain" diff --git a/vector/src/main/java/im/vector/app/features/crypto/util/Extensions.kt b/vector/src/main/java/im/vector/app/features/crypto/util/Extensions.kt deleted file mode 100644 index c75bcb252b..0000000000 --- a/vector/src/main/java/im/vector/app/features/crypto/util/Extensions.kt +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2020 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package im.vector.app.features.crypto.util - -import androidx.annotation.DrawableRes -import im.vector.app.R -import im.vector.app.core.extensions.exhaustive -import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel - -@DrawableRes -fun RoomEncryptionTrustLevel?.toImageRes(): Int { - return when (this) { - null -> 0 - RoomEncryptionTrustLevel.Default -> R.drawable.ic_shield_black - RoomEncryptionTrustLevel.Warning -> R.drawable.ic_shield_warning - RoomEncryptionTrustLevel.Trusted -> R.drawable.ic_shield_trusted - }.exhaustive -} diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt index b7f967e592..48f3f0a460 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt @@ -92,13 +92,11 @@ class IncomingVerificationRequestHandler @Inject constructor( } addButton( context.getString(R.string.ignore), - Runnable { - tx.cancel() - } + { tx.cancel() } ) addButton( context.getString(R.string.action_open), - Runnable { + { (weakCurrentActivity?.get() as? VectorBaseActivity<*>)?.let { it.navigator.performDeviceVerification(it, tx.otherUserId, tx.transactionId) } diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheet.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheet.kt index eb20e815f6..8d15249c11 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheet.kt @@ -24,7 +24,6 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.appcompat.app.AlertDialog -import androidx.core.view.isVisible import androidx.fragment.app.Fragment import com.airbnb.mvrx.MvRx import com.airbnb.mvrx.fragmentViewModel @@ -49,6 +48,7 @@ import im.vector.app.features.crypto.verification.request.VerificationRequestFra import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.settings.VectorSettingsActivity import kotlinx.parcelize.Parcelize +import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME @@ -162,23 +162,22 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() { @EpoxyAttribute - var imageRes: Int = 0 - - @EpoxyAttribute - var contentDescription: String? = null + lateinit var roomEncryptionTrustLevel: RoomEncryptionTrustLevel override fun bind(holder: Holder) { super.bind(holder) - holder.image.setImageResource(imageRes) - - if (contentDescription == null) { - ViewCompat.setImportantForAccessibility(holder.image, ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO) - } else { - ViewCompat.setImportantForAccessibility(holder.image, ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES) - holder.image.contentDescription = contentDescription - } + holder.image.render(roomEncryptionTrustLevel) } class Holder : VectorEpoxyHolder() { - val image by bind(R.id.itemVerificationBigImage) + val image by bind(R.id.itemVerificationBigImage) } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/qrconfirmation/VerificationQRWaitingController.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/qrconfirmation/VerificationQRWaitingController.kt index 8cb5068b63..4b7452b511 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/qrconfirmation/VerificationQRWaitingController.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/qrconfirmation/VerificationQRWaitingController.kt @@ -23,6 +23,7 @@ import im.vector.app.core.resources.StringProvider import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationBigImageItem import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationNoticeItem import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationWaitingItem +import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel import javax.inject.Inject class VerificationQRWaitingController @Inject constructor( @@ -49,7 +50,7 @@ class VerificationQRWaitingController @Inject constructor( bottomSheetVerificationBigImageItem { id("image") - imageRes(R.drawable.ic_shield_trusted) + roomEncryptionTrustLevel(RoomEncryptionTrustLevel.Trusted) } bottomSheetVerificationWaitingItem { diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/qrconfirmation/VerificationQrScannedByOtherController.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/qrconfirmation/VerificationQrScannedByOtherController.kt index cabce6410d..da382f75f1 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/qrconfirmation/VerificationQrScannedByOtherController.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/qrconfirmation/VerificationQrScannedByOtherController.kt @@ -25,6 +25,7 @@ import im.vector.app.features.crypto.verification.VerificationBottomSheetViewSta import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationActionItem import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationBigImageItem import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationNoticeItem +import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel import javax.inject.Inject class VerificationQrScannedByOtherController @Inject constructor( @@ -58,7 +59,7 @@ class VerificationQrScannedByOtherController @Inject constructor( bottomSheetVerificationBigImageItem { id("image") - imageRes(R.drawable.ic_shield_trusted) + roomEncryptionTrustLevel(RoomEncryptionTrustLevel.Trusted) } dividerItem { diff --git a/vector/src/main/java/im/vector/app/features/devtools/DevToolsInteractionListener.kt b/vector/src/main/java/im/vector/app/features/devtools/DevToolsInteractionListener.kt new file mode 100644 index 0000000000..e1e6f6b7cb --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/devtools/DevToolsInteractionListener.kt @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.devtools + +interface DevToolsInteractionListener { + fun processAction(action: RoomDevToolAction) +} diff --git a/vector/src/main/java/im/vector/app/features/devtools/DevToolsViewEvents.kt b/vector/src/main/java/im/vector/app/features/devtools/DevToolsViewEvents.kt new file mode 100644 index 0000000000..96aed20dc4 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/devtools/DevToolsViewEvents.kt @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.devtools + +import im.vector.app.core.platform.VectorViewEvents + +sealed class DevToolsViewEvents : VectorViewEvents { + object Dismiss : DevToolsViewEvents() + + // object ShowStateList : DevToolsViewEvents() + data class ShowAlertMessage(val message: String) : DevToolsViewEvents() + data class ShowSnackMessage(val message: String) : DevToolsViewEvents() +} diff --git a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolAction.kt b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolAction.kt new file mode 100644 index 0000000000..c6246bbe08 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolAction.kt @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.devtools + +import im.vector.app.core.platform.VectorViewModelAction +import org.matrix.android.sdk.api.session.events.model.Event + +sealed class RoomDevToolAction : VectorViewModelAction { + object ExploreRoomState : RoomDevToolAction() + object OnBackPressed : RoomDevToolAction() + object MenuEdit : RoomDevToolAction() + object MenuItemSend : RoomDevToolAction() + data class ShowStateEvent(val event: Event) : RoomDevToolAction() + data class ShowStateEventType(val stateEventType: String) : RoomDevToolAction() + data class UpdateContentText(val contentJson: String) : RoomDevToolAction() + data class SendCustomEvent(val isStateEvent: Boolean) : RoomDevToolAction() + data class CustomEventTypeChange(val type: String) : RoomDevToolAction() + data class CustomEventContentChange(val content: String) : RoomDevToolAction() + data class CustomEventStateKeyChange(val stateKey: String) : RoomDevToolAction() +} diff --git a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolActivity.kt b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolActivity.kt new file mode 100644 index 0000000000..31b495a4d4 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolActivity.kt @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.devtools + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.os.Parcelable +import android.view.Menu +import android.view.MenuItem +import androidx.appcompat.app.AlertDialog +import androidx.core.view.forEach +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentManager +import com.airbnb.mvrx.Fail +import com.airbnb.mvrx.Loading +import com.airbnb.mvrx.MvRx +import com.airbnb.mvrx.Success +import com.airbnb.mvrx.Uninitialized +import com.airbnb.mvrx.viewModel +import com.airbnb.mvrx.withState +import im.vector.app.R +import im.vector.app.core.di.ScreenComponent +import im.vector.app.core.extensions.exhaustive +import im.vector.app.core.extensions.replaceFragment +import im.vector.app.core.extensions.toMvRxBundle +import im.vector.app.core.platform.SimpleFragmentActivity +import im.vector.app.core.resources.ColorProvider +import im.vector.app.core.utils.createJSonViewerStyleProvider +import kotlinx.parcelize.Parcelize +import org.billcarsonfr.jsonviewer.JSonViewerFragment +import javax.inject.Inject + +class RoomDevToolActivity : SimpleFragmentActivity(), RoomDevToolViewModel.Factory, + FragmentManager.OnBackStackChangedListener { + + @Inject lateinit var viewModelFactory: RoomDevToolViewModel.Factory + @Inject lateinit var colorProvider: ColorProvider + + // private lateinit var viewModel: RoomDevToolViewModel + private val viewModel: RoomDevToolViewModel by viewModel() + + override fun getTitleRes() = R.string.dev_tools_menu_name + + override fun getMenuRes() = R.menu.menu_devtools + + private var currentDisplayMode: RoomDevToolViewState.Mode? = null + + @Parcelize + data class Args( + val roomId: String + ) : Parcelable + + override fun injectWith(injector: ScreenComponent) { + super.injectWith(injector) + injector.inject(this) + } + + override fun create(initialState: RoomDevToolViewState): RoomDevToolViewModel { + return viewModelFactory.create(initialState) + } + + override fun initUiAndData() { + super.initUiAndData() + viewModel.subscribe(this) { + renderState(it) + } + + viewModel.observeViewEvents { + when (it) { + DevToolsViewEvents.Dismiss -> finish() + is DevToolsViewEvents.ShowAlertMessage -> { + AlertDialog.Builder(this) + .setMessage(it.message) + .setPositiveButton(R.string.ok, null) + .show() + Unit + } + is DevToolsViewEvents.ShowSnackMessage -> showSnackbar(it.message) + }.exhaustive + } + supportFragmentManager.addOnBackStackChangedListener(this) + } + + private fun renderState(it: RoomDevToolViewState) { + if (it.displayMode != currentDisplayMode) { + when (it.displayMode) { + RoomDevToolViewState.Mode.Root -> { + val classJava = RoomDevToolFragment::class.java + val tag = classJava.name + if (supportFragmentManager.findFragmentByTag(tag) == null) { + replaceFragment(R.id.container, RoomDevToolFragment::class.java) + } else { + supportFragmentManager.popBackStack() + } + } + RoomDevToolViewState.Mode.StateEventDetail -> { + val frag = JSonViewerFragment.newInstance( + jsonString = it.selectedEventJson ?: "", + initialOpenDepth = -1, + wrap = true, + styleProvider = createJSonViewerStyleProvider(colorProvider) + ) + navigateTo(frag) + } + RoomDevToolViewState.Mode.StateEventList, + RoomDevToolViewState.Mode.StateEventListByType -> { + val frag = createFragment(RoomDevToolStateEventListFragment::class.java, Bundle().toMvRxBundle()) + navigateTo(frag) + } + RoomDevToolViewState.Mode.EditEventContent -> { + val frag = createFragment(RoomDevToolEditFragment::class.java, Bundle().toMvRxBundle()) + navigateTo(frag) + } + is RoomDevToolViewState.Mode.SendEventForm -> { + val frag = createFragment(RoomDevToolSendFormFragment::class.java, Bundle().toMvRxBundle()) + navigateTo(frag) + } + } + currentDisplayMode = it.displayMode + invalidateOptionsMenu() + } + + when (it.modalLoading) { + is Loading -> showWaitingView() + is Success -> hideWaitingView() + is Fail -> { + hideWaitingView() + } + Uninitialized -> { + } + } + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + if (item.itemId == android.R.id.home) { + onBackPressed() + return true + } + if (item.itemId == R.id.menuItemEdit) { + viewModel.handle(RoomDevToolAction.MenuEdit) + return true + } + if (item.itemId == R.id.menuItemSend) { + viewModel.handle(RoomDevToolAction.MenuItemSend) + return true + } + return super.onOptionsItemSelected(item) + } + + override fun onBackPressed() { + viewModel.handle(RoomDevToolAction.OnBackPressed) + } + + private fun navigateTo(fragment: Fragment) { + val tag = fragment.javaClass.name + if (supportFragmentManager.findFragmentByTag(tag) == null) { + supportFragmentManager.beginTransaction() + .setCustomAnimations(R.anim.fade_in, R.anim.fade_out, R.anim.fade_in, R.anim.fade_out) + .replace(R.id.container, fragment, tag) + .addToBackStack(tag) + .commit() + } else { + if (!supportFragmentManager.popBackStackImmediate(tag, 0)) { + supportFragmentManager.beginTransaction() + .setCustomAnimations(R.anim.fade_in, R.anim.fade_out, R.anim.fade_in, R.anim.fade_out) + .replace(R.id.container, fragment, tag) + .addToBackStack(tag) + .commit() + } + } + } + + override fun onDestroy() { + supportFragmentManager.removeOnBackStackChangedListener(this) + currentDisplayMode = null + super.onDestroy() + } + + override fun onPrepareOptionsMenu(menu: Menu?): Boolean = withState(viewModel) { state -> + menu?.forEach { + val isVisible = when (it.itemId) { + R.id.menuItemEdit -> { + state.displayMode is RoomDevToolViewState.Mode.StateEventDetail + } + R.id.menuItemSend -> { + state.displayMode is RoomDevToolViewState.Mode.EditEventContent + || state.displayMode is RoomDevToolViewState.Mode.SendEventForm + } + else -> true + } + it.isVisible = isVisible + } + return@withState true + } + + companion object { + + fun intent(context: Context, roomId: String): Intent { + return Intent(context, RoomDevToolActivity::class.java).apply { + putExtra(MvRx.KEY_ARG, Args(roomId)) + } + } + } + + override fun onBackStackChanged() = withState(viewModel) { state -> + updateToolBar(state) + } + + private fun updateToolBar(state: RoomDevToolViewState) { + val title = when (state.displayMode) { + RoomDevToolViewState.Mode.Root -> { + getString(getTitleRes()) + } + RoomDevToolViewState.Mode.StateEventList -> { + getString(R.string.dev_tools_state_event) + } + RoomDevToolViewState.Mode.StateEventDetail -> { + state.selectedEvent?.type + } + RoomDevToolViewState.Mode.EditEventContent -> { + getString(R.string.dev_tools_edit_content) + } + RoomDevToolViewState.Mode.StateEventListByType -> { + state.currentStateType ?: "" + } + is RoomDevToolViewState.Mode.SendEventForm -> { + getString( + if (state.displayMode.isState) R.string.dev_tools_send_custom_state_event + else R.string.dev_tools_send_custom_event + ) + } + } + + supportActionBar?.let { + it.title = title + } ?: run { + setTitle(title) + } + invalidateOptionsMenu() + } +} diff --git a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolEditFragment.kt b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolEditFragment.kt new file mode 100644 index 0000000000..98fe19a765 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolEditFragment.kt @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.devtools + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.airbnb.mvrx.activityViewModel +import com.airbnb.mvrx.withState +import com.jakewharton.rxbinding3.widget.textChanges +import im.vector.app.core.extensions.hideKeyboard +import im.vector.app.core.platform.VectorBaseFragment +import im.vector.app.databinding.FragmentDevtoolsEditorBinding +import javax.inject.Inject + +class RoomDevToolEditFragment @Inject constructor() + : VectorBaseFragment() { + + private val sharedViewModel: RoomDevToolViewModel by activityViewModel() + + override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentDevtoolsEditorBinding { + return FragmentDevtoolsEditorBinding.inflate(inflater, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + withState(sharedViewModel) { + views.editText.setText(it.editedContent ?: "{}") + } + views.editText.textChanges() + .skipInitialValue() + .subscribe { + sharedViewModel.handle(RoomDevToolAction.UpdateContentText(it.toString())) + } + .disposeOnDestroyView() + } + + override fun onResume() { + super.onResume() + views.editText.requestFocus() + } + + override fun onStop() { + super.onStop() + views.editText.hideKeyboard() + } +} diff --git a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolFragment.kt b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolFragment.kt new file mode 100644 index 0000000000..0cc2a69bcf --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolFragment.kt @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.devtools + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.airbnb.mvrx.activityViewModel +import im.vector.app.core.extensions.cleanup +import im.vector.app.core.extensions.configureWith +import im.vector.app.core.platform.VectorBaseFragment +import im.vector.app.databinding.FragmentGenericRecyclerBinding +import javax.inject.Inject + +class RoomDevToolFragment @Inject constructor( + private val epoxyController: RoomDevToolRootController +) : VectorBaseFragment(), + DevToolsInteractionListener { + + private val sharedViewModel: RoomDevToolViewModel by activityViewModel() + + override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentGenericRecyclerBinding { + return FragmentGenericRecyclerBinding.inflate(inflater, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + views.genericRecyclerView.configureWith(epoxyController, showDivider = true) + epoxyController.interactionListener = this + +// sharedViewModel.observeViewEvents { +// when (it) { +// is DevToolsViewEvents.showJson -> { +// JSonViewerDialog.newInstance(it.jsonString, -1, createJSonViewerStyleProvider(colorProvider)) +// .show(childFragmentManager, "JSON_VIEWER") +// +// } +// } +// } + } + + override fun onDestroyView() { + views.genericRecyclerView.cleanup() + epoxyController.interactionListener = null + super.onDestroyView() + } + + override fun processAction(action: RoomDevToolAction) { + sharedViewModel.handle(action) + } +} diff --git a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolRootController.kt b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolRootController.kt new file mode 100644 index 0000000000..785e0140ac --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolRootController.kt @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.devtools + +import android.view.View +import com.airbnb.epoxy.EpoxyController +import im.vector.app.R +import im.vector.app.core.resources.StringProvider +import im.vector.app.core.ui.list.genericButtonItem +import javax.inject.Inject + +class RoomDevToolRootController @Inject constructor( + private val stringProvider: StringProvider +) : EpoxyController() { + + init { + requestModelBuild() + } + + var interactionListener: DevToolsInteractionListener? = null + + override fun buildModels() { + genericButtonItem { + id("explore") + text(stringProvider.getString(R.string.dev_tools_explore_room_state)) + buttonClickAction(View.OnClickListener { + interactionListener?.processAction(RoomDevToolAction.ExploreRoomState) + }) + } + genericButtonItem { + id("send") + text(stringProvider.getString(R.string.dev_tools_send_custom_event)) + buttonClickAction(View.OnClickListener { + interactionListener?.processAction(RoomDevToolAction.SendCustomEvent(false)) + }) + } + genericButtonItem { + id("send_state") + text(stringProvider.getString(R.string.dev_tools_send_state_event)) + buttonClickAction(View.OnClickListener { + interactionListener?.processAction(RoomDevToolAction.SendCustomEvent(true)) + }) + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolSendFormController.kt b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolSendFormController.kt new file mode 100644 index 0000000000..e5b3fb737e --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolSendFormController.kt @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.devtools + +import com.airbnb.epoxy.TypedEpoxyController +import im.vector.app.R +import im.vector.app.core.resources.StringProvider +import im.vector.app.core.ui.list.genericFooterItem +import im.vector.app.features.form.formEditTextItem +import im.vector.app.features.form.formMultiLineEditTextItem +import javax.inject.Inject + +class RoomDevToolSendFormController @Inject constructor( + private val stringProvider: StringProvider +) : TypedEpoxyController() { + + var interactionListener: DevToolsInteractionListener? = null + + override fun buildModels(data: RoomDevToolViewState?) { + val sendEventForm = (data?.displayMode as? RoomDevToolViewState.Mode.SendEventForm) ?: return + + genericFooterItem { + id("topSpace") + text("") + } + formEditTextItem { + id("event_type") + enabled(true) + value(data.sendEventDraft?.type) + hint(stringProvider.getString(R.string.dev_tools_form_hint_type)) + showBottomSeparator(false) + onTextChange { text -> + interactionListener?.processAction(RoomDevToolAction.CustomEventTypeChange(text)) + } + } + + if (sendEventForm.isState) { + formEditTextItem { + id("state_key") + enabled(true) + value(data.sendEventDraft?.stateKey) + hint(stringProvider.getString(R.string.dev_tools_form_hint_state_key)) + showBottomSeparator(false) + onTextChange { text -> + interactionListener?.processAction(RoomDevToolAction.CustomEventStateKeyChange(text)) + } + } + } + + formMultiLineEditTextItem { + id("event_content") + enabled(true) + value(data.sendEventDraft?.content) + hint(stringProvider.getString(R.string.dev_tools_form_hint_event_content)) + showBottomSeparator(false) + onTextChange { text -> + interactionListener?.processAction(RoomDevToolAction.CustomEventContentChange(text)) + } + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolSendFormFragment.kt b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolSendFormFragment.kt new file mode 100644 index 0000000000..abda6104cd --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolSendFormFragment.kt @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.devtools + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.airbnb.mvrx.activityViewModel +import com.airbnb.mvrx.withState +import im.vector.app.core.extensions.cleanup +import im.vector.app.core.extensions.configureWith +import im.vector.app.core.platform.VectorBaseFragment +import im.vector.app.databinding.FragmentGenericRecyclerBinding +import javax.inject.Inject + +class RoomDevToolSendFormFragment @Inject constructor( + private val epoxyController: RoomDevToolSendFormController +) : VectorBaseFragment(), DevToolsInteractionListener { + + val sharedViewModel: RoomDevToolViewModel by activityViewModel() + + override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentGenericRecyclerBinding { + return FragmentGenericRecyclerBinding.inflate(inflater, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + views.genericRecyclerView.configureWith(epoxyController, showDivider = false) + epoxyController.interactionListener = this + } + + override fun onDestroyView() { + views.genericRecyclerView.cleanup() + epoxyController.interactionListener = null + super.onDestroyView() + } + + override fun invalidate() = withState(sharedViewModel) { state -> + epoxyController.setData(state) + } + + override fun processAction(action: RoomDevToolAction) { + sharedViewModel.handle(action) + } +} diff --git a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolStateEventListFragment.kt b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolStateEventListFragment.kt new file mode 100644 index 0000000000..600464bb6d --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolStateEventListFragment.kt @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.devtools + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.airbnb.mvrx.activityViewModel +import com.airbnb.mvrx.withState +import im.vector.app.core.extensions.cleanup +import im.vector.app.core.extensions.configureWith +import im.vector.app.core.platform.VectorBaseFragment +import im.vector.app.databinding.FragmentGenericRecyclerBinding +import javax.inject.Inject + +class RoomDevToolStateEventListFragment @Inject constructor( + private val epoxyController: RoomStateListController +) : VectorBaseFragment(), DevToolsInteractionListener { + + val sharedViewModel: RoomDevToolViewModel by activityViewModel() + + override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentGenericRecyclerBinding { + return FragmentGenericRecyclerBinding.inflate(inflater, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + views.genericRecyclerView.configureWith(epoxyController, showDivider = true) + epoxyController.interactionListener = this + } + + override fun onDestroyView() { + views.genericRecyclerView.cleanup() + epoxyController.interactionListener = null + super.onDestroyView() + } + + override fun invalidate() = withState(sharedViewModel) { state -> + epoxyController.setData(state) + } + + override fun processAction(action: RoomDevToolAction) { + sharedViewModel.handle(action) + } +} diff --git a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolViewModel.kt b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolViewModel.kt new file mode 100644 index 0000000000..9fffe70872 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolViewModel.kt @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.devtools + +import androidx.lifecycle.viewModelScope +import com.airbnb.mvrx.ActivityViewModelContext +import com.airbnb.mvrx.Fail +import com.airbnb.mvrx.FragmentViewModelContext +import com.airbnb.mvrx.Loading +import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.Success +import com.airbnb.mvrx.ViewModelContext +import com.squareup.moshi.Types +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import im.vector.app.R +import im.vector.app.core.error.ErrorFormatter +import im.vector.app.core.platform.VectorViewModel +import im.vector.app.core.resources.StringProvider +import kotlinx.coroutines.launch +import org.json.JSONObject +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.api.session.events.model.EventType +import org.matrix.android.sdk.api.session.events.model.toModel +import org.matrix.android.sdk.api.session.room.model.message.MessageContent +import org.matrix.android.sdk.api.util.JsonDict +import org.matrix.android.sdk.internal.di.MoshiProvider +import org.matrix.android.sdk.rx.rx + +class RoomDevToolViewModel @AssistedInject constructor( + @Assisted val initialState: RoomDevToolViewState, + private val errorFormatter: ErrorFormatter, + private val stringProvider: StringProvider, + private val session: Session +) : VectorViewModel(initialState) { + + @AssistedFactory + interface Factory { + fun create(initialState: RoomDevToolViewState): RoomDevToolViewModel + } + + companion object : MvRxViewModelFactory { + + @JvmStatic + override fun create(viewModelContext: ViewModelContext, state: RoomDevToolViewState): RoomDevToolViewModel { + val factory = when (viewModelContext) { + is FragmentViewModelContext -> viewModelContext.fragment as? Factory + is ActivityViewModelContext -> viewModelContext.activity as? Factory + } + return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") + } + } + + init { + session.getRoom(initialState.roomId) + ?.rx() + ?.liveStateEvents(emptySet()) + ?.execute { async -> + copy(stateEvents = async) + } + } + + override fun handle(action: RoomDevToolAction) { + when (action) { + RoomDevToolAction.ExploreRoomState -> { + setState { + copy( + displayMode = RoomDevToolViewState.Mode.StateEventList, + selectedEvent = null + ) + } + } + is RoomDevToolAction.ShowStateEvent -> { + val jsonString = MoshiProvider.providesMoshi() + .adapter(Event::class.java) + .toJson(action.event) + + setState { + copy( + displayMode = RoomDevToolViewState.Mode.StateEventDetail, + selectedEvent = action.event, + selectedEventJson = jsonString + ) + } + } + RoomDevToolAction.OnBackPressed -> { + handleBack() + } + RoomDevToolAction.MenuEdit -> { + withState { + if (it.displayMode == RoomDevToolViewState.Mode.StateEventDetail) { + // we want to edit it + val content = it.selectedEvent?.content?.let { JSONObject(it).toString(4) } ?: "{\n\t\n}" + setState { + copy( + editedContent = content, + displayMode = RoomDevToolViewState.Mode.EditEventContent + ) + } + } + } + } + is RoomDevToolAction.ShowStateEventType -> { + setState { + copy( + displayMode = RoomDevToolViewState.Mode.StateEventListByType, + currentStateType = action.stateEventType + ) + } + } + RoomDevToolAction.MenuItemSend -> { + handleMenuItemSend() + } + is RoomDevToolAction.UpdateContentText -> { + setState { + copy(editedContent = action.contentJson) + } + } + is RoomDevToolAction.SendCustomEvent -> { + setState { + copy( + displayMode = RoomDevToolViewState.Mode.SendEventForm(action.isStateEvent), + sendEventDraft = RoomDevToolViewState.SendEventDraft(EventType.MESSAGE, null, "{\n}") + ) + } + } + is RoomDevToolAction.CustomEventTypeChange -> { + setState { + copy( + sendEventDraft = sendEventDraft?.copy(type = action.type) + ) + } + } + is RoomDevToolAction.CustomEventStateKeyChange -> { + setState { + copy( + sendEventDraft = sendEventDraft?.copy(stateKey = action.stateKey) + ) + } + } + is RoomDevToolAction.CustomEventContentChange -> { + setState { + copy( + sendEventDraft = sendEventDraft?.copy(content = action.content) + ) + } + } + } + } + + private fun handleMenuItemSend() = withState { state -> + when (state.displayMode) { + RoomDevToolViewState.Mode.EditEventContent -> editEventContent(state) + is RoomDevToolViewState.Mode.SendEventForm -> sendEventContent(state, state.displayMode.isState) + else -> Unit + } + } + + private fun editEventContent(state: RoomDevToolViewState) { + setState { copy(modalLoading = Loading()) } + + viewModelScope.launch { + try { + val room = session.getRoom(initialState.roomId) + ?: throw IllegalArgumentException(stringProvider.getString(R.string.room_error_not_found)) + + val adapter = MoshiProvider.providesMoshi() + .adapter(Types.newParameterizedType(Map::class.java, String::class.java, Any::class.java)) + val json = adapter.fromJson(state.editedContent ?: "") + ?: throw IllegalArgumentException(stringProvider.getString(R.string.dev_tools_error_no_content)) + + room.sendStateEvent( + state.selectedEvent?.type ?: "", + state.selectedEvent?.stateKey, + json + + ) + _viewEvents.post(DevToolsViewEvents.ShowSnackMessage(stringProvider.getString(R.string.dev_tools_success_state_event))) + setState { + copy( + modalLoading = Success(Unit), + selectedEventJson = null, + editedContent = null, + displayMode = RoomDevToolViewState.Mode.StateEventListByType + ) + } + } catch (failure: Throwable) { + _viewEvents.post(DevToolsViewEvents.ShowAlertMessage(errorFormatter.toHumanReadable(failure))) + setState { copy(modalLoading = Fail(failure)) } + } + } + } + + private fun sendEventContent(state: RoomDevToolViewState, isState: Boolean) { + setState { copy(modalLoading = Loading()) } + viewModelScope.launch { + try { + val room = session.getRoom(initialState.roomId) + ?: throw IllegalArgumentException(stringProvider.getString(R.string.room_error_not_found)) + + val adapter = MoshiProvider.providesMoshi() + .adapter(Types.newParameterizedType(Map::class.java, String::class.java, Any::class.java)) + val json = adapter.fromJson(state.sendEventDraft?.content ?: "") + ?: throw IllegalArgumentException(stringProvider.getString(R.string.dev_tools_error_no_content)) + + val eventType = state.sendEventDraft?.type + ?: throw IllegalArgumentException(stringProvider.getString(R.string.dev_tools_error_no_message_type)) + + if (isState) { + room.sendStateEvent( + eventType, + state.sendEventDraft.stateKey, + json + ) + } else { + // can we try to do some validation?? + // val validParse = MoshiProvider.providesMoshi().adapter(MessageContent::class.java).fromJson(it.sendEventDraft.content ?: "") + json.toModel(catchError = false) + ?: throw IllegalArgumentException(stringProvider.getString(R.string.dev_tools_error_malformed_event)) + room.sendEvent( + eventType, + json + ) + } + + _viewEvents.post(DevToolsViewEvents.ShowSnackMessage(stringProvider.getString(R.string.dev_tools_success_event))) + setState { + copy( + modalLoading = Success(Unit), + sendEventDraft = null, + displayMode = RoomDevToolViewState.Mode.Root + ) + } + } catch (failure: Throwable) { + _viewEvents.post(DevToolsViewEvents.ShowAlertMessage(errorFormatter.toHumanReadable(failure))) + setState { copy(modalLoading = Fail(failure)) } + } + } + } + + private fun handleBack() = withState { + when (it.displayMode) { + RoomDevToolViewState.Mode.Root -> { + _viewEvents.post(DevToolsViewEvents.Dismiss) + } + RoomDevToolViewState.Mode.StateEventList -> { + setState { + copy( + selectedEvent = null, + selectedEventJson = null, + displayMode = RoomDevToolViewState.Mode.Root + ) + } + } + RoomDevToolViewState.Mode.StateEventDetail -> { + setState { + copy( + selectedEvent = null, + selectedEventJson = null, + displayMode = RoomDevToolViewState.Mode.StateEventListByType + ) + } + } + RoomDevToolViewState.Mode.EditEventContent -> { + setState { + copy( + displayMode = RoomDevToolViewState.Mode.StateEventDetail + ) + } + } + RoomDevToolViewState.Mode.StateEventListByType -> { + setState { + copy( + currentStateType = null, + displayMode = RoomDevToolViewState.Mode.StateEventList + ) + } + } + is RoomDevToolViewState.Mode.SendEventForm -> { + setState { + copy( + displayMode = RoomDevToolViewState.Mode.Root + ) + } + } + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolViewState.kt b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolViewState.kt new file mode 100644 index 0000000000..885de005b0 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolViewState.kt @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.devtools + +import com.airbnb.mvrx.Async +import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.Uninitialized +import org.matrix.android.sdk.api.session.events.model.Event + +data class RoomDevToolViewState( + val roomId: String = "", + val displayMode: Mode = Mode.Root, + val stateEvents: Async> = Uninitialized, + val currentStateType: String? = null, + val selectedEvent: Event? = null, + val selectedEventJson: String? = null, + val editedContent: String? = null, + val modalLoading: Async = Uninitialized, + val sendEventDraft: SendEventDraft? = null +) : MvRxState { + + constructor(args: RoomDevToolActivity.Args) : this(roomId = args.roomId, displayMode = Mode.Root) + + sealed class Mode { + object Root : Mode() + object StateEventList : Mode() + object StateEventListByType : Mode() + object StateEventDetail : Mode() + object EditEventContent : Mode() + data class SendEventForm(val isState: Boolean) : Mode() + } + + data class SendEventDraft( + val type: String?, + val stateKey: String?, + val content: String? + ) +} diff --git a/vector/src/main/java/im/vector/app/features/devtools/RoomStateListController.kt b/vector/src/main/java/im/vector/app/features/devtools/RoomStateListController.kt new file mode 100644 index 0000000000..69070c509b --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/devtools/RoomStateListController.kt @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.devtools + +import com.airbnb.epoxy.TypedEpoxyController +import im.vector.app.R +import im.vector.app.core.epoxy.noResultItem +import im.vector.app.core.resources.ColorProvider +import im.vector.app.core.resources.StringProvider +import im.vector.app.core.ui.list.GenericItem +import im.vector.app.core.ui.list.genericItem +import me.gujun.android.span.span +import org.json.JSONObject +import javax.inject.Inject + +class RoomStateListController @Inject constructor( + private val stringProvider: StringProvider, + private val colorProvider: ColorProvider +) : TypedEpoxyController() { + + var interactionListener: DevToolsInteractionListener? = null + + override fun buildModels(data: RoomDevToolViewState?) { + when (data?.displayMode) { + RoomDevToolViewState.Mode.StateEventList -> { + val stateEventsGroups = data.stateEvents.invoke().orEmpty().groupBy { it.type } + + if (stateEventsGroups.isEmpty()) { + noResultItem { + id("no state events") + text(stringProvider.getString(R.string.no_result_placeholder)) + } + } else { + stateEventsGroups.forEach { entry -> + genericItem { + id(entry.key) + title(entry.key) + description(stringProvider.getQuantityString(R.plurals.entries, entry.value.size, entry.value.size)) + itemClickAction(GenericItem.Action("view").apply { + perform = Runnable { + interactionListener?.processAction(RoomDevToolAction.ShowStateEventType(entry.key)) + } + }) + } + } + } + } + RoomDevToolViewState.Mode.StateEventListByType -> { + val stateEvents = data.stateEvents.invoke().orEmpty().filter { it.type == data.currentStateType } + if (stateEvents.isEmpty()) { + noResultItem { + id("no state events") + text(stringProvider.getString(R.string.no_result_placeholder)) + } + } else { + stateEvents.forEach { stateEvent -> + val contentJson = JSONObject(stateEvent.content.orEmpty()).toString().let { + if (it.length > 140) { + it.take(140) + Typography.ellipsis + } else { + it.take(140) + } + } + genericItem { + id(stateEvent.eventId) + title(span { + +"Type: " + span { + textColor = colorProvider.getColorFromAttribute(R.attr.riotx_text_secondary) + text = "\"${stateEvent.type}\"" + textStyle = "normal" + } + +"\nState Key: " + span { + textColor = colorProvider.getColorFromAttribute(R.attr.riotx_text_secondary) + text = stateEvent.stateKey.let { "\"$it\"" } + textStyle = "normal" + } + }) + description(contentJson) + itemClickAction(GenericItem.Action("view").apply { + perform = Runnable { + interactionListener?.processAction(RoomDevToolAction.ShowStateEvent(stateEvent)) + } + }) + } + } + } + } + else -> { + // nop + } + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/form/FormMultiLineEditTextItem.kt b/vector/src/main/java/im/vector/app/features/form/FormMultiLineEditTextItem.kt new file mode 100644 index 0000000000..4ba668a051 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/form/FormMultiLineEditTextItem.kt @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.form + +import android.graphics.Typeface +import android.text.Editable +import android.view.View +import androidx.core.view.isVisible +import com.airbnb.epoxy.EpoxyAttribute +import com.airbnb.epoxy.EpoxyModelClass +import com.google.android.material.textfield.TextInputEditText +import com.google.android.material.textfield.TextInputLayout +import im.vector.app.R +import im.vector.app.core.epoxy.VectorEpoxyHolder +import im.vector.app.core.epoxy.VectorEpoxyModel +import im.vector.app.core.extensions.setTextSafe +import im.vector.app.core.platform.SimpleTextWatcher + +@EpoxyModelClass(layout = R.layout.item_form_multiline_text_input) +abstract class FormMultiLineEditTextItem : VectorEpoxyModel() { + + @EpoxyAttribute + var hint: String? = null + + @EpoxyAttribute + var value: String? = null + + @EpoxyAttribute + var showBottomSeparator: Boolean = true + + @EpoxyAttribute + var errorMessage: String? = null + + @EpoxyAttribute + var enabled: Boolean = true + + @EpoxyAttribute + var textSizeSp: Int? = null + + @EpoxyAttribute + var minLines: Int = 3 + + @EpoxyAttribute + var typeFace: Typeface = Typeface.DEFAULT + + @EpoxyAttribute + var onTextChange: ((String) -> Unit)? = null + + private val onTextChangeListener = object : SimpleTextWatcher() { + override fun afterTextChanged(s: Editable) { + onTextChange?.invoke(s.toString()) + } + } + + override fun bind(holder: Holder) { + super.bind(holder) + holder.textInputLayout.isEnabled = enabled + holder.textInputLayout.hint = hint + holder.textInputLayout.error = errorMessage + + holder.textInputEditText.typeface = typeFace + holder.textInputEditText.textSize = textSizeSp?.toFloat() ?: 12f + holder.textInputEditText.minLines = minLines + + // Update only if text is different and value is not null + holder.textInputEditText.setTextSafe(value) + holder.textInputEditText.isEnabled = enabled + + holder.textInputEditText.addTextChangedListener(onTextChangeListener) + holder.bottomSeparator.isVisible = showBottomSeparator + } + + override fun shouldSaveViewState(): Boolean { + return false + } + + override fun unbind(holder: Holder) { + super.unbind(holder) + holder.textInputEditText.removeTextChangedListener(onTextChangeListener) + } + + class Holder : VectorEpoxyHolder() { + val textInputLayout by bind(R.id.formMultiLineTextInputLayout) + val textInputEditText by bind(R.id.formMultiLineEditText) + val bottomSeparator by bind(R.id.formTextInputDivider) + } +} diff --git a/vector/src/main/java/im/vector/app/features/grouplist/GroupListViewModel.kt b/vector/src/main/java/im/vector/app/features/grouplist/GroupListViewModel.kt index 3b096adbfb..4b187f83ca 100644 --- a/vector/src/main/java/im/vector/app/features/grouplist/GroupListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/grouplist/GroupListViewModel.kt @@ -29,7 +29,6 @@ import im.vector.app.R import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider import io.reactivex.Observable -import io.reactivex.functions.BiFunction import kotlinx.coroutines.launch import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.Session @@ -123,7 +122,7 @@ class GroupListViewModel @AssistedInject constructor(@Assisted initialState: Gro session .rx() .liveGroupSummaries(groupSummariesQueryParams), - BiFunction { allCommunityGroup, communityGroups -> + { allCommunityGroup, communityGroups -> listOf(allCommunityGroup) + communityGroups } ) diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt index 15e982492b..6a381ec049 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt @@ -285,10 +285,10 @@ class HomeActivity : dismissedAction = Runnable { homeActivityViewModel.handle(HomeActivityViewActions.PushPromptHasBeenReviewed) } - addButton(getString(R.string.dismiss), Runnable { + addButton(getString(R.string.dismiss), { homeActivityViewModel.handle(HomeActivityViewActions.PushPromptHasBeenReviewed) }, true) - addButton(getString(R.string.settings), Runnable { + addButton(getString(R.string.settings), { (weakCurrentActivity?.get() as? VectorBaseActivity<*>)?.let { // action(it) homeActivityViewModel.handle(HomeActivityViewActions.PushPromptHasBeenReviewed) diff --git a/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt index f4f16502ab..988b4fbabe 100644 --- a/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt @@ -32,7 +32,6 @@ import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModelAction import im.vector.app.features.settings.VectorPreferences import io.reactivex.Observable -import io.reactivex.functions.Function3 import org.matrix.android.sdk.api.NoOpMatrixCallback import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.Session @@ -103,7 +102,7 @@ class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor(@Assisted session.rx().liveUserCryptoDevices(session.myUserId), session.rx().liveMyDevicesInfo(), session.rx().liveCrossSigningPrivateKeys(), - Function3 { cryptoList, infoList, pInfo -> + { cryptoList, infoList, pInfo -> // Timber.v("## Detector trigger ${cryptoList.map { "${it.deviceId} ${it.trustLevel}" }}") // Timber.v("## Detector trigger canCrossSign ${pInfo.get().selfSigned != null}") infoList diff --git a/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsController.kt b/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsController.kt index 4e6bb124ad..a406803bbb 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsController.kt @@ -16,7 +16,6 @@ package im.vector.app.features.home.room.breadcrumbs -import android.view.View import com.airbnb.epoxy.EpoxyController import im.vector.app.core.epoxy.zeroItem import im.vector.app.core.utils.DebouncedClickListener @@ -65,7 +64,7 @@ class BreadcrumbsController @Inject constructor( hasUnreadMessage(it.hasUnreadMessages) hasDraft(it.userDrafts.isNotEmpty()) itemClickListener( - DebouncedClickListener(View.OnClickListener { _ -> + DebouncedClickListener({ _ -> listener?.onBreadcrumbClicked(it.roomId) }) ) diff --git a/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsItem.kt b/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsItem.kt index e1343f6f51..f39b7b6d0a 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsItem.kt @@ -46,6 +46,7 @@ abstract class BreadcrumbsItem : VectorEpoxyModel() { holder.rootView.setOnClickListener(itemClickListener) holder.unreadIndentIndicator.isVisible = hasUnreadMessage avatarRenderer.render(matrixItem, holder.avatarImageView) + holder.avatarImageView.contentDescription = matrixItem.getBestName() holder.unreadCounterBadgeView.render(UnreadCounterBadgeView.State(unreadNotificationCount, showHighlighted)) holder.draftIndentIndicator.isVisible = hasDraft holder.typingIndicator.isVisible = hasTypingUsers diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/JumpToBottomViewVisibilityManager.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/JumpToBottomViewVisibilityManager.kt index d3f9b6eda5..2810b27aa6 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/JumpToBottomViewVisibilityManager.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/JumpToBottomViewVisibilityManager.kt @@ -60,9 +60,9 @@ class JumpToBottomViewVisibilityManager( } fun maybeShowJumpToBottomViewVisibilityWithDelay() { - debouncer.debounce("jump_to_bottom_visibility", 250, Runnable { + debouncer.debounce("jump_to_bottom_visibility", 250) { maybeShowJumpToBottomViewVisibility() - }) + } } private fun maybeShowJumpToBottomViewVisibility() { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt index 55294edd76..c511c9e666 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt @@ -52,6 +52,7 @@ import androidx.core.view.ViewCompat import androidx.core.view.forEach import androidx.core.view.isInvisible import androidx.core.view.isVisible +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -124,7 +125,6 @@ import im.vector.app.features.call.conference.JitsiCallViewModel import im.vector.app.features.call.webrtc.WebRtcCallManager import im.vector.app.features.command.Command import im.vector.app.features.crypto.keysbackup.restore.KeysBackupRestoreActivity -import im.vector.app.features.crypto.util.toImageRes import im.vector.app.features.crypto.verification.VerificationBottomSheet import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.room.detail.composer.TextComposerView @@ -537,8 +537,18 @@ class RoomDetailFragment @Inject constructor( .Builder .fromRootView(views.rootConstraintLayout) .setKeyboardAnimationStyle(R.style.emoji_fade_animation_style) - .setOnEmojiPopupShownListener { views.composerLayout.views.composerEmojiButton.setImageResource(R.drawable.ic_keyboard) } - .setOnEmojiPopupDismissListener { views.composerLayout.views.composerEmojiButton.setImageResource(R.drawable.ic_insert_emoji) } + .setOnEmojiPopupShownListener { + views.composerLayout.views.composerEmojiButton.let { + it.setImageResource(R.drawable.ic_keyboard) + it.contentDescription = getString(R.string.a11y_close_emoji_picker) + } + } + .setOnEmojiPopupDismissListener { + views.composerLayout.views.composerEmojiButton.let { + it.setImageResource(R.drawable.ic_insert_emoji) + it.contentDescription = getString(R.string.a11y_open_emoji_picker) + } + } .build(views.composerLayout.views.composerEditText) views.composerLayout.views.composerEmojiButton.debouncedClicks { @@ -789,6 +799,10 @@ class RoomDetailFragment @Inject constructor( handleSearchAction() true } + R.id.dev_tools -> { + navigator.openDevTools(requireContext(), roomDetailArgs.roomId) + true + } else -> super.onOptionsItemSelected(item) } } @@ -1030,7 +1044,7 @@ class RoomDetailFragment @Inject constructor( } private fun updateJumpToReadMarkerViewVisibility() { - views.jumpToReadMarkerView.post { + viewLifecycleOwner.lifecycleScope.launchWhenResumed { withState(roomDetailViewModel) { val showJumpToUnreadBanner = when (it.unreadState) { UnreadState.Unknown, @@ -1189,10 +1203,7 @@ class RoomDetailFragment @Inject constructor( avatarRenderer.render(roomSummary.toMatrixItem(), views.roomToolbarAvatarImageView) renderSubTitle(typingMessage, roomSummary.topic) - views.roomToolbarDecorationImageView.let { - it.setImageResource(roomSummary.roomEncryptionTrustLevel.toImageRes()) - it.isVisible = roomSummary.roomEncryptionTrustLevel != null - } + views.roomToolbarDecorationImageView.render(roomSummary.roomEncryptionTrustLevel) } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt index b4ac226db4..c7a5873a65 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt @@ -49,7 +49,6 @@ import im.vector.app.features.raw.wellknown.getElementWellknown import im.vector.app.features.settings.VectorLocale import im.vector.app.features.settings.VectorPreferences import io.reactivex.Observable -import io.reactivex.functions.BiFunction import io.reactivex.rxkotlin.subscribeBy import io.reactivex.schedulers.Schedulers import kotlinx.coroutines.Dispatchers @@ -627,9 +626,10 @@ class RoomDetailViewModel @AssistedInject constructor( R.id.clear_all -> state.asyncRoomSummary()?.hasFailedSending == true R.id.open_matrix_apps -> true R.id.voice_call, - R.id.video_call -> callManager.getCallsByRoomId(state.roomId).isEmpty() - R.id.hangup_call -> callManager.getCallsByRoomId(state.roomId).isNotEmpty() - R.id.search -> true + R.id.video_call -> callManager.getCallsByRoomId(state.roomId).isEmpty() + R.id.hangup_call -> callManager.getCallsByRoomId(state.roomId).isNotEmpty() + R.id.search -> true + R.id.dev_tools -> vectorPreferences.developerMode() else -> false } } @@ -1332,7 +1332,7 @@ class RoomDetailViewModel @AssistedInject constructor( .combineLatest, RoomSummary, UnreadState>( timelineEvents.observeOn(Schedulers.computation()), room.rx().liveRoomSummary().unwrap(), - BiFunction { timelineEvents, roomSummary -> + { timelineEvents, roomSummary -> computeUnreadState(timelineEvents, roomSummary) } ) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerView.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerView.kt index 5baec0a8f4..6c7721ca02 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerView.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerView.kt @@ -24,7 +24,6 @@ import android.view.ViewGroup import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet import androidx.core.text.toSpannable -import androidx.core.view.isVisible import androidx.transition.ChangeBounds import androidx.transition.Fade import androidx.transition.Transition @@ -41,8 +40,8 @@ import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel */ class TextComposerView @JvmOverloads constructor( context: Context, - attrs: AttributeSet? = null, - defStyleAttr: Int = 0) : ConstraintLayout(context, attrs, defStyleAttr) { + attrs: AttributeSet? = null, + defStyleAttr: Int = 0) : ConstraintLayout(context, attrs, defStyleAttr) { interface Callback : ComposerEditText.Callback { fun onCloseRelatedMessage() @@ -143,16 +142,10 @@ class TextComposerView @JvmOverloads constructor( fun setRoomEncrypted(isEncrypted: Boolean, roomEncryptionTrustLevel: RoomEncryptionTrustLevel?) { if (isEncrypted) { views.composerEditText.setHint(R.string.room_message_placeholder) - views.composerShieldImageView.isVisible = true - val shieldRes = when (roomEncryptionTrustLevel) { - RoomEncryptionTrustLevel.Trusted -> R.drawable.ic_shield_trusted - RoomEncryptionTrustLevel.Warning -> R.drawable.ic_shield_warning - else -> R.drawable.ic_shield_black - } - views.composerShieldImageView.setImageResource(shieldRes) + views.composerShieldImageView.render(roomEncryptionTrustLevel) } else { views.composerEditText.setHint(R.string.room_message_placeholder) - views.composerShieldImageView.isVisible = false + views.composerShieldImageView.render(null) } } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/DefaultItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/DefaultItemFactory.kt index 2120576bee..9d82103d3b 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/DefaultItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/DefaultItemFactory.kt @@ -16,7 +16,6 @@ package im.vector.app.features.home.room.detail.timeline.factory -import android.view.View import im.vector.app.R import im.vector.app.core.resources.StringProvider import im.vector.app.features.home.AvatarRenderer @@ -42,7 +41,7 @@ class DefaultItemFactory @Inject constructor(private val avatarSizeProvider: Ava avatarRenderer = avatarRenderer, informationData = informationData, text = text, - itemLongClickListener = View.OnLongClickListener { view -> + itemLongClickListener = { view -> callback?.onEventLongClicked(informationData, null, view) ?: false }, readReceiptsCallback = callback diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt index b3fa7c0eda..e9cf8251de 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt @@ -322,7 +322,7 @@ class MessageItemFactory @Inject constructor( mode(ImageContentRenderer.Mode.STICKER) } else { clickListener( - DebouncedClickListener(View.OnClickListener { view -> + DebouncedClickListener({ view -> callback?.onImageMessageClicked(messageContent, data, view) })) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/NoticeItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/NoticeItemFactory.kt index cd8c682f39..12c7c2318a 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/NoticeItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/NoticeItemFactory.kt @@ -16,7 +16,6 @@ package im.vector.app.features.home.room.detail.timeline.factory -import android.view.View import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.room.detail.timeline.TimelineEventController import im.vector.app.features.home.room.detail.timeline.format.NoticeEventFormatter @@ -41,7 +40,7 @@ class NoticeItemFactory @Inject constructor(private val eventFormatter: NoticeEv avatarRenderer = avatarRenderer, informationData = informationData, noticeText = formattedText, - itemLongClickListener = View.OnLongClickListener { view -> + itemLongClickListener = { view -> callback?.onEventLongClicked(informationData, null, view) ?: false }, readReceiptsCallback = callback, diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MessageItemAttributesFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MessageItemAttributesFactory.kt index c120fa671c..043fd9e896 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MessageItemAttributesFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MessageItemAttributesFactory.kt @@ -15,7 +15,6 @@ */ package im.vector.app.features.home.room.detail.timeline.helper -import android.view.View import im.vector.app.EmojiCompatFontProvider import im.vector.app.core.utils.DebouncedClickListener import im.vector.app.features.home.AvatarRenderer @@ -39,13 +38,13 @@ class MessageItemAttributesFactory @Inject constructor( informationData = informationData, avatarRenderer = avatarRenderer, messageColorProvider = messageColorProvider, - itemLongClickListener = View.OnLongClickListener { view -> + itemLongClickListener = { view -> callback?.onEventLongClicked(informationData, messageContent, view) ?: false }, - itemClickListener = DebouncedClickListener(View.OnClickListener { view -> + itemClickListener = DebouncedClickListener({ view -> callback?.onEventCellClicked(informationData, messageContent, view) }), - memberClickListener = DebouncedClickListener(View.OnClickListener { + memberClickListener = DebouncedClickListener({ callback?.onMemberNameClicked(informationData) }), reactionPillCallback = callback, diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsBaseMessageItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsBaseMessageItem.kt index 29aca2c4d5..a65f1e10f2 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsBaseMessageItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsBaseMessageItem.kt @@ -23,11 +23,13 @@ import android.widget.TextView import androidx.annotation.IdRes import androidx.core.view.isVisible import im.vector.app.R +import im.vector.app.core.ui.views.ShieldImageView import im.vector.app.core.utils.DebouncedClickListener import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.room.detail.timeline.MessageColorProvider import im.vector.app.features.home.room.detail.timeline.TimelineEventController import im.vector.app.features.reactions.widget.ReactionButton +import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel import org.matrix.android.sdk.api.session.room.send.SendState /** @@ -39,7 +41,7 @@ abstract class AbsBaseMessageItem : BaseEventItem abstract val baseAttributes: Attributes - private val _readReceiptsClickListener = DebouncedClickListener(View.OnClickListener { + private val _readReceiptsClickListener = DebouncedClickListener({ baseAttributes.readReceiptsCallback?.onReadReceiptsClicked(baseAttributes.informationData.readReceipts) }) @@ -94,13 +96,12 @@ abstract class AbsBaseMessageItem : BaseEventItem when (baseAttributes.informationData.e2eDecoration) { E2EDecoration.NONE -> { - holder.e2EDecorationView.isVisible = false + holder.e2EDecorationView.render(null) } E2EDecoration.WARN_IN_CLEAR, E2EDecoration.WARN_SENT_BY_UNVERIFIED, E2EDecoration.WARN_SENT_BY_UNKNOWN -> { - holder.e2EDecorationView.setImageResource(R.drawable.ic_shield_warning) - holder.e2EDecorationView.isVisible = true + holder.e2EDecorationView.render(RoomEncryptionTrustLevel.Warning) } } @@ -123,7 +124,7 @@ abstract class AbsBaseMessageItem : BaseEventItem abstract class Holder(@IdRes stubId: Int) : BaseEventItem.BaseHolder(stubId) { val reactionsContainer by bind(R.id.reactionsContainer) - val e2EDecorationView by bind(R.id.messageE2EDecoration) + val e2EDecorationView by bind(R.id.messageE2EDecoration) } /** diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageItem.kt index e0f67fdd30..d4b1b8859a 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageItem.kt @@ -42,10 +42,10 @@ abstract class AbsMessageItem : AbsBaseMessageItem @EpoxyAttribute lateinit var attributes: Attributes - private val _avatarClickListener = DebouncedClickListener(View.OnClickListener { + private val _avatarClickListener = DebouncedClickListener({ attributes.avatarCallback?.onAvatarClicked(attributes.informationData) }) - private val _memberNameClickListener = DebouncedClickListener(View.OnClickListener { + private val _memberNameClickListener = DebouncedClickListener({ attributes.avatarCallback?.onMemberNameClicked(attributes.informationData) }) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/DefaultItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/DefaultItem.kt index 8cd3c95141..cdc677334e 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/DefaultItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/DefaultItem.kt @@ -32,7 +32,7 @@ abstract class DefaultItem : BaseEventItem() { @EpoxyAttribute lateinit var attributes: Attributes - private val _readReceiptsClickListener = DebouncedClickListener(View.OnClickListener { + private val _readReceiptsClickListener = DebouncedClickListener({ attributes.readReceiptsCallback?.onReadReceiptsClicked(attributes.informationData.readReceipts) }) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MergedRoomCreationItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MergedRoomCreationItem.kt index 34b9ae1b9d..6a665bb44f 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MergedRoomCreationItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MergedRoomCreationItem.kt @@ -186,14 +186,14 @@ abstract class MergedRoomCreationItem : BasedMergedItem + holder.setAvatarButton.setOnClickListener(DebouncedClickListener({ attributes.callback?.onTimelineItemAction(RoomDetailAction.QuickActionSetAvatar) })) } holder.addPeopleButton.isVisible = !isDirect if (!isDirect) { - holder.addPeopleButton.setOnClickListener(DebouncedClickListener({ _ -> + holder.addPeopleButton.setOnClickListener(DebouncedClickListener({ attributes.callback?.onTimelineItemAction(RoomDetailAction.QuickActionInvitePeople) })) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessagePollItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessagePollItem.kt index a7db58deb1..ca5c3b2dea 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessagePollItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessagePollItem.kt @@ -143,7 +143,7 @@ abstract class MessagePollItem : AbsMessageItem() { override fun bindView(itemView: View) { super.bindView(itemView) val buttons = listOf(button1, button2, button3, button4, button5) - val clickListener = DebouncedClickListener(View.OnClickListener { + val clickListener = DebouncedClickListener({ val optionIndex = buttons.indexOf(it) if (optionIndex != -1 && pollId != null) { val compatValue = if (optionIndex < optionValues?.size ?: 0) optionValues?.get(optionIndex) else null diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/NoticeItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/NoticeItem.kt index 34ee8c69fa..bcf170dc4d 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/NoticeItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/NoticeItem.kt @@ -19,15 +19,16 @@ package im.vector.app.features.home.room.detail.timeline.item import android.view.View import android.widget.ImageView import android.widget.TextView -import androidx.core.view.isVisible import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyModelClass import im.vector.app.R import im.vector.app.core.epoxy.ClickListener import im.vector.app.core.epoxy.onClick +import im.vector.app.core.ui.views.ShieldImageView import im.vector.app.core.utils.DebouncedClickListener import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.room.detail.timeline.TimelineEventController +import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel @EpoxyModelClass(layout = R.layout.item_timeline_event_base_noinfo) abstract class NoticeItem : BaseEventItem() { @@ -35,7 +36,7 @@ abstract class NoticeItem : BaseEventItem() { @EpoxyAttribute lateinit var attributes: Attributes - private val _readReceiptsClickListener = DebouncedClickListener(View.OnClickListener { + private val _readReceiptsClickListener = DebouncedClickListener({ attributes.readReceiptsCallback?.onReadReceiptsClicked(attributes.informationData.readReceipts) }) @@ -49,13 +50,12 @@ abstract class NoticeItem : BaseEventItem() { when (attributes.informationData.e2eDecoration) { E2EDecoration.NONE -> { - holder.e2EDecorationView.isVisible = false + holder.e2EDecorationView.render(null) } E2EDecoration.WARN_IN_CLEAR, E2EDecoration.WARN_SENT_BY_UNVERIFIED, E2EDecoration.WARN_SENT_BY_UNKNOWN -> { - holder.e2EDecorationView.setImageResource(R.drawable.ic_shield_warning) - holder.e2EDecorationView.isVisible = true + holder.e2EDecorationView.render(RoomEncryptionTrustLevel.Warning) } } } @@ -75,7 +75,7 @@ abstract class NoticeItem : BaseEventItem() { class Holder : BaseHolder(STUB_ID) { val avatarImageView by bind(R.id.itemNoticeAvatarView) val noticeTextView by bind(R.id.itemNoticeTextView) - val e2EDecorationView by bind(R.id.messageE2EDecoration) + val e2EDecorationView by bind(R.id.messageE2EDecoration) } data class Attributes( diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsViewModel.kt index c1f98cc3ab..35d208e30e 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsViewModel.kt @@ -60,7 +60,7 @@ data class ReactionInfo( */ class ViewReactionsViewModel @AssistedInject constructor(@Assisted initialState: DisplayReactionsViewState, - private val session: Session, + session: Session, private val dateFormatter: VectorDateFormatter ) : VectorViewModel(initialState) { diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItem.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItem.kt index ddb51e4e64..06f55d3952 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItem.kt @@ -31,7 +31,7 @@ import im.vector.app.R import im.vector.app.core.epoxy.VectorEpoxyHolder import im.vector.app.core.epoxy.VectorEpoxyModel import im.vector.app.core.extensions.setTextOrHide -import im.vector.app.features.crypto.util.toImageRes +import im.vector.app.core.ui.views.ShieldImageView import im.vector.app.features.home.AvatarRenderer import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel import org.matrix.android.sdk.api.util.MatrixItem @@ -73,8 +73,7 @@ abstract class RoomSummaryItem : VectorEpoxyModel() { holder.unreadIndentIndicator.isVisible = hasUnreadMessage holder.draftView.isVisible = hasDraft avatarRenderer.render(matrixItem, holder.avatarImageView) - holder.roomAvatarDecorationImageView.isVisible = encryptionTrustLevel != null - holder.roomAvatarDecorationImageView.setImageResource(encryptionTrustLevel.toImageRes()) + holder.roomAvatarDecorationImageView.render(encryptionTrustLevel) holder.roomAvatarFailSendingImageView.isVisible = hasFailedSending renderSelection(holder, showSelected) holder.typingView.setTextOrHide(typingMessage) @@ -110,7 +109,7 @@ abstract class RoomSummaryItem : VectorEpoxyModel() { val lastEventTimeView by bind(R.id.roomLastEventTimeView) val avatarCheckedImageView by bind(R.id.roomAvatarCheckedImageView) val avatarImageView by bind(R.id.roomAvatarImageView) - val roomAvatarDecorationImageView by bind(R.id.roomAvatarDecorationImageView) + val roomAvatarDecorationImageView by bind(R.id.roomAvatarDecorationImageView) val roomAvatarFailSendingImageView by bind(R.id.roomAvatarFailSendingImageView) val rootView by bind(R.id.itemRoomLayout) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItemFactory.kt index 06cb0172d0..988b3769c4 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItemFactory.kt @@ -16,7 +16,6 @@ package im.vector.app.features.home.room.list -import android.view.View import im.vector.app.R import im.vector.app.core.date.DateFormatKind import im.vector.app.core.date.VectorDateFormatter @@ -109,7 +108,7 @@ class RoomSummaryItemFactory @Inject constructor(private val displayableEventFor onLongClick?.invoke(roomSummary) ?: false } .itemClickListener( - DebouncedClickListener(View.OnClickListener { _ -> + DebouncedClickListener({ onClick?.invoke(roomSummary) }) ) diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsViewModel.kt index c51c571815..75e9459d2c 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsViewModel.kt @@ -29,7 +29,7 @@ import org.matrix.android.sdk.rx.rx import org.matrix.android.sdk.rx.unwrap class RoomListQuickActionsViewModel @AssistedInject constructor(@Assisted initialState: RoomListQuickActionsState, - private val session: Session + session: Session ) : VectorViewModel(initialState) { @AssistedFactory diff --git a/vector/src/main/java/im/vector/app/features/login/LoginFragment.kt b/vector/src/main/java/im/vector/app/features/login/LoginFragment.kt index 3b22e0f206..5ba1fa394b 100644 --- a/vector/src/main/java/im/vector/app/features/login/LoginFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login/LoginFragment.kt @@ -36,7 +36,6 @@ import im.vector.app.core.extensions.showPassword import im.vector.app.core.extensions.toReducedUrl import im.vector.app.databinding.FragmentLoginBinding import io.reactivex.Observable -import io.reactivex.functions.BiFunction import io.reactivex.rxkotlin.subscribeBy import org.matrix.android.sdk.api.failure.Failure @@ -225,7 +224,7 @@ class LoginFragment @Inject constructor() : AbstractSSOLoginFragment { isLoginNotEmpty, isPasswordNotEmpty -> + { isLoginNotEmpty, isPasswordNotEmpty -> isLoginNotEmpty && isPasswordNotEmpty } ) @@ -255,14 +254,7 @@ class LoginFragment @Inject constructor() : AbstractSSOLoginFragment { isEmail, isPasswordNotEmpty -> + { isEmail, isPasswordNotEmpty -> isEmail && isPasswordNotEmpty } ) @@ -127,14 +126,7 @@ class LoginResetPasswordFragment @Inject constructor() : AbstractLoginFragment { views.resetPasswordEmailTil.error = errorFormatter.toHumanReadable(state.asyncResetPassword.error) } - is Success -> { - Unit - } + is Success -> Unit } } } diff --git a/vector/src/main/java/im/vector/app/features/login/LoginResetPasswordMailConfirmationFragment.kt b/vector/src/main/java/im/vector/app/features/login/LoginResetPasswordMailConfirmationFragment.kt index 72f1ca801b..892fd92f95 100644 --- a/vector/src/main/java/im/vector/app/features/login/LoginResetPasswordMailConfirmationFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login/LoginResetPasswordMailConfirmationFragment.kt @@ -74,9 +74,7 @@ class LoginResetPasswordMailConfirmationFragment @Inject constructor() : Abstrac .setPositiveButton(R.string.ok, null) .show() } - is Success -> { - Unit - } + is Success -> Unit } } } diff --git a/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheet.kt b/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheet.kt index ca15efe25a..1d897477a2 100644 --- a/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheet.kt @@ -138,7 +138,7 @@ class MatrixToBottomSheet : fun withLink(matrixToLink: String, listener: InteractionListener?): MatrixToBottomSheet { return MatrixToBottomSheet().apply { arguments = Bundle().apply { - putParcelable(MvRx.KEY_ARG, MatrixToBottomSheet.MatrixToArgs( + putParcelable(MvRx.KEY_ARG, MatrixToArgs( matrixToLink = matrixToLink )) } diff --git a/vector/src/main/java/im/vector/app/features/media/VectorAttachmentViewerActivity.kt b/vector/src/main/java/im/vector/app/features/media/VectorAttachmentViewerActivity.kt index c632a008ce..158dbfdaae 100644 --- a/vector/src/main/java/im/vector/app/features/media/VectorAttachmentViewerActivity.kt +++ b/vector/src/main/java/im/vector/app/features/media/VectorAttachmentViewerActivity.kt @@ -29,6 +29,7 @@ import androidx.core.view.ViewCompat import androidx.core.view.isInvisible import androidx.core.view.isVisible import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope import androidx.transition.Transition import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder @@ -131,7 +132,7 @@ class VectorAttachmentViewerActivity : AttachmentViewerActivity(), BaseAttachmen if (savedInstanceState == null) { pager2.setCurrentItem(initialIndex, false) // The page change listener is not notified of the change... - pager2.post { + lifecycleScope.launchWhenResumed { onSelectedPositionChanged(initialIndex) } } diff --git a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt index d130512611..76e4cad28f 100644 --- a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt +++ b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt @@ -45,6 +45,7 @@ import im.vector.app.features.crypto.recover.SetupMode import im.vector.app.features.crypto.verification.SupportedVerificationMethodsProvider import im.vector.app.features.crypto.verification.VerificationBottomSheet import im.vector.app.features.debug.DebugMenuActivity +import im.vector.app.features.devtools.RoomDevToolActivity import im.vector.app.features.home.room.detail.RoomDetailActivity import im.vector.app.features.home.room.detail.RoomDetailArgs import im.vector.app.features.home.room.detail.search.SearchActivity @@ -357,6 +358,10 @@ class DefaultNavigator @Inject constructor( context.startActivity(intent) } + override fun openDevTools(context: Context, roomId: String) { + context.startActivity(RoomDevToolActivity.intent(context, roomId)) + } + override fun openCallTransfer(context: Context, callId: String) { val intent = CallTransferActivity.newIntent(context, callId) context.startActivity(intent) diff --git a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt index 8d30a2dd46..b4bd677b0c 100644 --- a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt +++ b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt @@ -118,5 +118,7 @@ interface Navigator { fun openSearch(context: Context, roomId: String) + fun openDevTools(context: Context, roomId: String) + fun openCallTransfer(context: Context, callId: String) } diff --git a/vector/src/main/java/im/vector/app/features/rageshake/BugReporter.kt b/vector/src/main/java/im/vector/app/features/rageshake/BugReporter.kt index 1a03fc6c47..a43aca488d 100755 --- a/vector/src/main/java/im/vector/app/features/rageshake/BugReporter.kt +++ b/vector/src/main/java/im/vector/app/features/rageshake/BugReporter.kt @@ -379,7 +379,7 @@ class BugReporter @Inject constructor( if (responseCode != HttpURLConnection.HTTP_OK) { if (null != errorMessage) { serverError = "Failed with error $errorMessage" - } else if (null == response || null == response.body) { + } else if (response?.body == null) { serverError = "Failed with error $responseCode" } else { try { diff --git a/vector/src/main/java/im/vector/app/features/reactions/EmojiReactionPickerActivity.kt b/vector/src/main/java/im/vector/app/features/reactions/EmojiReactionPickerActivity.kt index 992e677723..b2f1c3c15a 100644 --- a/vector/src/main/java/im/vector/app/features/reactions/EmojiReactionPickerActivity.kt +++ b/vector/src/main/java/im/vector/app/features/reactions/EmojiReactionPickerActivity.kt @@ -26,7 +26,6 @@ import android.view.MenuItem import android.widget.SearchView import androidx.appcompat.widget.Toolbar import androidx.core.view.isVisible -import androidx.lifecycle.Observer import com.airbnb.mvrx.viewModel import com.google.android.material.tabs.TabLayout import com.jakewharton.rxbinding3.widget.queryTextChanges @@ -107,13 +106,13 @@ class EmojiReactionPickerActivity : VectorBaseActivity + viewModel.currentSection.observe(this) { section -> section?.let { views.tabs.removeOnTabSelectedListener(tabLayoutSelectionListener) views.tabs.getTabAt(it)?.select() views.tabs.addOnTabSelectedListener(tabLayoutSelectionListener) } - }) + } viewModel.navigateEvent.observeEvent(this) { if (it == EmojiChooserViewModel.NAVIGATE_FINISH) { diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt index 688f74ba5d..b47767dddc 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt @@ -53,6 +53,7 @@ import im.vector.app.features.home.room.detail.RoomDetailPendingActionStore import im.vector.app.features.roommemberprofile.devices.DeviceListBottomSheet import im.vector.app.features.roommemberprofile.powerlevel.EditPowerLevelDialogs import kotlinx.parcelize.Parcelize +import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel import org.matrix.android.sdk.api.session.room.powerlevels.Role import org.matrix.android.sdk.api.util.MatrixItem import javax.inject.Inject @@ -205,31 +206,28 @@ class RoomMemberProfileFragment @Inject constructor( if (state.isRoomEncrypted) { headerViews.memberProfileDecorationImageView.isVisible = true - if (state.userMXCrossSigningInfo != null) { + val trustLevel = if (state.userMXCrossSigningInfo != null) { // Cross signing is enabled for this user - val icon = if (state.userMXCrossSigningInfo.isTrusted()) { + if (state.userMXCrossSigningInfo.isTrusted()) { // User is trusted if (state.allDevicesAreCrossSignedTrusted) { - R.drawable.ic_shield_trusted + RoomEncryptionTrustLevel.Trusted } else { - R.drawable.ic_shield_warning + RoomEncryptionTrustLevel.Warning } } else { - R.drawable.ic_shield_black + RoomEncryptionTrustLevel.Default } - - headerViews.memberProfileDecorationImageView.setImageResource(icon) - views.matrixProfileDecorationToolbarAvatarImageView.setImageResource(icon) } else { // Legacy if (state.allDevicesAreTrusted) { - headerViews.memberProfileDecorationImageView.setImageResource(R.drawable.ic_shield_trusted) - views.matrixProfileDecorationToolbarAvatarImageView.setImageResource(R.drawable.ic_shield_trusted) + RoomEncryptionTrustLevel.Trusted } else { - headerViews.memberProfileDecorationImageView.setImageResource(R.drawable.ic_shield_warning) - views.matrixProfileDecorationToolbarAvatarImageView.setImageResource(R.drawable.ic_shield_warning) + RoomEncryptionTrustLevel.Warning } } + headerViews.memberProfileDecorationImageView.render(trustLevel) + views.matrixProfileDecorationToolbarAvatarImageView.render(trustLevel) } else { headerViews.memberProfileDecorationImageView.isVisible = false } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt index 70eb4dd40e..154483652b 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt @@ -44,7 +44,6 @@ import im.vector.app.core.utils.copyToClipboard import im.vector.app.core.utils.startSharePlainTextIntent import im.vector.app.databinding.FragmentMatrixProfileBinding import im.vector.app.databinding.ViewStubRoomProfileHeaderBinding -import im.vector.app.features.crypto.util.toImageRes import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.room.list.actions.RoomListActionsArgs import im.vector.app.features.home.room.list.actions.RoomListQuickActionsBottomSheet @@ -204,9 +203,8 @@ class RoomProfileFragment @Inject constructor( val matrixItem = it.toMatrixItem() avatarRenderer.render(matrixItem, headerViews.roomProfileAvatarView) avatarRenderer.render(matrixItem, views.matrixProfileToolbarAvatarImageView) - headerViews.roomProfileDecorationImageView.isVisible = it.roomEncryptionTrustLevel != null - headerViews.roomProfileDecorationImageView.setImageResource(it.roomEncryptionTrustLevel.toImageRes()) - views.matrixProfileDecorationToolbarAvatarImageView.setImageResource(it.roomEncryptionTrustLevel.toImageRes()) + headerViews.roomProfileDecorationImageView.render(it.roomEncryptionTrustLevel) + views.matrixProfileDecorationToolbarAvatarImageView.render(it.roomEncryptionTrustLevel) } } roomProfileController.setData(state) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListController.kt index 71ac7fcec4..eda461de14 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListController.kt @@ -63,7 +63,7 @@ class RoomMemberListController @Inject constructor( ?.filter { event -> event.content.toModel() ?.takeIf { - data.filter.isEmpty() || it.displayName.contains(data.filter, ignoreCase = true) + data.filter.isEmpty() || it.displayName?.contains(data.filter, ignoreCase = true) == true } != null } .orEmpty() diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt index de983537fc..f08f8906b8 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt @@ -29,7 +29,6 @@ import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.powerlevel.PowerLevelsObservableFactory import io.reactivex.Observable import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.functions.BiFunction import kotlinx.coroutines.launch import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel import org.matrix.android.sdk.api.extensions.orFalse @@ -90,7 +89,7 @@ class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState .liveStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.NoCondition) .mapOptional { it.content.toModel() } .unwrap(), - BiFunction { roomMembers, powerLevelsContent -> + { roomMembers, powerLevelsContent -> buildRoomMemberSummaries(powerLevelsContent, roomMembers) } ) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt index 30ba0529ce..e431dbfcd6 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt @@ -199,7 +199,6 @@ class RoomSettingsFragment @Inject constructor( } RoomSettingsViewState.AvatarAction.DeleteAvatar -> { /* Should not happen */ - Unit } is RoomSettingsViewState.AvatarAction.UpdateAvatar -> { // Cancel the update of the avatar diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsFragment.kt index 00550f6fc5..3867485e6f 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsFragment.kt @@ -96,6 +96,7 @@ class RoomUploadsFragment @Inject constructor( private fun renderRoomSummary(state: RoomUploadsViewState) { state.roomSummary()?.let { views.roomUploadsToolbarTitleView.text = it.displayName + views.roomUploadsDecorationToolbarAvatarImageView.render(it.roomEncryptionTrustLevel) avatarRenderer.render(it.toMatrixItem(), views.roomUploadsToolbarAvatarImageView) } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/media/UploadsImageItem.kt b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/media/UploadsImageItem.kt index 2294095623..b844883e69 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/media/UploadsImageItem.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/media/UploadsImageItem.kt @@ -38,7 +38,7 @@ abstract class UploadsImageItem : VectorEpoxyModel() { override fun bind(holder: Holder) { super.bind(holder) holder.view.setOnClickListener( - DebouncedClickListener(View.OnClickListener { _ -> + DebouncedClickListener({ listener?.onItemClicked(holder.imageView, data) }) ) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/media/UploadsVideoItem.kt b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/media/UploadsVideoItem.kt index 908e3ba72c..66138a70b8 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/media/UploadsVideoItem.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/media/UploadsVideoItem.kt @@ -39,7 +39,7 @@ abstract class UploadsVideoItem : VectorEpoxyModel() { override fun bind(holder: Holder) { super.bind(holder) holder.view.setOnClickListener( - DebouncedClickListener(View.OnClickListener { _ -> + DebouncedClickListener({ listener?.onItemClicked(holder.imageView, data) }) ) diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt index ac4088145e..b66a37b75f 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt @@ -361,7 +361,7 @@ class VectorSettingsGeneralFragment @Inject constructor( views.changePasswordNewPwdText.showPassword(passwordShown) views.changePasswordConfirmNewPwdText.showPassword(passwordShown) - views.changePasswordShowPasswords.setImageResource(if (passwordShown) R.drawable.ic_eye_closed else R.drawable.ic_eye) + views.changePasswordShowPasswords.render(passwordShown) } val dialog = AlertDialog.Builder(activity) diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt index c12df073ee..31fdda24a2 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt @@ -458,7 +458,7 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor( views.importDialogShowPassword.setOnClickListener { passwordVisible = !passwordVisible views.dialogE2eKeysPassphraseEditText.showPassword(passwordVisible) - views.importDialogShowPassword.setImageResource(if (passwordVisible) R.drawable.ic_eye_closed else R.drawable.ic_eye) + views.importDialogShowPassword.render(passwordVisible) } views.dialogE2eKeysPassphraseEditText.addTextChangedListener(object : SimpleTextWatcher() { diff --git a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewModel.kt index 04100056aa..566dea2cd4 100644 --- a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewModel.kt @@ -29,7 +29,6 @@ import im.vector.app.core.resources.StringProvider import im.vector.app.features.auth.ReAuthActivity import im.vector.app.features.login.ReAuthHelper import io.reactivex.Observable -import io.reactivex.functions.BiFunction import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.matrix.android.sdk.api.auth.UIABaseAuth @@ -62,7 +61,7 @@ class CrossSigningSettingsViewModel @AssistedInject constructor( Observable.combineLatest, Optional, Pair, Optional>>( session.rx().liveMyDevicesInfo(), session.rx().liveCrossSigningInfo(session.myUserId), - BiFunction { myDevicesInfo, mxCrossSigningInfo -> + { myDevicesInfo, mxCrossSigningInfo -> myDevicesInfo to mxCrossSigningInfo } ) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceItem.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceItem.kt index 49840cdd64..62c0b6149f 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceItem.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceItem.kt @@ -18,7 +18,6 @@ package im.vector.app.features.settings.devices import android.graphics.Typeface import android.view.ViewGroup -import android.widget.ImageView import android.widget.TextView import androidx.core.view.isVisible import com.airbnb.epoxy.EpoxyAttribute @@ -27,6 +26,7 @@ import im.vector.app.R import im.vector.app.core.epoxy.VectorEpoxyHolder import im.vector.app.core.epoxy.VectorEpoxyModel import im.vector.app.core.resources.ColorProvider +import im.vector.app.core.ui.views.ShieldImageView import im.vector.app.core.utils.DimensionConverter import me.gujun.android.span.span import org.matrix.android.sdk.internal.crypto.crosssigning.DeviceTrustLevel @@ -75,17 +75,17 @@ abstract class DeviceItem : VectorEpoxyModel() { super.bind(holder) holder.root.setOnClickListener { itemClickAction?.invoke() } - val shield = TrustUtils.shieldForTrust( - currentDevice, - trustedSession, - legacyMode, - trusted - ) - if (e2eCapable) { - holder.trustIcon.setImageResource(shield) + val shield = TrustUtils.shieldForTrust( + currentDevice, + trustedSession, + legacyMode, + trusted + ) + + holder.trustIcon.render(shield) } else { - holder.trustIcon.setImageDrawable(null) + holder.trustIcon.render(null) } val detailedModeLabels = listOf( @@ -152,6 +152,6 @@ abstract class DeviceItem : VectorEpoxyModel() { val deviceLastSeenLabelText by bind(R.id.itemDeviceLastSeenLabel) val deviceLastSeenText by bind(R.id.itemDeviceLastSeen) - val trustIcon by bind(R.id.itemDeviceTrustLevelIcon) + val trustIcon by bind(R.id.itemDeviceTrustLevelIcon) } } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetController.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetController.kt index 25a98bb4b5..100d4c97c5 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetController.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetController.kt @@ -24,6 +24,7 @@ import im.vector.app.core.resources.StringProvider import im.vector.app.core.ui.list.GenericItem import im.vector.app.core.ui.list.genericFooterItem import im.vector.app.core.ui.list.genericItem +import im.vector.app.core.ui.views.toDrawableRes import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationActionItem import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo @@ -62,7 +63,7 @@ class DeviceVerificationInfoBottomSheetController @Inject constructor( trustMSK = data.accountCrossSigningIsTrusted, legacyMode = !data.hasAccountCrossSigning, deviceTrustLevel = cryptoDeviceInfo.trustLevel - ) + ).toDrawableRes() if (data.hasAccountCrossSigning) { // Cross Signing is enabled diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt index b91b5255b6..a56a7b8d48 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt @@ -35,7 +35,6 @@ import im.vector.app.core.resources.StringProvider import im.vector.app.features.auth.ReAuthActivity import im.vector.app.features.login.ReAuthHelper import io.reactivex.Observable -import io.reactivex.functions.BiFunction import io.reactivex.subjects.PublishSubject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -121,7 +120,7 @@ class DevicesViewModel @AssistedInject constructor( Observable.combineLatest, List, List>( session.rx().liveUserCryptoDevices(session.myUserId), session.rx().liveMyDevicesInfo(), - BiFunction { cryptoList, infoList -> + { cryptoList, infoList -> infoList .sortedByDescending { it.lastSeenTs } .map { deviceInfo -> @@ -239,7 +238,6 @@ class DevicesViewModel @AssistedInject constructor( uiaContinuation?.resumeWith(Result.failure((Exception()))) uiaContinuation = null pendingAuth = null - Unit } } } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/TrustUtils.kt b/vector/src/main/java/im/vector/app/features/settings/devices/TrustUtils.kt index 80cd91664d..06ef96daf7 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/TrustUtils.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/TrustUtils.kt @@ -16,38 +16,50 @@ package im.vector.app.features.settings.devices -import androidx.annotation.DrawableRes -import im.vector.app.R +import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel import org.matrix.android.sdk.internal.crypto.crosssigning.DeviceTrustLevel object TrustUtils { - @DrawableRes - fun shieldForTrust(currentDevice: Boolean, trustMSK: Boolean, legacyMode: Boolean, deviceTrustLevel: DeviceTrustLevel?): Int { + fun shieldForTrust(currentDevice: Boolean, + trustMSK: Boolean, + legacyMode: Boolean, + deviceTrustLevel: DeviceTrustLevel?): RoomEncryptionTrustLevel { return when { currentDevice -> { if (legacyMode) { // In legacy, current session is always trusted - R.drawable.ic_shield_trusted + RoomEncryptionTrustLevel.Trusted } else { // If current session doesn't trust MSK, show red shield for current device - R.drawable.ic_shield_trusted.takeIf { trustMSK } ?: R.drawable.ic_shield_warning + if (trustMSK) { + RoomEncryptionTrustLevel.Trusted + } else { + RoomEncryptionTrustLevel.Warning + } } } else -> { if (legacyMode) { // use local trust - R.drawable.ic_shield_trusted.takeIf { deviceTrustLevel?.locallyVerified == true } ?: R.drawable.ic_shield_warning + if (deviceTrustLevel?.locallyVerified == true) { + RoomEncryptionTrustLevel.Trusted + } else { + RoomEncryptionTrustLevel.Warning + } } else { if (trustMSK) { // use cross sign trust, put locally trusted in black - R.drawable.ic_shield_trusted.takeIf { deviceTrustLevel?.crossSigningVerified == true } - ?: R.drawable.ic_shield_black.takeIf { deviceTrustLevel?.locallyVerified == true } - ?: R.drawable.ic_shield_warning + when { + deviceTrustLevel?.crossSigningVerified == true -> RoomEncryptionTrustLevel.Trusted + + deviceTrustLevel?.locallyVerified == true -> RoomEncryptionTrustLevel.Default + else -> RoomEncryptionTrustLevel.Warning + } } else { // The current session is untrusted, so displays others in black // as we can't know the cross-signing state - R.drawable.ic_shield_black + RoomEncryptionTrustLevel.Default } } } diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataEpoxyController.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataEpoxyController.kt index f06e23a366..13d7e0f396 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataEpoxyController.kt @@ -68,7 +68,7 @@ class AccountDataEpoxyController @Inject constructor( genericItemWithValue { id(accountData.type) title(accountData.type) - itemClickAction(DebouncedClickListener(View.OnClickListener { + itemClickAction(DebouncedClickListener({ interactionListener?.didTap(accountData) })) itemLongClickAction(View.OnLongClickListener { diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestsFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestsFragment.kt index 13b8581734..7bee0e283c 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestsFragment.kt @@ -67,7 +67,7 @@ class KeyRequestsFragment @Inject constructor( override fun onPageScrollStateChanged(state: Int) { childFragmentManager.fragments.forEach { - setHasOptionsMenu(state == SCROLL_STATE_IDLE) + it.setHasOptionsMenu(state == SCROLL_STATE_IDLE) } invalidateOptionsMenu() } diff --git a/vector/src/main/java/im/vector/app/features/settings/push/PushRuleItem.kt b/vector/src/main/java/im/vector/app/features/settings/push/PushRuleItem.kt index 0144b162e9..332033d10e 100644 --- a/vector/src/main/java/im/vector/app/features/settings/push/PushRuleItem.kt +++ b/vector/src/main/java/im/vector/app/features/settings/push/PushRuleItem.kt @@ -59,10 +59,13 @@ abstract class PushRuleItem : EpoxyModelWithHolder() { if (notifAction.shouldNotify && !notifAction.soundName.isNullOrBlank()) { holder.actionIcon.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_action_notify_noisy)) + holder.actionIcon.contentDescription = context.getString(R.string.a11y_rule_notify_noisy) } else if (notifAction.shouldNotify) { holder.actionIcon.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_action_notify_silent)) + holder.actionIcon.contentDescription = context.getString(R.string.a11y_rule_notify_silent) } else { holder.actionIcon.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_action_dont_notify)) + holder.actionIcon.contentDescription = context.getString(R.string.a11y_rule_notify_off) } val description = StringBuffer() diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/NotificationTroubleshootRecyclerViewAdapter.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/NotificationTroubleshootRecyclerViewAdapter.kt index 1467c1fa9a..d9ed590938 100644 --- a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/NotificationTroubleshootRecyclerViewAdapter.kt +++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/NotificationTroubleshootRecyclerViewAdapter.kt @@ -98,7 +98,7 @@ class NotificationTroubleshootRecyclerViewAdapter(val tests: ArrayList + troubleshootTestButton.setOnClickListener { test.quickFix!!.doFix() } troubleshootTestButton.visibility = View.VISIBLE diff --git a/vector/src/main/java/im/vector/app/features/share/IncomingShareFragment.kt b/vector/src/main/java/im/vector/app/features/share/IncomingShareFragment.kt index defce49f5a..f89480046f 100644 --- a/vector/src/main/java/im/vector/app/features/share/IncomingShareFragment.kt +++ b/vector/src/main/java/im/vector/app/features/share/IncomingShareFragment.kt @@ -121,7 +121,7 @@ class IncomingShareFragment @Inject constructor( return true } }) - views.sendShareButton.setOnClickListener { _ -> + views.sendShareButton.setOnClickListener { handleSendShare() } } diff --git a/vector/src/main/java/im/vector/app/features/signout/soft/epoxy/LoginPasswordFormItem.kt b/vector/src/main/java/im/vector/app/features/signout/soft/epoxy/LoginPasswordFormItem.kt index 32f296385f..79ab18942b 100644 --- a/vector/src/main/java/im/vector/app/features/signout/soft/epoxy/LoginPasswordFormItem.kt +++ b/vector/src/main/java/im/vector/app/features/signout/soft/epoxy/LoginPasswordFormItem.kt @@ -19,7 +19,6 @@ package im.vector.app.features.signout.soft.epoxy import android.os.Build import android.text.Editable import android.widget.Button -import android.widget.ImageView import androidx.autofill.HintConstants import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyModelClass @@ -31,6 +30,7 @@ import im.vector.app.core.epoxy.VectorEpoxyModel import im.vector.app.core.extensions.showPassword import im.vector.app.core.platform.SimpleTextWatcher import im.vector.app.core.resources.StringProvider +import im.vector.app.core.ui.views.RevealPasswordImageView @EpoxyModelClass(layout = R.layout.item_login_password_form) abstract class LoginPasswordFormItem : VectorEpoxyModel() { @@ -76,20 +76,13 @@ abstract class LoginPasswordFormItem : VectorEpoxyModel(R.id.itemLoginPasswordFormPasswordField) val passwordFieldTil by bind(R.id.itemLoginPasswordFormPasswordFieldTil) - val passwordReveal by bind(R.id.itemLoginPasswordFormPasswordReveal) + val passwordReveal by bind(R.id.itemLoginPasswordFormPasswordReveal) val forgetPassword by bind