Compare commits

..

1207 commits
c3 ... master

Author SHA1 Message Date
kosyak e9659f0fa3 fix disappearing bookmarks (issue #12) 2024-08-18 14:03:49 +02:00
kosyak ce40742ddf bump to 2.3.1 2024-08-12 10:30:07 +02:00
kosyak 5034778a7d get rid of jcenter 2024-08-12 10:19:51 +02:00
kosyak 3d5ecaf376 fix crash on group chats screen 2024-08-12 10:19:31 +02:00
kosyak a124b7e790 fix broken replies on conversation reopen 2024-08-06 20:12:05 +02:00
Bohdan Horbeshko 9f3fe75342 update fastlane metadata && bump version code (#5)
Reviewed-on: #5
Reviewed-by: kosyak <kosyak@narayana.im>
Co-authored-by: Bohdan Horbeshko <bodqhrohro@gmail.com>
Co-committed-by: Bohdan Horbeshko <bodqhrohro@gmail.com>
2024-08-06 18:03:50 +00:00
Sergei Poljanski 37aa0160b6 fdroid/build (#6)
Fix gradle warns for F-Droid  issues bot

Reviewed-on: #6
Reviewed-by: kosyak <kosyak@narayana.im>
Co-authored-by: Sergei Poljanski <me@asxp.io>
Co-committed-by: Sergei Poljanski <me@asxp.io>
2024-08-06 18:03:27 +00:00
Sergei Poljanski 1301f43eba lang/ru (#7)
Russian translation from Codeberg

Reviewed-on: #7
Reviewed-by: kosyak <kosyak@narayana.im>
Co-authored-by: Sergei Poljanski <me@asxp.io>
Co-committed-by: Sergei Poljanski <me@asxp.io>
2024-08-06 18:02:46 +00:00
Sergei Poljanski 66f56c25d6 Merge pull request 'eliminate hardcoded applicationId' (#8) from appid-hardcode-fix into master
Reviewed-on: #8
Reviewed-by: kosyak <kosyak@narayana.im>
2024-08-06 18:01:52 +00:00
Sergei Poljanski 689386fda6 Merge pull request 'hruuu' (#9) from hruuu into master
Reviewed-on: #9
Reviewed-by: kosyak <kosyak@narayana.im>
2024-08-06 18:01:07 +00:00
Bohdan Horbeshko 229bd9133b hruuu 2024-08-06 18:01:07 +00:00
Sergei Poljanski 4d52ee060d Merge pull request 'readme' (#11) from readme into master
Reviewed-on: #11
Reviewed-by: kosyak <kosyak@narayana.im>
2024-08-06 18:00:21 +00:00
Sergei Poljanski 3763829da0
about 2024-08-06 20:37:47 +03:00
Sergei Poljanski 97073a6b1e
updated readme 2024-08-06 18:41:50 +03:00
kosyak 4c2dd3e933 pinned message brighter background color 2024-08-06 09:37:53 +02:00
Bohdan Horbeshko 87bf8aa138 eliminate hardcoded applicationId 2024-08-06 06:55:36 +03:00
kosyak cfdaaec86e better ellipsize for subject text 2024-08-05 23:38:07 +02:00
kosyak 869182d640 add nick into reply fallback only for MUC 2024-08-05 23:28:54 +02:00
kosyak d7d922c1d1 account indicator near conversations 2024-08-05 23:18:55 +02:00
kosyak 9892ff8d77 better replies fallback 2024-08-05 22:54:41 +02:00
kosyak 42d59adc78 show only contacts in contacts tab 2024-08-05 22:38:51 +02:00
kosyak cf9d0c4f09 show contact status in a separate widget 2024-08-05 22:34:10 +02:00
kosyak f709e32805 mark group chats with additional icon 2024-08-05 21:20:22 +02:00
kosyak 2b2ecbb44e fix wired 'Hide offline' behavior 2024-08-05 21:19:52 +02:00
kosyak 1fa72c42a0 bump to 2.3.0 2024-07-28 09:21:51 -04:00
kosyak 07bef730aa muc imporvements 2024-07-28 09:20:35 -04:00
kosyak e94aae746d new settings screen 2024-07-21 13:49:31 +02:00
kosyak 14c3df535d medium font for unread conversation title 2024-07-21 12:29:15 +02:00
kosyak 21658d20d0 support avatars shape customization 2024-07-21 12:00:17 +02:00
kosyak 7ef0e53892 use original file name in chat attachements 2024-07-14 18:05:34 +02:00
kosyak 481f5ebfc1 per conversation custom backgrounds 2024-07-14 17:01:50 +02:00
kosyak 4453ad71ac support navigation via nav bar 2024-07-14 15:01:05 +02:00
kosyak c145a6b8e5 Revert "OTR implementation"
This reverts commit 900c72ed98.
2024-07-14 12:23:22 +02:00
kosyak 61af01ff28 Revert "otr fixes"
This reverts commit a136138daa.
2024-07-14 12:23:01 +02:00
kosyak 516f6321ef Merge branch 'master' of https://dev.narayana.im/narayana/Conversations 2024-07-14 12:22:41 +02:00
kosyak a136138daa otr fixes 2024-07-14 12:22:02 +02:00
kosyak 900c72ed98 OTR implementation 2024-07-07 23:23:47 +02:00
kosyak e3a4d0f36e Merge pull request 'hruu' (#3) from hruu into master
Reviewed-on: #3
Reviewed-by: kosyak <kosyak@narayana.im>
2024-06-22 07:05:04 +00:00
Bohdan Horbeshko af32590eaf hruu 2024-06-18 02:53:05 +03:00
kosyak 3d5794ea72 fix gestures handling conflict in commands 2024-06-17 11:17:02 +02:00
Sergei Poljanski f27240a611 Merge pull request 'hru' (#2) from hru into master
Reviewed-on: narayana/Conversations-Classic#2
Reviewed-by: kosyak <kosyak@narayana.im>
2024-06-17 07:27:59 +00:00
Sergei Poljanski 629838f27c Merge pull request 'ru lang' (#1) from lang/ru into master
Reviewed-on: narayana/Conversations-Classic#1
Reviewed-by: kosyak <kosyak@narayana.im>
2024-06-16 23:50:45 +00:00
Bohdan Horbeshko 126b826522 hru 2024-06-17 01:41:05 +03:00
kosyak ece06b9651 notifications throttling setting for all conversations 2024-06-16 23:52:38 +02:00
kosyak 9f0b59af9f update caps node field 2024-06-16 22:54:02 +02:00
Sergei Poljanski a5e45ca995
ru lang 2024-06-16 19:37:59 +03:00
kosyak 25cd65e4e7 fix save to downloads menu item visibility 2024-06-05 22:40:34 +01:00
kosyak d037e940c7 add cheogram mention on about screen 2024-06-05 22:38:32 +01:00
kosyak e8f624e2cb bump version 2024-06-02 20:31:35 +02:00
kosyak e745dae1dc properly show contact name in muc 2024-06-02 20:31:20 +02:00
kosyak 3f43dc6d5f fix conversation position handling while grouping enabled 2024-06-02 20:21:34 +02:00
kosyak 018120cfe6 render reply message author name on the top of replied text 2024-06-02 20:03:29 +02:00
kosyak a7a00b70d7 clean code and optimize imports 2024-05-28 15:38:55 +02:00
kosyak 410daee677 bump version 2024-05-28 14:44:00 +02:00
kosyak 3f8874361b fix contact long click crash 2024-05-28 14:43:27 +02:00
kosyak 379300bf02 get rid of outdated kernel version calculation code 2024-05-26 19:07:19 +02:00
kosyak 00606061ce bump version 2024-05-22 21:35:52 +02:00
kosyak 6891471ce8 fix 2024-05-22 21:34:30 +02:00
kosyak bb95bdf541 bump version 2024-05-22 21:33:10 +02:00
kosyak d42486c72c fix reactions handling in case of missing reply stanza 2024-05-22 21:31:23 +02:00
kosyak 7b8a61e068 per contact notifications throttling settings 2024-05-22 21:29:23 +02:00
kosyak 0494569a29 fix invalid ui state 2024-05-22 20:55:34 +02:00
kosyak 8c78febaa8 improve replies on file or image messages 2024-05-22 20:30:58 +02:00
kosyak 350b36a2d6 revert reply fallback handling fix 2024-05-22 09:56:57 +02:00
kosyak ead327d30a fix replies without callback handling 2024-05-21 21:26:36 +02:00
kosyak 162b0bf928 Revert "Revert "fix reply text handling""
This reverts commit 705cc33347.
2024-05-21 21:25:45 +02:00
kosyak 705cc33347 Revert "fix reply text handling"
This reverts commit 8c1d825bf3.
2024-05-21 21:09:59 +02:00
kosyak 888ed9643f handle notifications throttling on backlog finish event 2024-05-21 17:23:54 +02:00
kosyak 501de4f997 fix last notification timestamp default value 2024-05-21 16:50:59 +02:00
kosyak 6a546b03e1 fix comparsion 2024-05-21 13:49:17 +02:00
Pavel R. 2c8e801ac0 update README.md 2024-05-21 00:51:15 +03:00
kosyak 87d449ba87 bump app id 2024-05-20 23:27:34 +02:00
kosyak 383c004b9e change version to 2.2.0 2024-05-20 22:50:10 +02:00
kosyak fb3e46e30a fix throttling default value 2024-05-20 22:48:08 +02:00
kosyak 8c1d825bf3 fix reply text handling 2024-05-18 14:33:48 +02:00
kosyak 920656b45d move restore backup button from context menu on welcome screen 2024-05-18 13:41:39 +02:00
kosyak c3489836c4 bump version to 3.0.0 2024-05-18 13:35:38 +02:00
kosyak ee57da5320 contact jid copy button 2024-05-18 13:33:30 +02:00
kosyak 753ec13f25 fix compilation 2024-05-18 13:31:47 +02:00
kosyak 0ac9e037c3 get rid of old bug reports system 2024-05-18 13:29:19 +02:00
kosyak f6c0a14299 update app name 2024-05-18 13:17:17 +02:00
kosyak de9874fdf7 notification sound throttling setting 2024-05-18 13:17:08 +02:00
kosyak 3db7413bf2 Revert "Merge branch 'master' of https://codeberg.org/iNPUTmice/Conversations"
This reverts commit 6e71e26044, reversing
changes made to 82b4208304.
2024-05-18 11:06:10 +02:00
kosyak 2c749c8d95 Revert "after merge imports optimiztion"
This reverts commit 999a45dc51.
2024-05-18 11:06:02 +02:00
kosyak 999a45dc51 after merge imports optimiztion 2024-05-18 11:05:39 +02:00
kosyak 6e71e26044 Merge branch 'master' of https://codeberg.org/iNPUTmice/Conversations
# Conflicts:
#	build.gradle
#	gradle/wrapper/gradle-wrapper.properties
#	src/conversations/java/eu/siacs/conversations/ui/ImportBackupActivity.java
#	src/conversations/java/eu/siacs/conversations/ui/ManageAccountActivity.java
#	src/conversations/res/drawable-hdpi/ic_notification.png
#	src/conversations/res/drawable-mdpi/ic_notification.png
#	src/conversations/res/drawable-xhdpi/ic_notification.png
#	src/conversations/res/drawable-xxhdpi/ic_notification.png
#	src/conversations/res/drawable-xxxhdpi/ic_notification.png
#	src/main/java/eu/siacs/conversations/entities/Bookmark.java
#	src/main/java/eu/siacs/conversations/entities/Contact.java
#	src/main/java/eu/siacs/conversations/entities/Conversation.java
#	src/main/java/eu/siacs/conversations/parser/MessageParser.java
#	src/main/java/eu/siacs/conversations/parser/PresenceParser.java
#	src/main/java/eu/siacs/conversations/services/NotificationService.java
#	src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
#	src/main/java/eu/siacs/conversations/ui/AboutActivity.java
#	src/main/java/eu/siacs/conversations/ui/ChannelDiscoveryActivity.java
#	src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java
#	src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java
#	src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
#	src/main/java/eu/siacs/conversations/ui/ConversationsActivity.java
#	src/main/java/eu/siacs/conversations/ui/ConversationsOverviewFragment.java
#	src/main/java/eu/siacs/conversations/ui/EnterJidDialog.java
#	src/main/java/eu/siacs/conversations/ui/MemorizingActivity.java
#	src/main/java/eu/siacs/conversations/ui/RecordingActivity.java
#	src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java
#	src/main/java/eu/siacs/conversations/ui/SettingsActivity.java
#	src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java
#	src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java
#	src/main/java/eu/siacs/conversations/ui/XmppActivity.java
#	src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java
#	src/main/java/eu/siacs/conversations/ui/adapter/MediaPreviewAdapter.java
#	src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java
#	src/main/java/eu/siacs/conversations/ui/text/FixedURLSpan.java
#	src/main/java/eu/siacs/conversations/ui/util/SendButtonTool.java
#	src/main/java/eu/siacs/conversations/utils/ThemeHelper.java
#	src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
#	src/main/java/eu/siacs/conversations/xmpp/jingle/JingleFileTransferConnection.java
#	src/main/java/eu/siacs/conversations/xmpp/jingle/ToneManager.java
#	src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java
#	src/main/res/drawable-hdpi/date_bubble_grey.9.png
#	src/main/res/drawable-hdpi/date_bubble_white.9.png
#	src/main/res/drawable-hdpi/ic_flip_camera_android_black_24dp.png
#	src/main/res/drawable-hdpi/ic_no_results_background_black.png
#	src/main/res/drawable-hdpi/ic_no_results_background_white.png
#	src/main/res/drawable-hdpi/ic_notifications_none_white80.png
#	src/main/res/drawable-hdpi/ic_notifications_off_white80.png
#	src/main/res/drawable-hdpi/ic_notifications_paused_white80.png
#	src/main/res/drawable-hdpi/ic_notifications_white80.png
#	src/main/res/drawable-hdpi/ic_qr_code_scan_white_24dp.png
#	src/main/res/drawable-hdpi/ic_search_background_black.png
#	src/main/res/drawable-hdpi/ic_search_background_white.png
#	src/main/res/drawable-hdpi/ic_send_cancel_away.png
#	src/main/res/drawable-hdpi/ic_send_cancel_dnd.png
#	src/main/res/drawable-hdpi/ic_send_cancel_offline.png
#	src/main/res/drawable-hdpi/ic_send_cancel_offline_white.png
#	src/main/res/drawable-hdpi/ic_send_cancel_online.png
#	src/main/res/drawable-hdpi/ic_send_location_away.png
#	src/main/res/drawable-hdpi/ic_send_location_dnd.png
#	src/main/res/drawable-hdpi/ic_send_location_offline.png
#	src/main/res/drawable-hdpi/ic_send_location_offline_white.png
#	src/main/res/drawable-hdpi/ic_send_location_online.png
#	src/main/res/drawable-hdpi/ic_send_photo_away.png
#	src/main/res/drawable-hdpi/ic_send_photo_dnd.png
#	src/main/res/drawable-hdpi/ic_send_photo_offline.png
#	src/main/res/drawable-hdpi/ic_send_photo_offline_white.png
#	src/main/res/drawable-hdpi/ic_send_photo_online.png
#	src/main/res/drawable-hdpi/ic_send_picture_away.png
#	src/main/res/drawable-hdpi/ic_send_picture_dnd.png
#	src/main/res/drawable-hdpi/ic_send_picture_offline.png
#	src/main/res/drawable-hdpi/ic_send_picture_offline_white.png
#	src/main/res/drawable-hdpi/ic_send_picture_online.png
#	src/main/res/drawable-hdpi/ic_send_text_away.png
#	src/main/res/drawable-hdpi/ic_send_text_dnd.png
#	src/main/res/drawable-hdpi/ic_send_text_offline.png
#	src/main/res/drawable-hdpi/ic_send_text_offline_white.png
#	src/main/res/drawable-hdpi/ic_send_text_online.png
#	src/main/res/drawable-hdpi/ic_send_videocam_away.png
#	src/main/res/drawable-hdpi/ic_send_videocam_dnd.png
#	src/main/res/drawable-hdpi/ic_send_videocam_offline.png
#	src/main/res/drawable-hdpi/ic_send_videocam_offline_white.png
#	src/main/res/drawable-hdpi/ic_send_videocam_online.png
#	src/main/res/drawable-hdpi/ic_send_voice_away.png
#	src/main/res/drawable-hdpi/ic_send_voice_dnd.png
#	src/main/res/drawable-hdpi/ic_send_voice_offline.png
#	src/main/res/drawable-hdpi/ic_send_voice_offline_white.png
#	src/main/res/drawable-hdpi/ic_send_voice_online.png
#	src/main/res/drawable-hdpi/ic_verified_fingerprint.png
#	src/main/res/drawable-hdpi/message_bubble_received.9.png
#	src/main/res/drawable-hdpi/message_bubble_received_dark.9.png
#	src/main/res/drawable-hdpi/message_bubble_received_grey.9.png
#	src/main/res/drawable-hdpi/message_bubble_received_warning.9.png
#	src/main/res/drawable-hdpi/message_bubble_received_white.9.png
#	src/main/res/drawable-hdpi/message_bubble_sent.9.png
#	src/main/res/drawable-hdpi/message_bubble_sent_grey.9.png
#	src/main/res/drawable-mdpi/date_bubble_grey.9.png
#	src/main/res/drawable-mdpi/date_bubble_white.9.png
#	src/main/res/drawable-mdpi/ic_flip_camera_android_black_24dp.png
#	src/main/res/drawable-mdpi/ic_no_results_background_black.png
#	src/main/res/drawable-mdpi/ic_no_results_background_white.png
#	src/main/res/drawable-mdpi/ic_notifications_none_white80.png
#	src/main/res/drawable-mdpi/ic_notifications_off_white80.png
#	src/main/res/drawable-mdpi/ic_notifications_paused_white80.png
#	src/main/res/drawable-mdpi/ic_notifications_white80.png
#	src/main/res/drawable-mdpi/ic_qr_code_scan_white_24dp.png
#	src/main/res/drawable-mdpi/ic_search_background_black.png
#	src/main/res/drawable-mdpi/ic_search_background_white.png
#	src/main/res/drawable-mdpi/ic_send_cancel_away.png
#	src/main/res/drawable-mdpi/ic_send_cancel_dnd.png
#	src/main/res/drawable-mdpi/ic_send_cancel_offline.png
#	src/main/res/drawable-mdpi/ic_send_cancel_offline_white.png
#	src/main/res/drawable-mdpi/ic_send_cancel_online.png
#	src/main/res/drawable-mdpi/ic_send_location_away.png
#	src/main/res/drawable-mdpi/ic_send_location_dnd.png
#	src/main/res/drawable-mdpi/ic_send_location_offline.png
#	src/main/res/drawable-mdpi/ic_send_location_offline_white.png
#	src/main/res/drawable-mdpi/ic_send_location_online.png
#	src/main/res/drawable-mdpi/ic_send_photo_away.png
#	src/main/res/drawable-mdpi/ic_send_photo_dnd.png
#	src/main/res/drawable-mdpi/ic_send_photo_offline.png
#	src/main/res/drawable-mdpi/ic_send_photo_offline_white.png
#	src/main/res/drawable-mdpi/ic_send_photo_online.png
#	src/main/res/drawable-mdpi/ic_send_picture_away.png
#	src/main/res/drawable-mdpi/ic_send_picture_dnd.png
#	src/main/res/drawable-mdpi/ic_send_picture_offline.png
#	src/main/res/drawable-mdpi/ic_send_picture_offline_white.png
#	src/main/res/drawable-mdpi/ic_send_picture_online.png
#	src/main/res/drawable-mdpi/ic_send_text_away.png
#	src/main/res/drawable-mdpi/ic_send_text_dnd.png
#	src/main/res/drawable-mdpi/ic_send_text_offline.png
#	src/main/res/drawable-mdpi/ic_send_text_offline_white.png
#	src/main/res/drawable-mdpi/ic_send_text_online.png
#	src/main/res/drawable-mdpi/ic_send_videocam_away.png
#	src/main/res/drawable-mdpi/ic_send_videocam_dnd.png
#	src/main/res/drawable-mdpi/ic_send_videocam_offline.png
#	src/main/res/drawable-mdpi/ic_send_videocam_offline_white.png
#	src/main/res/drawable-mdpi/ic_send_videocam_online.png
#	src/main/res/drawable-mdpi/ic_send_voice_away.png
#	src/main/res/drawable-mdpi/ic_send_voice_dnd.png
#	src/main/res/drawable-mdpi/ic_send_voice_offline.png
#	src/main/res/drawable-mdpi/ic_send_voice_offline_white.png
#	src/main/res/drawable-mdpi/ic_send_voice_online.png
#	src/main/res/drawable-mdpi/ic_verified_fingerprint.png
#	src/main/res/drawable-mdpi/message_bubble_received.9.png
#	src/main/res/drawable-mdpi/message_bubble_received_dark.9.png
#	src/main/res/drawable-mdpi/message_bubble_received_grey.9.png
#	src/main/res/drawable-mdpi/message_bubble_received_warning.9.png
#	src/main/res/drawable-mdpi/message_bubble_received_white.9.png
#	src/main/res/drawable-mdpi/message_bubble_sent.9.png
#	src/main/res/drawable-mdpi/message_bubble_sent_grey.9.png
#	src/main/res/drawable-mdpi/play_gif_black.png
#	src/main/res/drawable-mdpi/play_gif_white.png
#	src/main/res/drawable-mdpi/play_video_black.png
#	src/main/res/drawable-mdpi/play_video_white.png
#	src/main/res/drawable-xhdpi/date_bubble_grey.9.png
#	src/main/res/drawable-xhdpi/date_bubble_white.9.png
#	src/main/res/drawable-xhdpi/ic_flip_camera_android_black_24dp.png
#	src/main/res/drawable-xhdpi/ic_no_results_background_black.png
#	src/main/res/drawable-xhdpi/ic_no_results_background_white.png
#	src/main/res/drawable-xhdpi/ic_notifications_none_white80.png
#	src/main/res/drawable-xhdpi/ic_notifications_off_white80.png
#	src/main/res/drawable-xhdpi/ic_notifications_paused_white80.png
#	src/main/res/drawable-xhdpi/ic_notifications_white80.png
#	src/main/res/drawable-xhdpi/ic_qr_code_scan_white_24dp.png
#	src/main/res/drawable-xhdpi/ic_search_background_black.png
#	src/main/res/drawable-xhdpi/ic_search_background_white.png
#	src/main/res/drawable-xhdpi/ic_send_cancel_away.png
#	src/main/res/drawable-xhdpi/ic_send_cancel_dnd.png
#	src/main/res/drawable-xhdpi/ic_send_cancel_offline.png
#	src/main/res/drawable-xhdpi/ic_send_cancel_offline_white.png
#	src/main/res/drawable-xhdpi/ic_send_cancel_online.png
#	src/main/res/drawable-xhdpi/ic_send_location_away.png
#	src/main/res/drawable-xhdpi/ic_send_location_dnd.png
#	src/main/res/drawable-xhdpi/ic_send_location_offline.png
#	src/main/res/drawable-xhdpi/ic_send_location_offline_white.png
#	src/main/res/drawable-xhdpi/ic_send_location_online.png
#	src/main/res/drawable-xhdpi/ic_send_photo_away.png
#	src/main/res/drawable-xhdpi/ic_send_photo_dnd.png
#	src/main/res/drawable-xhdpi/ic_send_photo_offline.png
#	src/main/res/drawable-xhdpi/ic_send_photo_offline_white.png
#	src/main/res/drawable-xhdpi/ic_send_photo_online.png
#	src/main/res/drawable-xhdpi/ic_send_picture_away.png
#	src/main/res/drawable-xhdpi/ic_send_picture_dnd.png
#	src/main/res/drawable-xhdpi/ic_send_picture_offline.png
#	src/main/res/drawable-xhdpi/ic_send_picture_offline_white.png
#	src/main/res/drawable-xhdpi/ic_send_picture_online.png
#	src/main/res/drawable-xhdpi/ic_send_text_away.png
#	src/main/res/drawable-xhdpi/ic_send_text_dnd.png
#	src/main/res/drawable-xhdpi/ic_send_text_offline.png
#	src/main/res/drawable-xhdpi/ic_send_text_offline_white.png
#	src/main/res/drawable-xhdpi/ic_send_text_online.png
#	src/main/res/drawable-xhdpi/ic_send_videocam_away.png
#	src/main/res/drawable-xhdpi/ic_send_videocam_dnd.png
#	src/main/res/drawable-xhdpi/ic_send_videocam_offline.png
#	src/main/res/drawable-xhdpi/ic_send_videocam_offline_white.png
#	src/main/res/drawable-xhdpi/ic_send_videocam_online.png
#	src/main/res/drawable-xhdpi/ic_send_voice_away.png
#	src/main/res/drawable-xhdpi/ic_send_voice_dnd.png
#	src/main/res/drawable-xhdpi/ic_send_voice_offline.png
#	src/main/res/drawable-xhdpi/ic_send_voice_offline_white.png
#	src/main/res/drawable-xhdpi/ic_send_voice_online.png
#	src/main/res/drawable-xhdpi/ic_verified_fingerprint.png
#	src/main/res/drawable-xhdpi/message_bubble_received.9.png
#	src/main/res/drawable-xhdpi/message_bubble_received_dark.9.png
#	src/main/res/drawable-xhdpi/message_bubble_received_grey.9.png
#	src/main/res/drawable-xhdpi/message_bubble_received_warning.9.png
#	src/main/res/drawable-xhdpi/message_bubble_received_white.9.png
#	src/main/res/drawable-xhdpi/message_bubble_sent.9.png
#	src/main/res/drawable-xhdpi/message_bubble_sent_grey.9.png
#	src/main/res/drawable-xxhdpi/date_bubble_grey.9.png
#	src/main/res/drawable-xxhdpi/date_bubble_white.9.png
#	src/main/res/drawable-xxhdpi/ic_flip_camera_android_black_24dp.png
#	src/main/res/drawable-xxhdpi/ic_no_results_background_black.png
#	src/main/res/drawable-xxhdpi/ic_no_results_background_white.png
#	src/main/res/drawable-xxhdpi/ic_notifications_none_white80.png
#	src/main/res/drawable-xxhdpi/ic_notifications_off_white80.png
#	src/main/res/drawable-xxhdpi/ic_notifications_paused_white80.png
#	src/main/res/drawable-xxhdpi/ic_notifications_white80.png
#	src/main/res/drawable-xxhdpi/ic_qr_code_scan_white_24dp.png
#	src/main/res/drawable-xxhdpi/ic_search_background_black.png
#	src/main/res/drawable-xxhdpi/ic_search_background_white.png
#	src/main/res/drawable-xxhdpi/ic_send_cancel_away.png
#	src/main/res/drawable-xxhdpi/ic_send_cancel_dnd.png
#	src/main/res/drawable-xxhdpi/ic_send_cancel_offline.png
#	src/main/res/drawable-xxhdpi/ic_send_cancel_offline_white.png
#	src/main/res/drawable-xxhdpi/ic_send_cancel_online.png
#	src/main/res/drawable-xxhdpi/ic_send_location_away.png
#	src/main/res/drawable-xxhdpi/ic_send_location_dnd.png
#	src/main/res/drawable-xxhdpi/ic_send_location_offline.png
#	src/main/res/drawable-xxhdpi/ic_send_location_offline_white.png
#	src/main/res/drawable-xxhdpi/ic_send_location_online.png
#	src/main/res/drawable-xxhdpi/ic_send_photo_away.png
#	src/main/res/drawable-xxhdpi/ic_send_photo_dnd.png
#	src/main/res/drawable-xxhdpi/ic_send_photo_offline.png
#	src/main/res/drawable-xxhdpi/ic_send_photo_offline_white.png
#	src/main/res/drawable-xxhdpi/ic_send_photo_online.png
#	src/main/res/drawable-xxhdpi/ic_send_picture_away.png
#	src/main/res/drawable-xxhdpi/ic_send_picture_dnd.png
#	src/main/res/drawable-xxhdpi/ic_send_picture_offline.png
#	src/main/res/drawable-xxhdpi/ic_send_picture_offline_white.png
#	src/main/res/drawable-xxhdpi/ic_send_picture_online.png
#	src/main/res/drawable-xxhdpi/ic_send_text_away.png
#	src/main/res/drawable-xxhdpi/ic_send_text_dnd.png
#	src/main/res/drawable-xxhdpi/ic_send_text_offline.png
#	src/main/res/drawable-xxhdpi/ic_send_text_offline_white.png
#	src/main/res/drawable-xxhdpi/ic_send_text_online.png
#	src/main/res/drawable-xxhdpi/ic_send_videocam_away.png
#	src/main/res/drawable-xxhdpi/ic_send_videocam_dnd.png
#	src/main/res/drawable-xxhdpi/ic_send_videocam_offline.png
#	src/main/res/drawable-xxhdpi/ic_send_videocam_offline_white.png
#	src/main/res/drawable-xxhdpi/ic_send_videocam_online.png
#	src/main/res/drawable-xxhdpi/ic_send_voice_away.png
#	src/main/res/drawable-xxhdpi/ic_send_voice_dnd.png
#	src/main/res/drawable-xxhdpi/ic_send_voice_offline.png
#	src/main/res/drawable-xxhdpi/ic_send_voice_offline_white.png
#	src/main/res/drawable-xxhdpi/ic_send_voice_online.png
#	src/main/res/drawable-xxhdpi/ic_verified_fingerprint.png
#	src/main/res/drawable-xxhdpi/message_bubble_received.9.png
#	src/main/res/drawable-xxhdpi/message_bubble_received_dark.9.png
#	src/main/res/drawable-xxhdpi/message_bubble_received_grey.9.png
#	src/main/res/drawable-xxhdpi/message_bubble_received_warning.9.png
#	src/main/res/drawable-xxhdpi/message_bubble_received_white.9.png
#	src/main/res/drawable-xxhdpi/message_bubble_sent.9.png
#	src/main/res/drawable-xxhdpi/message_bubble_sent_grey.9.png
#	src/main/res/drawable-xxxhdpi/date_bubble_grey.9.png
#	src/main/res/drawable-xxxhdpi/date_bubble_white.9.png
#	src/main/res/drawable-xxxhdpi/ic_flip_camera_android_black_24dp.png
#	src/main/res/drawable-xxxhdpi/ic_no_results_background_black.png
#	src/main/res/drawable-xxxhdpi/ic_no_results_background_white.png
#	src/main/res/drawable-xxxhdpi/ic_notifications_none_white80.png
#	src/main/res/drawable-xxxhdpi/ic_notifications_off_white80.png
#	src/main/res/drawable-xxxhdpi/ic_notifications_paused_white80.png
#	src/main/res/drawable-xxxhdpi/ic_notifications_white80.png
#	src/main/res/drawable-xxxhdpi/ic_qr_code_scan_white_24dp.png
#	src/main/res/drawable-xxxhdpi/ic_search_background_black.png
#	src/main/res/drawable-xxxhdpi/ic_search_background_white.png
#	src/main/res/drawable-xxxhdpi/ic_send_cancel_away.png
#	src/main/res/drawable-xxxhdpi/ic_send_cancel_dnd.png
#	src/main/res/drawable-xxxhdpi/ic_send_cancel_offline.png
#	src/main/res/drawable-xxxhdpi/ic_send_cancel_offline_white.png
#	src/main/res/drawable-xxxhdpi/ic_send_cancel_online.png
#	src/main/res/drawable-xxxhdpi/ic_send_location_away.png
#	src/main/res/drawable-xxxhdpi/ic_send_location_dnd.png
#	src/main/res/drawable-xxxhdpi/ic_send_location_offline.png
#	src/main/res/drawable-xxxhdpi/ic_send_location_offline_white.png
#	src/main/res/drawable-xxxhdpi/ic_send_location_online.png
#	src/main/res/drawable-xxxhdpi/ic_send_photo_away.png
#	src/main/res/drawable-xxxhdpi/ic_send_photo_dnd.png
#	src/main/res/drawable-xxxhdpi/ic_send_photo_offline.png
#	src/main/res/drawable-xxxhdpi/ic_send_photo_offline_white.png
#	src/main/res/drawable-xxxhdpi/ic_send_photo_online.png
#	src/main/res/drawable-xxxhdpi/ic_send_picture_away.png
#	src/main/res/drawable-xxxhdpi/ic_send_picture_dnd.png
#	src/main/res/drawable-xxxhdpi/ic_send_picture_offline.png
#	src/main/res/drawable-xxxhdpi/ic_send_picture_offline_white.png
#	src/main/res/drawable-xxxhdpi/ic_send_picture_online.png
#	src/main/res/drawable-xxxhdpi/ic_send_text_away.png
#	src/main/res/drawable-xxxhdpi/ic_send_text_dnd.png
#	src/main/res/drawable-xxxhdpi/ic_send_text_offline.png
#	src/main/res/drawable-xxxhdpi/ic_send_text_offline_white.png
#	src/main/res/drawable-xxxhdpi/ic_send_text_online.png
#	src/main/res/drawable-xxxhdpi/ic_send_videocam_away.png
#	src/main/res/drawable-xxxhdpi/ic_send_videocam_dnd.png
#	src/main/res/drawable-xxxhdpi/ic_send_videocam_offline.png
#	src/main/res/drawable-xxxhdpi/ic_send_videocam_offline_white.png
#	src/main/res/drawable-xxxhdpi/ic_send_videocam_online.png
#	src/main/res/drawable-xxxhdpi/ic_send_voice_away.png
#	src/main/res/drawable-xxxhdpi/ic_send_voice_dnd.png
#	src/main/res/drawable-xxxhdpi/ic_send_voice_offline.png
#	src/main/res/drawable-xxxhdpi/ic_send_voice_offline_white.png
#	src/main/res/drawable-xxxhdpi/ic_send_voice_online.png
#	src/main/res/drawable-xxxhdpi/ic_verified_fingerprint.png
#	src/main/res/drawable-xxxhdpi/message_bubble_received.9.png
#	src/main/res/drawable-xxxhdpi/message_bubble_received_dark.9.png
#	src/main/res/drawable-xxxhdpi/message_bubble_received_grey.9.png
#	src/main/res/drawable-xxxhdpi/message_bubble_received_warning.9.png
#	src/main/res/drawable-xxxhdpi/message_bubble_received_white.9.png
#	src/main/res/drawable-xxxhdpi/message_bubble_sent.9.png
#	src/main/res/drawable-xxxhdpi/message_bubble_sent_grey.9.png
#	src/main/res/drawable/ic_done_24dp.xml
#	src/main/res/layout/activity_muc_details.xml
#	src/main/res/layout/activity_rtp_session.xml
#	src/main/res/layout/enter_jid_dialog.xml
#	src/main/res/layout/fragment_conversation.xml
#	src/main/res/layout/message_date_bubble.xml
#	src/main/res/layout/message_received.xml
#	src/main/res/layout/message_rtp_session.xml
#	src/main/res/layout/message_sent.xml
#	src/main/res/layout/message_status.xml
#	src/main/res/layout/simple_list_item.xml
#	src/main/res/values/attrs.xml
#	src/main/res/values/colors.xml
#	src/main/res/values/defaults.xml
#	src/main/res/values/strings.xml
#	src/main/res/values/styles.xml
#	src/main/res/values/themes.xml
#	src/main/res/xml/preferences.xml
#	src/quicksy/java/eu/siacs/conversations/ui/ChooseCountryActivity.java
#	src/quicksy/res/drawable-hdpi/ic_notification.png
#	src/quicksy/res/drawable-mdpi/ic_notification.png
#	src/quicksy/res/drawable-xhdpi/ic_notification.png
#	src/quicksy/res/drawable-xxhdpi/ic_notification.png
#	src/quicksy/res/drawable-xxxhdpi/ic_notification.png
2024-05-09 23:48:21 +02:00
kosyak 82b4208304 improve scroll to bottom button behavior 2024-05-07 01:13:45 +02:00
kosyak 42a57ca532 checkbox for auto downloading all files when connected to wifi 2024-05-07 01:13:17 +02:00
SomeTr 36a4719d69
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1022 of 1022 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/uk/
2024-05-06 17:47:26 +00:00
Besnik_b 12eaa3464a
Translated using Weblate (Albanian)
Currently translated at 99.0% (1012 of 1022 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/sq/
2024-05-06 11:42:29 +00:00
licaon-kter 567fe9224a
Translated using Weblate (Romanian)
Currently translated at 100.0% (1022 of 1022 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ro/
2024-05-06 11:42:29 +00:00
SomeTr a5d52c6779
Translated using Weblate (Ukrainian)
Currently translated at 99.8% (1020 of 1022 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/uk/
2024-05-06 11:42:29 +00:00
ghose d39f30ee8f
Translated using Weblate (Galician)
Currently translated at 100.0% (1022 of 1022 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/gl/
2024-05-06 11:42:29 +00:00
Outbreak2096 088b870e7f
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (1022 of 1022 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2024-05-06 11:42:29 +00:00
Daniel Gultsch a46246c423
synchronize renegotiate to avoid race condition when that fails 2024-05-06 13:40:10 +02:00
Daniel Gultsch 359b2150eb
move PushMessageReceiver to receiver package 2024-05-06 13:26:16 +02:00
Mako 730f087a86
Translated using Weblate (Japanese)
Currently translated at 98.7% (1003 of 1016 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ja/
2024-05-06 02:18:25 +00:00
nautilusx 36aeb0b07b
Translated using Weblate (French)
Currently translated at 100.0% (13 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/fr/
2024-05-06 02:18:25 +00:00
random_r 0a4a518bbb
Translated using Weblate (Italian)
Currently translated at 100.0% (64 of 64 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/it/
2024-05-06 02:18:25 +00:00
woutput f49879a1a7
Translated using Weblate (Dutch)
Currently translated at 73.1% (743 of 1016 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/nl/
2024-05-06 02:18:25 +00:00
random_r 4c40a197cc
Translated using Weblate (Italian)
Currently translated at 100.0% (1016 of 1016 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/it/
2024-05-06 02:18:25 +00:00
Besnik_b 78a2ed6ccf
Translated using Weblate (Albanian)
Currently translated at 100.0% (64 of 64 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/sq/
2024-05-06 02:18:25 +00:00
Besnik_b 3461862371
Translated using Weblate (Albanian)
Currently translated at 99.2% (1008 of 1016 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/sq/
2024-05-06 02:18:25 +00:00
Daniel Gultsch d904b5e303
support mute via call integration 2024-05-04 15:49:05 +02:00
Daniel Gultsch 069b02004f
deep link into FSI setting if not granted 2024-05-03 11:26:41 +02:00
Daniel Gultsch 9bf1e51ac4
fix opening ringtone chooser when channel sound was set to null 2024-05-03 09:41:29 +02:00
Daniel Gultsch 5853f57f0a
add ability to cancel in-progress one-off backup 2024-05-03 08:51:09 +02:00
Daniel Gultsch 45b9c4dcc9
refactor ExportBackupService to worker 2024-05-02 19:12:39 +02:00
Daniel Gultsch cbd8fb3488
guard unregister phone account by system feature check 2024-05-02 10:58:40 +02:00
Daniel Gultsch 60e6841578
ignore quickLog startService exception 2024-05-02 10:58:06 +02:00
Daniel Gultsch 0b673ef1ab
set message input cursor color to text color 2024-05-02 10:56:39 +02:00
Daniel Gultsch 073a445a6d
use onSurface as link color 2024-04-29 08:26:25 +02:00
Daniel Gultsch 8856c048d6
exclude all realme devices up to Android 11 2024-04-26 09:59:21 +02:00
fuzfyy2 b185b75242
Translated using Weblate (Turkish)
Currently translated at 50.0% (1 of 2 strings)

Translation: Conversations/App Store Metadata (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-quicksy/tr/
2024-04-25 09:18:24 +00:00
fuzfyy2 37b3e2025e
Translated using Weblate (Turkish)
Currently translated at 100.0% (2 of 2 strings)

Translation: Conversations/App Store Metadata (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-conversations/tr/
2024-04-25 09:18:24 +00:00
gallegonovato 0ad7b017c9
Translated using Weblate (Spanish)
Currently translated at 100.0% (64 of 64 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/es/
2024-04-25 09:18:24 +00:00
Grzegorz Szymaszek 1198669724
Translated using Weblate (Polish)
Currently translated at 100.0% (9 of 9 strings)

Translation: Conversations/Android App (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-quicksy/pl/
2024-04-25 09:18:24 +00:00
licaon-kter d99a327b99
Translated using Weblate (Romanian)
Currently translated at 100.0% (1016 of 1016 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ro/
2024-04-25 09:18:23 +00:00
Grzegorz Szymaszek 02aa6fccf8
Translated using Weblate (Polish)
Currently translated at 100.0% (1016 of 1016 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/pl/
2024-04-25 09:18:23 +00:00
gallegonovato 86befddc6a
Translated using Weblate (Spanish)
Currently translated at 100.0% (1016 of 1016 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/es/
2024-04-25 09:18:23 +00:00
Waderasm 801bdfa3e3
Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (64 of 64 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/zh_Hant/
2024-04-24 07:38:08 +00:00
Waderasm 501bbac2ee
Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (1016 of 1016 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hant/
2024-04-24 07:38:07 +00:00
Outbreak2096 9c0501099e
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (64 of 64 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/zh_Hans/
2024-04-24 07:38:07 +00:00
SomeTr 5de4bb9708
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (64 of 64 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/uk/
2024-04-24 07:38:07 +00:00
ghose 91ef19827d
Translated using Weblate (Galician)
Currently translated at 56.2% (36 of 64 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/gl/
2024-04-24 07:38:07 +00:00
Grzegorz Szymaszek 60f5d07fd2
Translated using Weblate (Polish)
Currently translated at 12.5% (8 of 64 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/pl/
2024-04-24 07:38:07 +00:00
nautilusx 413b4db42d
Translated using Weblate (German)
Currently translated at 100.0% (64 of 64 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/de/
2024-04-24 07:38:07 +00:00
Outbreak2096 5ffe5acf8c
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (1016 of 1016 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2024-04-24 07:38:07 +00:00
licaon-kter 693159f367
Translated using Weblate (Romanian)
Currently translated at 100.0% (1016 of 1016 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ro/
2024-04-24 07:38:07 +00:00
SomeTr 933701e00a
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1016 of 1016 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/uk/
2024-04-24 07:38:07 +00:00
Grzegorz Szymaszek 9a473f69a3
Translated using Weblate (Polish)
Currently translated at 100.0% (1016 of 1016 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/pl/
2024-04-24 07:38:07 +00:00
ghose f0e2eb6fb0
Translated using Weblate (Galician)
Currently translated at 100.0% (1016 of 1016 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/gl/
2024-04-24 07:38:07 +00:00
nautilusx 2b66e0fe23
Translated using Weblate (German)
Currently translated at 100.0% (1016 of 1016 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/
2024-04-24 07:38:07 +00:00
Outbreak2096 ebcc072cd1
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (63 of 63 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/zh_Hans/
2024-04-24 07:38:07 +00:00
gallegonovato 71899c4a02
Translated using Weblate (Spanish)
Currently translated at 100.0% (63 of 63 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/es/
2024-04-24 07:38:07 +00:00
SomeTr 56cb9e3fd6
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (63 of 63 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/uk/
2024-04-24 07:38:07 +00:00
ghose 470db7b51e
Translated using Weblate (Galician)
Currently translated at 55.5% (35 of 63 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/gl/
2024-04-24 07:38:07 +00:00
Grzegorz Szymaszek adff3dfb30
Translated using Weblate (Polish)
Currently translated at 11.1% (7 of 63 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/pl/
2024-04-24 07:38:07 +00:00
nautilusx ad4f2273d7
Translated using Weblate (German)
Currently translated at 100.0% (63 of 63 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/de/
2024-04-24 07:38:07 +00:00
random_r 6df3f4491f
Translated using Weblate (Italian)
Currently translated at 100.0% (63 of 63 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/it/
2024-04-24 07:38:07 +00:00
Waderasm a3b10cf3bc
Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (13 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/zh_Hant/
2024-04-24 07:38:07 +00:00
Waderasm 1cdc0c4898
Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (1014 of 1014 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hant/
2024-04-24 07:38:07 +00:00
random_r b284b36074
Translated using Weblate (Italian)
Currently translated at 100.0% (1014 of 1014 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/it/
2024-04-24 07:38:07 +00:00
gallegonovato b366b0be4b
Translated using Weblate (Spanish)
Currently translated at 100.0% (1014 of 1014 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/es/
2024-04-24 07:38:07 +00:00
fuzfyy f4948354c9
Translated using Weblate (Turkish)
Currently translated at 50.0% (1 of 2 strings)

Translation: Conversations/App Store Metadata (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-conversations/tr/
2024-04-24 07:38:07 +00:00
gallegonovato cd12667d34
Translated using Weblate (Spanish)
Currently translated at 100.0% (62 of 62 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/es/
2024-04-24 07:38:07 +00:00
fuzfyy 9f4cb11595
Translated using Weblate (Turkish)
Currently translated at 100.0% (13 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/tr/
2024-04-24 07:38:07 +00:00
fuzfyy 6905139428
Translated using Weblate (Turkish)
Currently translated at 99.4% (1008 of 1014 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/tr/
2024-04-24 07:38:07 +00:00
gallegonovato 1156acf958
Translated using Weblate (Spanish)
Currently translated at 100.0% (1014 of 1014 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/es/
2024-04-24 07:38:07 +00:00
nautilusx 69ba31db65
Translated using Weblate (German)
Currently translated at 100.0% (1014 of 1014 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/
2024-04-24 07:38:07 +00:00
p42ity 73897f8db3 Voice Recordings: Add privacy option, deactivate workaround for AAC sensitive devices for Android 14+ and change default sampling frequency 2024-04-23 16:57:37 +02:00
Daniel Gultsch 23e88e6e42
version bump to 2.15.3 + changelog 2024-04-22 14:07:28 +02:00
Daniel Gultsch 19e7c7ff19
make restore backup dialog scrollable
fixes #267
2024-04-22 13:02:39 +02:00
Daniel Gultsch 3892015310
introduce setting to ignore invites from strangers 2024-04-22 12:49:27 +02:00
Daniel Gultsch d0945c35ee
add realme x50 to bad devices list
by now we probably have enough evidence that all realme devices
below Android 12 (or maybe 13) are broken

we just need to figure out how to properly put the entire vendor on
a list
2024-04-21 16:11:56 +02:00
Daniel Gultsch 3bcc8efc57
update gradle wrapper 2024-04-20 21:44:48 +02:00
Daniel Gultsch 538a714e29
fix formating for previous commit 2024-04-20 10:58:20 +02:00
Daniel Gultsch ed2e0ab73c
getting and setting audio devices on Android 14 when not integrated should go to fallback 2024-04-20 10:51:31 +02:00
Daniel Gultsch cfc22ba2af
stop linter from warning about feature check 2024-04-19 17:34:35 +02:00
Daniel Gultsch 67c900b344
version bump to 2.15.2 2024-04-19 14:00:05 +02:00
Daniel Gultsch c89413ee99
delete 'screenshots' compilation image 2024-04-19 10:29:37 +02:00
Daniel Gultsch 488bd38f27 link to individual fastlane screenshots instead of compilation image 2024-04-19 08:27:20 +00:00
Daniel Gultsch 027a4e139a
delay integrated audio routing on callee end until picked up 2024-04-19 10:01:23 +02:00
Licaon_Kter 32e25e36fc Update fastlane screenshots
Co-authored-by: Licaon_Kter <licaon-kter@noreply.codeberg.org>
Co-committed-by: Licaon_Kter <licaon-kter@noreply.codeberg.org>
2024-04-18 18:37:05 +00:00
Grzegorz Szymaszek d95a277037
Translated using Weblate (Polish)
Currently translated at 100.0% (2 of 2 strings)

Translation: Conversations/App Store Metadata (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-quicksy/pl/
2024-04-18 15:23:37 +00:00
Grzegorz Szymaszek e0882cb901
Translated using Weblate (Polish)
Currently translated at 9.6% (6 of 62 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/pl/
2024-04-18 15:23:37 +00:00
Grzegorz Szymaszek df90dc8514
Translated using Weblate (Polish)
Currently translated at 100.0% (1014 of 1014 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/pl/
2024-04-18 15:23:37 +00:00
ghose 507e7bd849
Translated using Weblate (Galician)
Currently translated at 100.0% (1014 of 1014 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/gl/
2024-04-18 15:23:37 +00:00
Grzegorz Szymaszek bccb4d824a
Translated using Weblate (Polish)
Currently translated at 94.7% (961 of 1014 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/pl/
2024-04-18 13:03:21 +00:00
ghose 3f9818ce0a
Translated using Weblate (Galician)
Currently translated at 54.8% (34 of 62 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/gl/
2024-04-18 13:03:21 +00:00
Grzegorz Szymaszek ebcc307940
Translated using Weblate (Polish)
Currently translated at 8.0% (5 of 62 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/pl/
2024-04-18 13:03:21 +00:00
nautilusx 1e67d98734
Translated using Weblate (German)
Currently translated at 100.0% (62 of 62 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/de/
2024-04-18 13:03:21 +00:00
random_r 0b200ffe1e
Translated using Weblate (Italian)
Currently translated at 100.0% (62 of 62 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/it/
2024-04-18 13:03:21 +00:00
Grzegorz Szymaszek 405c7bac5c
Translated using Weblate (Polish)
Currently translated at 94.6% (960 of 1014 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/pl/
2024-04-18 13:03:21 +00:00
random_r eb9edebab8
Translated using Weblate (Italian)
Currently translated at 100.0% (1014 of 1014 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/it/
2024-04-18 13:03:21 +00:00
nautilusx 30a77301f6
Translated using Weblate (German)
Currently translated at 100.0% (1014 of 1014 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/
2024-04-18 13:03:21 +00:00
Daniel Gultsch 961e0bb025
use bold done all for displayed 2024-04-18 14:36:44 +02:00
Daniel Gultsch 1a77356a27
use bundled letsencrypt for quicksy registration 2024-04-18 13:49:32 +02:00
Daniel Gultsch 1d4d2c6664
add vendor and android version to 'about' section
that way a screenshot of the settings is a one stop shop for maintaining our
broken device list
2024-04-18 10:10:21 +02:00
Daniel Gultsch d1552c9397
add more realme devices to broken call integration list 2024-04-18 10:10:21 +02:00
Outbreak2096 8c35d2c0be
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (62 of 62 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/zh_Hans/
2024-04-17 14:09:43 +00:00
SomeTr 9c41eeab1f
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (62 of 62 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/uk/
2024-04-17 14:09:43 +00:00
Dirk 9caf71421c
Translated using Weblate (German)
Currently translated at 100.0% (62 of 62 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/de/
2024-04-17 14:09:43 +00:00
SomeTr 4220743b51
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1014 of 1014 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/uk/
2024-04-17 14:09:43 +00:00
Outbreak2096 d07d05f171
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (1014 of 1014 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2024-04-17 14:09:43 +00:00
licaon-kter 7163c28de1
Translated using Weblate (Romanian)
Currently translated at 100.0% (1014 of 1014 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ro/
2024-04-17 14:09:43 +00:00
SomeTr fcca345dcc
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1014 of 1014 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/uk/
2024-04-17 14:09:43 +00:00
0ko a4518374b7
Translated using Weblate (Russian)
Currently translated at 96.7% (981 of 1014 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ru/
2024-04-17 14:09:43 +00:00
ghose a810525154
Translated using Weblate (Galician)
Currently translated at 100.0% (1014 of 1014 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/gl/
2024-04-17 14:09:43 +00:00
nautilusx ba8b80c99c
Translated using Weblate (German)
Currently translated at 100.0% (1014 of 1014 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/
2024-04-17 14:09:43 +00:00
Daniel Gultsch c1193f059b
add Realme XT to list of broken devices 2024-04-17 16:05:53 +02:00
Daniel Gultsch 9cabc0262f
request READ_MEDIA_* permission when restoring backup on fdroid version 2024-04-17 14:58:44 +02:00
Daniel Gultsch 7088c1f507
delay legacy audio routing until after call has been accepted
fixes #249
2024-04-17 11:29:13 +02:00
Daniel Gultsch 9d01af239a
play ringtone through notification
in 484f633180 we switched from letting the notification play the ringtone and handle the vibration to doing it ourselves.

this had two reasons. The ringtone selector in the android notification settings was broken at times and we wanted to silence the notification when pressing volume down.

This commit essentially reverts that change. We fixed the ringtone selection by handling it internally in Conversations and simply recreating the entire channel. Silencing the notification can be achieved by re-posting it with onlyAlertOnce set to true (I guess we didn’t know that at the time)
2024-04-17 10:32:20 +02:00
Daniel Gultsch cf5c038611
fix quicksy build 2024-04-16 10:34:40 +02:00
Daniel Gultsch 1ba46529ad
version bump to 2.15.1 2024-04-16 10:16:52 +02:00
Outbreak2096 a1e1417e93
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (2 of 2 strings)

Translation: Conversations/App Store Metadata (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-conversations/zh_Hans/
2024-04-16 06:52:24 +00:00
random_r ceedffbc31
Translated using Weblate (Italian)
Currently translated at 100.0% (61 of 61 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/it/
2024-04-16 06:52:24 +00:00
Outbreak2096 2e5d68027c
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (1012 of 1012 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2024-04-16 06:52:24 +00:00
Besnik_b ac7918f01b
Translated using Weblate (Albanian)
Currently translated at 99.2% (1004 of 1012 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/sq/
2024-04-16 06:52:24 +00:00
licaon-kter aa4cb2bd9a
Translated using Weblate (Romanian)
Currently translated at 100.0% (1012 of 1012 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ro/
2024-04-16 06:52:24 +00:00
SomeTr bf8fccb0da
Translated using Weblate (Ukrainian)
Currently translated at 99.7% (1009 of 1012 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/uk/
2024-04-16 06:52:24 +00:00
random_r d79aae2496
Translated using Weblate (Italian)
Currently translated at 100.0% (1012 of 1012 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/it/
2024-04-16 06:52:24 +00:00
ghose 7259ab35a5
Translated using Weblate (Galician)
Currently translated at 100.0% (1012 of 1012 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/gl/
2024-04-16 06:52:24 +00:00
Anonymous 8783800cf5
Translated using Weblate (Korean)
Currently translated at 36.9% (374 of 1012 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ko/
2024-04-16 06:52:24 +00:00
nautilusx 577524da93
Translated using Weblate (German)
Currently translated at 100.0% (61 of 61 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/de/
2024-04-16 06:52:24 +00:00
Dirk 6ff3758db6
Translated using Weblate (German)
Currently translated at 100.0% (61 of 61 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/de/
2024-04-16 06:52:24 +00:00
Daniel Gultsch 244c29cd6f
start to maintain list of devices with broken call integration 2024-04-16 08:51:40 +02:00
Daniel Gultsch 960b20db3e
introduce 'large font' setting 2024-04-16 08:35:50 +02:00
Daniel Gultsch f75c061627
do not show captcha dialog when activity is gone 2024-04-16 08:34:33 +02:00
Daniel Gultsch ee57ba7fba
app: use static variables to refer to config settings 2024-04-15 18:49:27 +02:00
Daniel Gultsch 97eb7d4e48
persist 'cancelled' annotation on failed file transfer 2024-04-15 18:15:01 +02:00
Daniel Gultsch 3a489741e7
fix audio player time display + progress for long audio files 2024-04-15 18:11:22 +02:00
Daniel Gultsch c06f2d23f6
shorten colorful bubble description 2024-04-15 12:35:11 +02:00
Daniel Gultsch e950d431bc
use internal location viewer for in text geo uris 2024-04-15 10:14:14 +02:00
Daniel Gultsch a3cc91dc11
replace FlowLayout with constraint layout 2024-04-15 10:14:14 +02:00
Daniel Gultsch 699b8723e0
make bubbles slightly distinct in non color mode 2024-04-15 10:14:14 +02:00
Daniel Gultsch 376374263c
do not show message status for RTP
we don’t track them well enough (we could though)
2024-04-15 10:14:14 +02:00
Daniel Gultsch b0bc2c2856
omit type of encryption from message hint 2024-04-15 10:14:14 +02:00
Daniel Gultsch d5ef06055f
refactor message status. use more icons 2024-04-15 10:14:13 +02:00
nautilusx 35c8833dd5
Translated using Weblate (German)
Currently translated at 100.0% (61 of 61 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/de/
2024-04-14 08:18:23 +00:00
Licaon_Kter f351f467b6 Even tablet 2024-04-12 15:47:09 +00:00
Licaon_Kter 10e673a3dd Add more 2024-04-12 15:46:43 +00:00
Licaon_Kter 407588a319 Update screenshots 2024-04-12 15:46:14 +00:00
Daniel Gultsch 6c564d8483
version bump to 2.15.0 2024-04-12 09:36:31 +02:00
Daniel Gultsch b62f244c63
fix default value for colorful chat bubbles 2024-04-12 09:34:52 +02:00
Outbreak2096 eadff422c9
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (61 of 61 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/zh_Hans/
2024-04-12 06:41:10 +00:00
Daniel Gultsch 21816e0e64
fix paddings in contact and muc details 2024-04-12 08:38:46 +02:00
Daniel Gultsch 13a879841f
fix cancelled p2p ft showing up as such 2024-04-12 08:38:46 +02:00
resoli c6289c442d
Translated using Weblate (Italian)
Currently translated at 100.0% (13 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/it/
2024-04-10 19:34:11 +00:00
resoli 34314b526b
Translated using Weblate (Italian)
Currently translated at 93.6% (949 of 1013 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/it/
2024-04-10 19:34:10 +00:00
SomeTr 4a01305f70
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1013 of 1013 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/uk/
2024-04-10 16:37:16 +00:00
gallegonovato 5b801b2e78
Translated using Weblate (Spanish)
Currently translated at 100.0% (61 of 61 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/es/
2024-04-10 12:41:42 +00:00
gallegonovato 7d31e15e08
Translated using Weblate (Spanish)
Currently translated at 94.4% (957 of 1013 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/es/
2024-04-10 12:41:42 +00:00
Outbreak2096 3c2eb38c73
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (61 of 61 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/zh_Hans/
2024-04-10 04:51:37 +00:00
SomeTr 8616409900
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (61 of 61 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/uk/
2024-04-10 04:51:36 +00:00
ghose 952606732f
Translated using Weblate (Galician)
Currently translated at 54.0% (33 of 61 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/gl/
2024-04-10 04:51:36 +00:00
Besnik_b 317d7a0bcc
Translated using Weblate (Albanian)
Currently translated at 100.0% (61 of 61 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/sq/
2024-04-10 04:51:35 +00:00
Outbreak2096 1cb7fcbbab
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (1013 of 1013 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2024-04-10 04:51:35 +00:00
nautilusx 7f33a65820
Translated using Weblate (German)
Currently translated at 100.0% (1013 of 1013 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/
2024-04-09 15:47:25 +00:00
Daniel Gultsch b94549551c
add fastlane changelog 2024-04-09 17:46:43 +02:00
Daniel Gultsch 2c28a0d1e6
version bump to 2.15.0-beta + changelog 2024-04-09 17:39:26 +02:00
Daniel Gultsch 11d435f327
enable MDS support 2024-04-09 17:30:07 +02:00
nautilusx fee79a0927
Translated using Weblate (German)
Currently translated at 99.4% (1007 of 1013 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/
2024-04-09 15:28:02 +00:00
nautilusx 8ff3a3d6bc
Translated using Weblate (German)
Currently translated at 99.3% (1006 of 1013 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/
2024-04-09 15:27:48 +00:00
inputmice c5d3d8ce6e
Translated using Weblate (German)
Currently translated at 99.0% (1003 of 1013 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/
2024-04-09 15:24:48 +00:00
nautilusx 5cde091cd4
Translated using Weblate (German)
Currently translated at 99.0% (1003 of 1013 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/
2024-04-09 15:24:48 +00:00
inputmice d5892366d1
Translated using Weblate (German)
Currently translated at 98.5% (998 of 1013 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/
2024-04-09 15:21:56 +00:00
nautilusx ccc256b8f9
Translated using Weblate (German)
Currently translated at 98.5% (998 of 1013 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/
2024-04-09 15:21:56 +00:00
nautilusx f3eec0d4bf
Translated using Weblate (German)
Currently translated at 97.2% (985 of 1013 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/
2024-04-09 15:16:16 +00:00
ghose a3ba38c944
Translated using Weblate (Galician)
Currently translated at 100.0% (1013 of 1013 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/gl/
2024-04-09 13:50:24 +00:00
licaon-kter 5f5ab1c6dc
Translated using Weblate (Romanian)
Currently translated at 100.0% (1013 of 1013 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ro/
2024-04-09 11:11:02 +00:00
SomeTr a25595e87a
Translated using Weblate (Ukrainian)
Currently translated at 99.6% (1009 of 1013 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/uk/
2024-04-09 11:11:02 +00:00
Daniel Gultsch c104f67c4d
fix typo in UP description 2024-04-09 11:50:16 +02:00
SomeTr 67d5cbad56 Translated using Weblate (Ukrainian)
Currently translated at 97.2% (985 of 1013 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/uk/
2024-04-09 09:10:33 +00:00
Outbreak2096 df39863fd7 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (60 of 60 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/zh_Hans/
2024-04-09 09:10:33 +00:00
Outbreak2096 e13b411db3 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (2 of 2 strings)

Translation: Conversations/App Store Metadata (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-conversations/zh_Hans/
2024-04-09 09:10:33 +00:00
Outbreak2096 d8f44fef77 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (1013 of 1013 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2024-04-09 09:10:33 +00:00
Besnik_b 9a62dcec3c Translated using Weblate (Albanian)
Currently translated at 99.2% (1005 of 1013 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/sq/
2024-04-09 09:10:33 +00:00
licaon-kter 2265b114d4 Translated using Weblate (Romanian)
Currently translated at 100.0% (1013 of 1013 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ro/
2024-04-09 09:10:33 +00:00
SomeTr df52d79920 Translated using Weblate (Ukrainian)
Currently translated at 96.6% (979 of 1013 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/uk/
2024-04-09 09:10:33 +00:00
licaon-kter f68e8c4a73 Translated using Weblate (Romanian)
Currently translated at 97.2% (985 of 1013 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ro/
2024-04-09 09:10:33 +00:00
Daniel Gultsch e8bdd5de7a
style dynamic tags 2024-04-09 11:09:14 +02:00
Daniel Gultsch 0ec46d1d34
trigger message deletion after toggling setting 2024-04-09 10:42:44 +02:00
Daniel Gultsch 0e4fd163f2
use MITM instead of MIM 2024-04-08 18:52:33 +02:00
Daniel Gultsch 38bff563ae
use slighter darker shades for send button in dark theme 2024-04-08 18:51:45 +02:00
Anonymous 6dd964b81b
Translated using Weblate (Chinese (Simplified))
Currently translated at 96.2% (975 of 1013 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2024-04-08 15:38:03 +00:00
Anonymous 3a4858ce59
Translated using Weblate (Czech)
Currently translated at 88.7% (899 of 1013 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/cs/
2024-04-08 15:37:58 +00:00
Anonymous 1470b958bb
Translated using Weblate (Catalan)
Currently translated at 83.5% (846 of 1013 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ca/
2024-04-08 15:37:58 +00:00
licaon-kter 236c477f4e
Translated using Weblate (Romanian)
Currently translated at 100.0% (984 of 984 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ro/
2024-04-08 15:36:34 +00:00
Daniel Gultsch 1bcb6bdd86
Merge branch 'translate-weblate-conversations-android-app-shared' 2024-04-08 17:35:53 +02:00
Daniel Gultsch 21535fc13c
Merge branch 'weblate-conversations-android-app-shared' of https://codeberg.org/translate/Conversations into translate-weblate-conversations-android-app-shared 2024-04-08 17:35:34 +02:00
Daniel Gultsch 32de58ce65
add require channel binding 2024-04-08 17:20:39 +02:00
Daniel Gultsch 7bdd924dd8
fix color on input message field 2024-04-08 16:43:12 +02:00
Daniel Gultsch 359b386a6c
fix 'create backup' button 2024-04-08 16:07:37 +02:00
Daniel Gultsch ae35bce828
give port input field a little more space 2024-04-08 15:44:34 +02:00
Daniel Gultsch b752508adb
remove autojoin setting 2024-04-08 15:32:52 +02:00
Daniel Gultsch ecf0e23144
remove quiet hours 2024-04-08 14:25:14 +02:00
Daniel Gultsch b7b4a78761
Merge branch '2.14.x' 2024-04-08 14:20:20 +02:00
Daniel Gultsch 96bf407bf0
fix rare crashes in call integration 2024-04-08 14:12:30 +02:00
Daniel Gultsch 5d1362f385
fix back button in AboutActivity 2024-04-08 10:37:20 +02:00
Daniel Gultsch 03ee84bf35
fix crash when toggling setting after rotation 2024-04-08 10:05:07 +02:00
Daniel Gultsch 67e01af5ef
flip sent/received bubble colors 2024-04-08 10:04:34 +02:00
Daniel Gultsch 319be12288
fix compiler error introduce in earlier commmit 2024-04-08 09:15:36 +02:00
Daniel Gultsch dbebb7feed
trigger endpoint renewal when up settings change 2024-04-08 08:20:08 +02:00
Daniel Gultsch e84d66874c
flip 'never send crash reports' to positive 2024-04-08 08:10:13 +02:00
Daniel Gultsch dc0a139392
collapse start chat button 2024-04-08 07:45:44 +02:00
Daniel Gultsch b30a88e7a5
make notification tone picker work 2024-04-07 20:35:01 +02:00
Daniel Gultsch 85771fc26c
redesign settings screen (WIP) 2024-04-07 20:05:43 +02:00
SomeTr d6357feeae
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (60 of 60 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/uk/
2024-04-07 13:18:23 +00:00
Outbreak2096 b6b864f9ea
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (984 of 984 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2024-04-07 13:18:22 +00:00
SomeTr 8238431580
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (984 of 984 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/uk/
2024-04-07 13:18:22 +00:00
ghose ec5672b1a8
Translated using Weblate (Galician)
Currently translated at 100.0% (984 of 984 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/gl/
2024-04-07 13:18:22 +00:00
nautilusx 5addb13c77
Translated using Weblate (German)
Currently translated at 100.0% (984 of 984 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/
2024-04-06 13:09:57 +00:00
yurtpage 8557b98bbf
Translated using Weblate (Russian)
Currently translated at 100.0% (2 of 2 strings)

Translation: Conversations/App Store Metadata (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-conversations/ru/
2024-04-06 07:18:28 +00:00
Outbreak2096 d765021fad
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (984 of 984 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2024-04-06 07:18:28 +00:00
Besnik_b d9f30db001
Translated using Weblate (Albanian)
Currently translated at 98.9% (974 of 984 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/sq/
2024-04-06 07:18:28 +00:00
licaon-kter cda9063fa4
Translated using Weblate (Romanian)
Currently translated at 100.0% (984 of 984 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ro/
2024-04-06 07:18:28 +00:00
ghose 35be17c9e4
Translated using Weblate (Galician)
Currently translated at 100.0% (984 of 984 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/gl/
2024-04-06 07:18:28 +00:00
Daniel Gultsch ed332020fd
fix typos 2024-04-05 15:32:36 +02:00
Daniel Gultsch 20c0193047
add check to 'attach' recording button 2024-04-05 13:32:53 +02:00
Daniel Gultsch 0f101ffd9f
use correct status bar color in splash theme 2024-04-05 13:32:53 +02:00
Daniel Gultsch a2c44aac8a
use vector drawable for notification icon 2024-04-05 13:32:52 +02:00
ghose 77a40679ab
Translated using Weblate (Galician)
Currently translated at 53.3% (32 of 60 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/gl/
2024-04-05 05:55:37 +00:00
Anonymous 575c73f77f
Translated using Weblate (Chinese (Traditional))
Currently translated at 94.6% (931 of 984 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hant/
2024-04-05 05:55:37 +00:00
Anonymous e163de43ca
Translated using Weblate (Chinese (Simplified))
Currently translated at 97.1% (956 of 984 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2024-04-05 05:55:36 +00:00
Anonymous cd2f5e474b
Translated using Weblate (Turkish)
Currently translated at 91.6% (902 of 984 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/tr/
2024-04-05 05:55:36 +00:00
Anonymous f14d2ff785
Translated using Weblate (Silesian)
Currently translated at 90.4% (890 of 984 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/szl/
2024-04-05 05:55:36 +00:00
Anonymous 26d2df5dc7
Translated using Weblate (Albanian)
Currently translated at 96.1% (946 of 984 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/sq/
2024-04-05 05:55:36 +00:00
Anonymous c1f698f5a4
Translated using Weblate (Romanian)
Currently translated at 96.3% (948 of 984 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ro/
2024-04-05 05:55:36 +00:00
Anonymous cf1513c072
Translated using Weblate (Portuguese (Brazil))
Currently translated at 91.9% (905 of 984 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/pt_BR/
2024-04-05 05:55:36 +00:00
Anonymous ea323742c8
Translated using Weblate (Vietnamese)
Currently translated at 93.4% (920 of 984 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/vi/
2024-04-05 05:55:35 +00:00
Anonymous dd909756bc
Translated using Weblate (Ukrainian)
Currently translated at 96.3% (948 of 984 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/uk/
2024-04-05 05:55:35 +00:00
Anonymous 950f1752df
Translated using Weblate (Swedish)
Currently translated at 95.8% (943 of 984 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/sv/
2024-04-05 05:55:35 +00:00
Anonymous f6b0d523d8
Translated using Weblate (Serbian)
Currently translated at 63.2% (622 of 984 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/sr/
2024-04-05 05:55:34 +00:00
Anonymous 92e3caf608
Translated using Weblate (Slovak)
Currently translated at 49.0% (483 of 984 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/sk/
2024-04-05 05:55:34 +00:00
Anonymous d224afe572
Translated using Weblate (Russian)
Currently translated at 96.3% (948 of 984 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ru/
2024-04-05 05:55:34 +00:00
Anonymous 94fc08d3da
Translated using Weblate (Polish)
Currently translated at 96.3% (948 of 984 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/pl/
2024-04-05 05:55:34 +00:00
Anonymous 3ce5489c1c
Translated using Weblate (Dutch)
Currently translated at 76.1% (749 of 984 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/nl/
2024-04-05 05:55:34 +00:00
Anonymous 8c6e788a5b
Translated using Weblate (Malayalam)
Currently translated at 31.6% (311 of 984 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ml/
2024-04-05 05:55:34 +00:00
Anonymous 793d2b79cf
Translated using Weblate (Korean)
Currently translated at 39.0% (384 of 984 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ko/
2024-04-05 05:55:34 +00:00
Anonymous 98fa91df77
Translated using Weblate (Japanese)
Currently translated at 96.3% (948 of 984 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ja/
2024-04-05 05:55:33 +00:00
Anonymous 0fc2e2512b
Translated using Weblate (Italian)
Currently translated at 95.5% (940 of 984 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/it/
2024-04-05 05:55:33 +00:00
Anonymous 2b4cac25f9
Translated using Weblate (Indonesian)
Currently translated at 45.5% (448 of 984 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/id/
2024-04-05 05:55:33 +00:00
Anonymous 02c63b297f
Translated using Weblate (Hungarian)
Currently translated at 83.6% (823 of 984 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/hu/
2024-04-05 05:55:33 +00:00
Anonymous ee6a1dcc3d
Translated using Weblate (Croatian)
Currently translated at 7.2% (71 of 984 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/hr/
2024-04-05 05:55:33 +00:00
Anonymous 99a06c5d5e
Translated using Weblate (Galician)
Currently translated at 96.8% (953 of 984 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/gl/
2024-04-05 05:55:33 +00:00
Anonymous 7025cd4f45
Translated using Weblate (French)
Currently translated at 95.8% (943 of 984 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/fr/
2024-04-05 05:55:32 +00:00
Anonymous 4343660c9e
Translated using Weblate (Persian)
Currently translated at 98.7% (974 of 986 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/fa/
2024-04-05 05:54:15 +00:00
Anonymous ab1b6bf440
Translated using Weblate (Finnish)
Currently translated at 90.1% (889 of 986 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/fi/
2024-04-05 05:54:15 +00:00
Anonymous b874135412
Translated using Weblate (Basque)
Currently translated at 72.3% (713 of 986 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/eu/
2024-04-05 05:54:15 +00:00
Anonymous aefd6d5122
Translated using Weblate (Spanish)
Currently translated at 99.0% (977 of 986 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/es/
2024-04-05 05:54:14 +00:00
Anonymous 81cdea6448
Translated using Weblate (Danish)
Currently translated at 95.8% (945 of 986 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/da/
2024-04-05 05:54:09 +00:00
Anonymous 1091bad7ec
Translated using Weblate (Greek)
Currently translated at 92.6% (914 of 986 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/el/
2024-04-05 05:54:09 +00:00
Anonymous 10b3d26cd2
Translated using Weblate (German)
Currently translated at 100.0% (986 of 986 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/
2024-04-05 05:54:08 +00:00
Anonymous 9742b9156c
Translated using Weblate (Czech)
Currently translated at 94.5% (932 of 986 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/cs/
2024-04-05 05:54:08 +00:00
Anonymous 5e03560453
Translated using Weblate (Bengali (India))
Currently translated at 11.1% (110 of 986 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/bn_IN/
2024-04-05 05:54:01 +00:00
Anonymous ae05d05d8f
Translated using Weblate (Catalan)
Currently translated at 89.0% (878 of 986 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ca/
2024-04-05 05:54:01 +00:00
Anonymous 771f405cf0
Translated using Weblate (Bulgarian)
Currently translated at 91.6% (904 of 986 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/bg/
2024-04-05 05:54:01 +00:00
yurtpage 26dd4aa2d3
Translated using Weblate (Russian)
Currently translated at 100.0% (2 of 2 strings)

Translation: Conversations/App Store Metadata (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-quicksy/ru/
2024-04-05 05:53:53 +00:00
yurtpage 622b38ad2d
Translated using Weblate (Russian)
Currently translated at 100.0% (2 of 2 strings)

Translation: Conversations/App Store Metadata (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-conversations/ru/
2024-04-05 05:53:53 +00:00
Outbreak2096 d34b17eb60
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (60 of 60 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/zh_Hans/
2024-04-05 05:53:53 +00:00
ghose 34a5db3cc4
Translated using Weblate (Galician)
Currently translated at 51.6% (31 of 60 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/gl/
2024-04-05 05:53:52 +00:00
nautilusx 57b7d93705
Translated using Weblate (German)
Currently translated at 100.0% (60 of 60 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/de/
2024-04-05 05:53:52 +00:00
Outbreak2096 491017b1ca
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (986 of 986 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2024-04-05 05:53:52 +00:00
melanotosjaunty a4eda305fb
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (986 of 986 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2024-04-05 05:53:52 +00:00
Besnik_b 169174825a
Translated using Weblate (Albanian)
Currently translated at 98.9% (976 of 986 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/sq/
2024-04-05 05:53:52 +00:00
ghose f545e4be8c
Translated using Weblate (Galician)
Currently translated at 99.6% (983 of 986 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/gl/
2024-04-05 05:53:52 +00:00
nautilusx a3b65b5b15
Translated using Weblate (German)
Currently translated at 100.0% (986 of 986 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/
2024-04-05 05:53:52 +00:00
yurtpage ff3484bc08
Translated using Weblate (Russian)
Currently translated at 50.0% (1 of 2 strings)

Translation: Conversations/App Store Metadata (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-conversations/ru/
2024-04-05 05:53:52 +00:00
Daniel Gultsch 347f995c21
use 'chat' and 'archive' instead of 'conversation' and 'close' 2024-04-05 07:53:19 +02:00
Daniel Gultsch 6e43248135
apply Material 3 theme to all activites 2024-04-04 11:38:27 +02:00
Daniel Gultsch 4968bde774
version bump to 2.14.2 + changelog 2024-04-04 11:07:53 +02:00
Daniel Gultsch c294a24f4e
add some call integration debug aids 2024-04-04 09:57:01 +02:00
Daniel Gultsch b90906b973
make RtpSessionActivity onNewIntent and onBackendConnected run through the same code 2024-04-03 20:51:48 +02:00
Jonas Lochmann a619cfe0d8 Add MY_PACKAGE_REPLACED to the EventReceiver
This ensures that Conversations is restarted after a update without waiting
for the next eu.siacs.conversations.POST_CONNECTIVITY_CHANGE from the AlarmManager
2024-04-03 07:57:13 +00:00
Daniel Gultsch ba1861f958
Merge branch '2.13.x' 2024-04-03 08:34:06 +02:00
Daniel Gultsch 0e9f4e5265
Channel discovery service / okttp needs bundled letsencrypt too 2024-04-03 08:33:21 +02:00
Daniel Gultsch 429b190b8e
version bump to 2.14.1 + changelog 2024-03-30 15:00:32 +01:00
Daniel Gultsch f22d19f7d0
disable mds for bug fix release 2024-03-30 14:54:28 +01:00
Daniel Gultsch cb37321ecb
rtp session propsoal: fix race condition with very fast 'busy' or 'error' 2024-03-30 11:15:41 +01:00
Daniel Gultsch c527e76337
parse invalid jingle actions 2024-03-30 08:48:09 +01:00
Daniel Gultsch 14fb903522
use fake 'tel:0' address on Android 8 2024-03-29 21:22:53 +01:00
Daniel Gultsch c415e7d1d5
add 0490 (mds) to doap file 2024-03-29 15:59:53 +01:00
Daniel Gultsch 283f363088
use voice call stream to play connect sound 2024-03-29 06:55:24 +01:00
Daniel Gultsch 26dde5370a
use distinct notification id for video transcoder 2024-03-28 13:53:29 +01:00
Daniel Gultsch 378efe1a8a
do not crash when error notification comes after service has shut down 2024-03-27 14:44:17 +01:00
Daniel Gultsch fbfb6c803f
catch early exception in video transcoder 2024-03-27 14:43:34 +01:00
Daniel Gultsch 6b37b6377b
remember mds display state until after mam catchup 2024-03-27 14:11:20 +01:00
Daniel Gultsch dd73b01ab1
rudimentary XEP-0490 implementation 2024-03-27 10:30:14 +01:00
Daniel Gultsch 38e9533be4
bump gradle and AGP 2024-03-26 10:27:46 +01:00
Daniel Gultsch f18ec53233
minor safeguard to ensure call integration ends 2024-03-25 10:58:20 +01:00
Daniel Gultsch 6b5fb6fee6
deliver session-initiate before integrating call
otherwise there could potentially be race conditions with
showIncomingCallUi being called before we have media information
2024-03-25 10:39:31 +01:00
Daniel Gultsch 21732237d4
add safeguards to ringtone playing twice 2024-03-25 08:50:44 +01:00
Daniel Gultsch e7edc2ce82
unnecessary version code bump to trigger new Google review 2024-03-22 11:56:21 +01:00
Daniel Gultsch c0a43c1f99
fix version code of ru-RU changelog 2024-03-22 09:39:59 +01:00
0ko 3e06eab0fe Translated using Weblate (Russian)
Currently translated at 6.8% (4 of 58 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/ru/
2024-03-22 08:39:01 +00:00
0ko fe988765d5 Translated using Weblate (Russian)
Currently translated at 100.0% (13 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/ru/
2024-03-22 08:39:01 +00:00
aei 5fa42afcfc Translated using Weblate (Arabic)
Currently translated at 100.0% (13 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/ar/
2024-03-22 08:39:01 +00:00
0ko ac4b588d23 Translated using Weblate (Russian)
Currently translated at 100.0% (987 of 987 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ru/
2024-03-22 08:39:01 +00:00
wiktor 6e560dc5dc Translated using Weblate (Polish)
Currently translated at 100.0% (987 of 987 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/pl/
2024-03-22 08:39:01 +00:00
Daniel Gultsch 6b00e0b462
version bump to 2.14.0 2024-03-20 08:37:32 +01:00
Daniel Gultsch 400cbd8eee
channel discovery screen code clean up 2024-03-20 07:26:20 +01:00
Daniel Gultsch 3e333eb972
catch exception when checking phone lock state 2024-03-19 18:00:12 +01:00
locness3 bac62261f8
Translated using Weblate (French)
Currently translated at 50.0% (1 of 2 strings)

Translation: Conversations/App Store Metadata (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-conversations/fr/
2024-03-18 18:53:34 +00:00
fnetX 5978494e60
Translated using Weblate (French)
Currently translated at 99.2% (980 of 987 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/fr/
2024-03-17 14:57:23 +00:00
Daniel Gultsch 3d5d257707
fix system feature detection for call integration 2024-03-16 16:31:32 +01:00
Daniel Gultsch 72d194d8ff
treat delayed destruction call integration as busy 2024-03-16 16:31:32 +01:00
gallegonovato faf2088452
Translated using Weblate (Spanish)
Currently translated at 100.0% (987 of 987 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/es/
2024-03-14 08:19:37 +00:00
Quini98 baf4788220
Translated using Weblate (Dutch)
Currently translated at 100.0% (13 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/nl/
2024-03-13 09:53:37 +00:00
Mako ea19e7d46c
Translated using Weblate (Japanese)
Currently translated at 100.0% (13 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/ja/
2024-03-13 09:53:37 +00:00
melanotosjaunty 80437d20cf
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (987 of 987 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2024-03-13 09:53:37 +00:00
Outbreak2096 3fd322ec9b
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (987 of 987 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2024-03-13 09:53:37 +00:00
Besnik_b 5ff205315e
Translated using Weblate (Albanian)
Currently translated at 98.7% (975 of 987 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/sq/
2024-03-13 09:53:37 +00:00
licaon-kter 40b8005e8d
Translated using Weblate (Romanian)
Currently translated at 100.0% (987 of 987 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ro/
2024-03-13 09:53:37 +00:00
SomeTr 7190475325
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (987 of 987 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/uk/
2024-03-13 09:53:37 +00:00
Quini98 39340ad956
Translated using Weblate (Dutch)
Currently translated at 79.1% (781 of 987 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/nl/
2024-03-13 09:53:37 +00:00
Mako 7823ccb1a1
Translated using Weblate (Japanese)
Currently translated at 100.0% (987 of 987 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ja/
2024-03-13 09:53:37 +00:00
ghose bab05a0dab
Translated using Weblate (Galician)
Currently translated at 100.0% (987 of 987 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/gl/
2024-03-13 09:53:36 +00:00
Dirk 4e72ef12f9
Translated using Weblate (German)
Currently translated at 100.0% (987 of 987 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/
2024-03-13 09:53:36 +00:00
Daniel Gultsch 8f35242c35
version bump to 2.14.0-beta.3 2024-03-12 19:17:55 +01:00
Daniel Gultsch 13839401e5
Merge branch '2.13.x' 2024-03-12 19:15:34 +01:00
Daniel Gultsch 2fa541f2dc
version bump to 2.13.5 2024-03-12 19:05:47 +01:00
Daniel Gultsch 028b3cff88
remove contacts integration again 2024-03-12 19:05:23 +01:00
Daniel Gultsch a2c67a6e38
ensure that jingle ft transport is terminated 2024-03-12 19:01:39 +01:00
Daniel Gultsch 0f50f71176
prevent deletion of bookmark w/o also closing conversation 2024-03-12 09:22:26 +01:00
Hund f4d8601d01
Translated using Weblate (Swedish)
Currently translated at 99.5% (981 of 985 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/sv/
2024-03-11 04:43:13 +00:00
v1s7 d242aaf920
Translated using Weblate (Russian)
Currently translated at 100.0% (985 of 985 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ru/
2024-03-11 04:43:13 +00:00
Eryk Michalak 20935f2271
Translated using Weblate (Polish)
Currently translated at 100.0% (985 of 985 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/pl/
2024-03-11 04:43:13 +00:00
Daniel Gultsch 1bf1411e11
add FAST to doap file 2024-03-10 18:58:08 +01:00
Daniel Gultsch 53029906bc
Merge branch '2.13.x' 2024-03-10 17:41:17 +01:00
Daniel Gultsch 9ad5b68d57
do not attempt unique/exporter channel binding on non conscrypt sockets 2024-03-10 17:40:41 +01:00
Daniel Gultsch e5cffa11be
fix stanza counting error after inline SM enable 2024-03-10 17:38:56 +01:00
Eryk Michalak bc3ccfb1be
Translated using Weblate (Polish)
Currently translated at 100.0% (13 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/pl/
2024-03-09 17:41:37 +00:00
Eryk Michalak ea78617ee1
Translated using Weblate (Polish)
Currently translated at 99.6% (982 of 985 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/pl/
2024-03-09 17:41:37 +00:00
petitpois 4264764d41
Translated using Weblate (French)
Currently translated at 50.0% (1 of 2 strings)

Translation: Conversations/App Store Metadata (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-conversations/fr/
2024-03-09 03:01:50 +00:00
petitpois 6bb99d9da6
Translated using Weblate (French)
Currently translated at 100.0% (13 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/fr/
2024-03-09 03:01:50 +00:00
petitpois 88030c8b8b
Translated using Weblate (French)
Currently translated at 99.3% (979 of 985 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/fr/
2024-03-09 03:01:50 +00:00
Outbreak2096 a3cf788e0d
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (13 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/zh_Hans/
2024-03-08 10:13:10 +00:00
Hund 781741b43a
Translated using Weblate (Swedish)
Currently translated at 98.9% (975 of 985 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/sv/
2024-03-08 10:13:10 +00:00
Daniel Gultsch 5cca842e66
fix return to call 2024-03-07 13:37:40 +01:00
Daniel Gultsch 55fd7157c8
version bump to 2.14.0-beta.2 2024-03-07 11:39:16 +01:00
Daniel Gultsch 71763902f8
do not use call integration on devices w/o telephony support 2024-03-07 11:38:21 +01:00
Daniel Gultsch 20e1f54277
play ringback sound on android 6/7 2024-03-05 13:08:43 +01:00
ghose 6ecc481eca
Translated using Weblate (Galician)
Currently translated at 100.0% (13 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/gl/
2024-03-05 06:43:16 +00:00
nautilusx 08ec15e9e8
Translated using Weblate (German)
Currently translated at 100.0% (13 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/de/
2024-03-05 06:43:16 +00:00
gallegonovato 5993328cde
Translated using Weblate (Spanish)
Currently translated at 100.0% (2 of 2 strings)

Translation: Conversations/App Store Metadata (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-quicksy/es/
2024-03-04 21:04:30 +00:00
Besnik_b b4fd767429
Translated using Weblate (Albanian)
Currently translated at 100.0% (13 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/sq/
2024-03-04 21:04:30 +00:00
licaon-kter 4587b7e818
Translated using Weblate (Romanian)
Currently translated at 100.0% (13 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/ro/
2024-03-04 21:04:30 +00:00
gallegonovato a3538f9753
Translated using Weblate (Spanish)
Currently translated at 100.0% (13 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/es/
2024-03-04 21:04:30 +00:00
kosyak cf889b3450 fix npe 2024-03-04 18:34:51 +01:00
ghose f80e8918e0
Translated using Weblate (Galician)
Currently translated at 100.0% (13 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/gl/
2024-03-04 09:08:19 +00:00
Anonymous 973431755f
Translated using Weblate (Turkish)
Currently translated at 92.3% (12 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/tr/
2024-03-04 09:08:19 +00:00
Anonymous 6328773c6f
Translated using Weblate (Silesian)
Currently translated at 92.3% (12 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/szl/
2024-03-04 09:08:19 +00:00
Anonymous 8b52e0ac5c
Translated using Weblate (Serbian)
Currently translated at 38.4% (5 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/sr/
2024-03-04 09:08:19 +00:00
Anonymous 3450a0d523
Translated using Weblate (Slovak)
Currently translated at 76.9% (10 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/sk/
2024-03-04 09:08:19 +00:00
Anonymous 1c0334f796
Translated using Weblate (Russian)
Currently translated at 92.3% (12 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/ru/
2024-03-04 09:08:19 +00:00
Anonymous b896504bb2
Translated using Weblate (Romanian)
Currently translated at 92.3% (12 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/ro/
2024-03-04 09:08:19 +00:00
Anonymous c5d396706c
Translated using Weblate (Portuguese (Brazil))
Currently translated at 92.3% (12 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/pt_BR/
2024-03-04 09:08:18 +00:00
Anonymous 282126862f
Translated using Weblate (Polish)
Currently translated at 92.3% (12 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/pl/
2024-03-04 09:08:18 +00:00
Anonymous bad24ffacc
Translated using Weblate (Dutch)
Currently translated at 84.6% (11 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/nl/
2024-03-04 09:08:18 +00:00
Anonymous 14ac2a7be0
Translated using Weblate (Japanese)
Currently translated at 92.3% (12 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/ja/
2024-03-04 09:08:18 +00:00
Anonymous aa28472a21
Translated using Weblate (Hungarian)
Currently translated at 92.3% (12 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/hu/
2024-03-04 09:08:18 +00:00
Anonymous e77eacc668
Translated using Weblate (Croatian)
Currently translated at 92.3% (12 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/hr/
2024-03-04 09:08:18 +00:00
Anonymous 5d31d14ed7
Translated using Weblate (Galician)
Currently translated at 92.3% (12 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/gl/
2024-03-04 09:08:18 +00:00
Anonymous cf134927dd
Translated using Weblate (French)
Currently translated at 92.3% (12 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/fr/
2024-03-04 09:08:18 +00:00
Anonymous 5ed98b25a3
Translated using Weblate (Finnish)
Currently translated at 76.9% (10 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/fi/
2024-03-04 09:08:18 +00:00
Anonymous 9ed182c6d6
Translated using Weblate (Basque)
Currently translated at 30.7% (4 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/eu/
2024-03-04 09:08:18 +00:00
Anonymous e58fdaddf1
Translated using Weblate (Greek)
Currently translated at 92.3% (12 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/el/
2024-03-04 09:08:18 +00:00
Anonymous 489e5cf41f
Translated using Weblate (German)
Currently translated at 92.3% (12 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/de/
2024-03-04 09:08:18 +00:00
Anonymous c3df579aeb
Translated using Weblate (Danish)
Currently translated at 92.3% (12 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/da/
2024-03-04 09:08:18 +00:00
Anonymous df8ba6c4bc
Translated using Weblate (Bulgarian)
Currently translated at 92.3% (12 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/bg/
2024-03-04 09:08:18 +00:00
MasoudAbkenar 6414894ffe
Translated using Weblate (Persian)
Currently translated at 100.0% (985 of 985 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/fa/
2024-03-04 09:08:18 +00:00
Besnik_b ff96570e4f
Translated using Weblate (Albanian)
Currently translated at 100.0% (2 of 2 strings)

Translation: Conversations/App Store Metadata (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-quicksy/sq/
2024-03-04 09:08:18 +00:00
Daniel Gultsch 6ea8a25802
call client app in welcome screen 2024-03-04 09:50:30 +01:00
Daniel Gultsch 29978a0f2a
null check SurfaceTextureHelper 2024-03-04 09:49:34 +01:00
Daniel Gultsch b9a19dc6c7
do not terminate jingle ft session twice (after iq timeout) 2024-03-04 09:49:34 +01:00
Daniel Gultsch 00f52226d8
execute all account state managments on ping thread 2024-03-04 09:49:33 +01:00
Daniel Gultsch 86b733e159
prevent receiving (as share with target) file URIs
as Element (Matrix client) demonstrated again file URIs are unnecessarily dangerous. On Android 7+ there is no good reason to process them anymore
2024-03-04 09:49:33 +01:00
inference 2ac4efa259 Improve onboarding text string 2024-03-03 19:22:41 +00:00
p42ity e1a2d81294 Added Oukitel WP12 Pro and Volla Phone X to the list of AAC sensitive devices (truncated voice messages workaround) 2024-03-02 12:39:22 +01:00
kosyak 2bae668561 Revert "debug"
This reverts commit db7896054b.
2024-02-29 15:07:37 +01:00
kosyak db7896054b debug 2024-02-29 14:25:48 +01:00
kosyak c427596107 get rid of command blicks 2024-02-28 22:45:02 +01:00
kosyak 79c7a3748c improve swipe gesture 2024-02-28 22:44:24 +01:00
kosyak 7b239d5412 fix timeout 2024-02-28 22:43:48 +01:00
kosyak dd7a1c1bdb fix npe 2024-02-28 19:29:20 +01:00
kosyak 1726fc86e3 fix notifications fallback color 2024-02-28 19:24:30 +01:00
Daniel Gultsch 6f8d8b9330
log reason for SM resume failure 2024-02-28 12:01:18 +01:00
SomeTr bab9553750
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (58 of 58 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/uk/
2024-02-24 07:40:57 +00:00
ghose e5c7392789
Translated using Weblate (Galician)
Currently translated at 51.7% (30 of 58 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/gl/
2024-02-24 07:40:56 +00:00
SomeTr cebe688a14
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (985 of 985 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/uk/
2024-02-24 07:40:56 +00:00
ghose 733abc4b45
Translated using Weblate (Galician)
Currently translated at 100.0% (985 of 985 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/gl/
2024-02-24 07:40:56 +00:00
licaon-kter faf042d29e Translated using Weblate (Romanian)
Currently translated at 100.0% (985 of 985 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ro/
2024-02-23 17:20:46 +00:00
Outbreak2096 1c3ad05353 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (58 of 58 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/zh_Hans/
2024-02-23 17:20:46 +00:00
gallegonovato 5f2666835a Translated using Weblate (Spanish)
Currently translated at 100.0% (58 of 58 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/es/
2024-02-23 17:20:46 +00:00
Besnik_b 4454da3a48 Translated using Weblate (Albanian)
Currently translated at 100.0% (58 of 58 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/sq/
2024-02-23 17:20:46 +00:00
nautilusx 7ee1b9ea13 Translated using Weblate (German)
Currently translated at 100.0% (58 of 58 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/de/
2024-02-23 17:20:46 +00:00
Outbreak2096 c7e153def9 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (985 of 985 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2024-02-23 17:20:46 +00:00
Besnik_b fa655811bc Translated using Weblate (Albanian)
Currently translated at 98.7% (973 of 985 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/sq/
2024-02-23 17:20:46 +00:00
gallegonovato 339204212e Translated using Weblate (Spanish)
Currently translated at 100.0% (985 of 985 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/es/
2024-02-23 17:20:46 +00:00
nautilusx a72214bab3 Translated using Weblate (German)
Currently translated at 100.0% (985 of 985 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/
2024-02-23 17:20:46 +00:00
Daniel Gultsch bd2b9b414e
do not enforce main thread for getting audio devices
fixes #206
2024-02-23 18:02:16 +01:00
acioustick 9386769409
Translated using Weblate (Japanese)
Currently translated at 50.0% (1 of 2 strings)

Translation: Conversations/App Store Metadata (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-conversations/ja/
2024-02-23 08:55:00 +00:00
ghose b381f125de
Translated using Weblate (Galician)
Currently translated at 50.8% (29 of 57 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/gl/
2024-02-23 08:55:00 +00:00
licaon-kter d38c8b2e9d
Translated using Weblate (Romanian)
Currently translated at 100.0% (982 of 982 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ro/
2024-02-23 08:55:00 +00:00
Application-Maker f3390a54bc
Translated using Weblate (Russian)
Currently translated at 100.0% (982 of 982 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ru/
2024-02-23 08:55:00 +00:00
Besnik_b 676acce1e2
Translated using Weblate (Albanian)
Currently translated at 100.0% (2 of 2 strings)

Translation: Conversations/App Store Metadata (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-quicksy/sq/
2024-02-23 08:55:00 +00:00
Outbreak2096 6c19c46742
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (57 of 57 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/zh_Hans/
2024-02-23 08:55:00 +00:00
Outbreak2096 b8a56c4d61
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (982 of 982 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2024-02-23 08:55:00 +00:00
Besnik_b aad750f2c8
Translated using Weblate (Albanian)
Currently translated at 98.5% (968 of 982 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/sq/
2024-02-23 08:55:00 +00:00
Mako 6e7892415a
Translated using Weblate (Japanese)
Currently translated at 100.0% (982 of 982 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ja/
2024-02-23 08:55:00 +00:00
ghose 34b60bba39
Translated using Weblate (Galician)
Currently translated at 100.0% (982 of 982 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/gl/
2024-02-23 08:55:00 +00:00
Daniel Gultsch 94f3b1fb57
version bump to 2.14.0-beta + changelog 2024-02-23 09:54:13 +01:00
Daniel Gultsch f1abfbdf35
work around dead system exception when querying active network 2024-02-23 09:54:13 +01:00
Daniel Gultsch 3ae561d74a
bump dependencies 2024-02-23 09:54:13 +01:00
Daniel Gultsch 7eaad9842c
remove mic availability check 2024-02-23 09:54:13 +01:00
Daniel Gultsch ca1d8b4d1b
fix race condition accessing rtpSender 2024-02-23 09:54:13 +01:00
Daniel Gultsch e416a6c4eb
maintain phone accounts only for enabled accounts 2024-02-23 09:54:12 +01:00
Daniel Gultsch a04dc6e4ad
show warning when call integration accounts exceed 10 2024-02-23 09:54:12 +01:00
Daniel Gultsch d2d76322b9
show speaker configuration during ACCEPTING 2024-02-23 09:54:12 +01:00
Daniel Gultsch 18dea352b0
send jmi finish alongside session terminate 2024-02-23 09:54:12 +01:00
Daniel Gultsch 21b8bf424a
skip automatic audio device selection when BT is available 2024-02-23 09:54:12 +01:00
Daniel Gultsch a78747eaa2
react to onSilence() and stop ringtone 2024-02-23 09:54:12 +01:00
Daniel Gultsch bfe2aff7a1
show speaker selection during 'ringing' 2024-02-23 09:54:11 +01:00
Daniel Gultsch bff1ac5ebc
do not nofiy UI if UI triggered retract 2024-02-23 09:54:11 +01:00
Daniel Gultsch 5158fc4530
do not process JMI from offline queue as live messages 2024-02-23 09:54:11 +01:00
Daniel Gultsch 32da5853d7
track offline message queue 2024-02-23 09:54:11 +01:00
Daniel Gultsch ef5508e5b1
trigger incoming call integration only for rtp connections 2024-02-23 09:54:11 +01:00
Daniel Gultsch b7da7f3367
add generator for JMI finish message 2024-02-23 09:54:11 +01:00
Daniel Gultsch bcc0c32af3
fix crash when using direct jingle init on offline contacts 2024-02-23 09:54:11 +01:00
Daniel Gultsch 1090b2edd3
add optional strict offline checking for calls 2024-02-23 09:54:11 +01:00
Daniel Gultsch d4225fcf32
fix regression with screen not being put in right state 2024-02-23 09:54:10 +01:00
Daniel Gultsch 7f9d836f1a
play connected sound from sound file 2024-02-23 09:54:10 +01:00
Daniel Gultsch d31b24d05a
get rid of ToneManager and play sounds in CallIntegration instead
CallIntegration takes care of audio routing so it makes sense to play
and sounds here too
2024-02-23 09:54:10 +01:00
Daniel Gultsch 66cd50e163
add permission check to placeCall method 2024-02-23 09:54:10 +01:00
Daniel Gultsch 0ffc295888
provide alternative method to create calls for Android <8 2024-02-23 09:54:10 +01:00
Daniel Gultsch d79fc1bb79
run some AppRTCAudioManager actions on main thread 2024-02-23 09:54:10 +01:00
Daniel Gultsch 6ba9208eea
switch audio device when switching to video 2024-02-23 09:54:09 +01:00
Daniel Gultsch ebb48e9320
set correct video state for calls 2024-02-23 09:54:09 +01:00
Daniel Gultsch 6975299a28
hook into onAnswer/onReject of CallIntegration
the Operating System shows a notification on our behalf if there is currently
a call going on that can not be put on hold (For example a Quicksy call is going
on while a Conversations call is coming on)
2024-02-23 09:54:09 +01:00
Daniel Gultsch f119c36bff
(un)register phone account on xmpp account creation/deletion 2024-02-23 09:54:09 +01:00
Daniel Gultsch b9e4296321
reformat debug info 2024-02-23 09:54:09 +01:00
Daniel Gultsch dfa389f61f
update UI in case proposal gets retracted by system 2024-02-23 09:54:09 +01:00
Daniel Gultsch a44ad6015d
update UI with correct state after UI gets invoked with ACTION_VIEW 2024-02-23 09:54:08 +01:00
Daniel Gultsch d20cc87bda
retract proposal when accepting other call 2024-02-23 09:54:08 +01:00
Daniel Gultsch 4378f8931b
add Config flag to debug direct call init 2024-02-23 09:54:08 +01:00
Daniel Gultsch 19c634f3d2
use call integration via MANAGE_OWN_CALLS
better integrate calls into the system via 'Build a calling app'¹

a few hooks like onAnswer/onReject and automatic PhoneAccount creation are still missing

¹: https://developer.android.com/develop/connectivity/telecom/selfManaged
2024-02-23 09:54:08 +01:00
gallegonovato aefcce430d
Translated using Weblate (Spanish)
Currently translated at 100.0% (57 of 57 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/es/
2024-02-20 16:52:44 +00:00
SomeTr 1c2db7bcc5
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (57 of 57 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/uk/
2024-02-20 16:52:44 +00:00
nautilusx e5098d2556
Translated using Weblate (German)
Currently translated at 100.0% (57 of 57 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/de/
2024-02-20 16:52:44 +00:00
nautilusx 16644810cc
Translated using Weblate (German)
Currently translated at 100.0% (9 of 9 strings)

Translation: Conversations/Android App (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-quicksy/de/
2024-02-20 16:52:44 +00:00
SomeTr 268c09bda0
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (982 of 982 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/uk/
2024-02-20 16:52:44 +00:00
gallegonovato 38910d4406
Translated using Weblate (Spanish)
Currently translated at 100.0% (982 of 982 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/es/
2024-02-20 16:52:44 +00:00
nautilusx 34cf6d758a
Translated using Weblate (German)
Currently translated at 100.0% (982 of 982 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/
2024-02-20 16:52:44 +00:00
Anonymous 24a387f705
Translated using Weblate (Silesian)
Currently translated at 94.1% (925 of 982 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/szl/
2024-02-20 16:52:44 +00:00
Anonymous 6b7bc1027e
Translated using Weblate (Vietnamese)
Currently translated at 97.5% (958 of 982 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/vi/
2024-02-20 16:52:44 +00:00
Anonymous a6ef77db92
Translated using Weblate (Serbian)
Currently translated at 65.2% (641 of 982 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/sr/
2024-02-20 16:52:44 +00:00
Anonymous b3aa78c50a
Translated using Weblate (Portuguese (Brazil))
Currently translated at 96.0% (943 of 982 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/pt_BR/
2024-02-20 16:52:44 +00:00
Anonymous 8531bae43d
Translated using Weblate (Norwegian Bokmål)
Currently translated at 46.9% (461 of 982 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/nb_NO/
2024-02-20 16:52:44 +00:00
Anonymous c846ac8723
Translated using Weblate (Russian)
Currently translated at 99.4% (977 of 982 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ru/
2024-02-20 16:52:44 +00:00
Anonymous d32fa8ae1b
Translated using Weblate (Portuguese)
Currently translated at 41.2% (405 of 982 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/pt/
2024-02-20 16:52:44 +00:00
Anonymous 5c13f30058
Translated using Weblate (Polish)
Currently translated at 99.0% (973 of 982 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/pl/
2024-02-20 16:52:44 +00:00
Anonymous c98d14757f
Translated using Weblate (Korean)
Currently translated at 40.1% (394 of 982 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ko/
2024-02-20 16:52:44 +00:00
Anonymous 007731f757
Translated using Weblate (Japanese)
Currently translated at 98.4% (967 of 982 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ja/
2024-02-20 16:52:44 +00:00
Anonymous 75707507ba
Translated using Weblate (Hebrew)
Currently translated at 27.9% (274 of 982 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/he/
2024-02-20 16:52:44 +00:00
Anonymous d668819c01
Translated using Weblate (Italian)
Currently translated at 99.7% (980 of 982 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/it/
2024-02-20 16:52:44 +00:00
Anonymous 5cdd0c0609
Translated using Weblate (Indonesian)
Currently translated at 47.7% (469 of 982 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/id/
2024-02-20 16:52:44 +00:00
Anonymous 6785fe049d
Translated using Weblate (Hungarian)
Currently translated at 87.1% (856 of 982 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/hu/
2024-02-20 16:52:44 +00:00
Anonymous ab2d363873
Translated using Weblate (Galician)
Currently translated at 100.0% (982 of 982 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/gl/
2024-02-20 16:52:44 +00:00
Anonymous a964bc171d
Translated using Weblate (Finnish)
Currently translated at 91.7% (901 of 982 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/fi/
2024-02-20 16:52:44 +00:00
Anonymous 117c4e310b
Translated using Weblate (Spanish)
Currently translated at 100.0% (982 of 982 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/es/
2024-02-20 16:52:44 +00:00
Anonymous 4ad410e795
Translated using Weblate (Danish)
Currently translated at 97.5% (958 of 982 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/da/
2024-02-20 16:52:44 +00:00
Anonymous 149ea5f6a9
Translated using Weblate (Greek)
Currently translated at 94.2% (926 of 982 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/el/
2024-02-20 16:52:44 +00:00
Anonymous e6ec72aaf7
Translated using Weblate (German)
Currently translated at 100.0% (982 of 982 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/
2024-02-20 16:52:44 +00:00
Anonymous 2cc0ea93ae
Translated using Weblate (Czech)
Currently translated at 96.2% (945 of 982 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/cs/
2024-02-20 16:52:44 +00:00
Anonymous f199999c5f
Translated using Weblate (Bulgarian)
Currently translated at 93.2% (916 of 982 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/bg/
2024-02-20 16:52:43 +00:00
licaon-kter 710cf21fb0
Translated using Weblate (Romanian)
Currently translated at 100.0% (982 of 982 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ro/
2024-02-20 16:52:43 +00:00
Daniel Gultsch 5183a51625
version bump to 2.13.4 2024-02-20 10:27:35 +01:00
Daniel Gultsch 38a77455a2
add Contacts permission to play store flavor 2024-02-20 10:25:18 +01:00
Daniel Gultsch d50cd3ae39
push changelog (to be translated before release) 2024-02-19 14:34:25 +01:00
nautilusx 86486c54e7 Translated using Weblate (German)
Currently translated at 100.0% (56 of 56 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/de/
2024-02-19 09:13:35 +00:00
nautilusx 293a2f54fe Translated using Weblate (German)
Currently translated at 100.0% (982 of 982 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/
2024-02-19 09:13:35 +00:00
gallegonovato 3fe418b807 Translated using Weblate (Spanish)
Currently translated at 100.0% (56 of 56 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/es/
2024-02-19 09:13:35 +00:00
gallegonovato a1013a6aad Translated using Weblate (Spanish)
Currently translated at 100.0% (982 of 982 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/es/
2024-02-19 09:13:35 +00:00
ghose 80d1e67e20 Translated using Weblate (Galician)
Currently translated at 50.0% (28 of 56 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/gl/
2024-02-19 09:13:35 +00:00
ghose 2025d9773c Translated using Weblate (Galician)
Currently translated at 100.0% (982 of 982 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/gl/
2024-02-19 09:13:35 +00:00
Daniel Gultsch 8e73b7f477
make contact integration depend on manifest, not flavor 2024-02-19 10:12:52 +01:00
kosyak 16a834e0eb fix commands related bugs 2024-02-19 03:58:26 +01:00
kosyak d5a089fb24 leakCanary only in debug 2024-02-19 00:49:30 +01:00
kosyak 94d9b69c6c fix memory leak 2024-02-19 00:46:18 +01:00
kosyak 3db7c2c3da add leakCanary 2024-02-19 00:46:11 +01:00
kosyak abcdd96cc9 proper swipe to reply handling 2024-02-18 23:32:44 +01:00
Daniel Gultsch 9b832e1285
null check PushTargetMessenger 2024-02-18 17:38:32 +01:00
Daniel Gultsch a62a7a4a84
resolver results need to be editable if we inject see-other-host 2024-02-18 10:20:54 +01:00
Daniel Gultsch d175843cbd
ignore 'subscribe' presence for blocked contacts 2024-02-16 16:59:52 +01:00
Outbreak2096 5692cccf70
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (56 of 56 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/zh_Hans/
2024-02-15 14:20:58 +00:00
SomeTr d66eb14713
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (56 of 56 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/uk/
2024-02-15 14:20:58 +00:00
Outbreak2096 06adbf18c8
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (982 of 982 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2024-02-15 14:20:57 +00:00
SomeTr 6cc2f639df
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (982 of 982 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/uk/
2024-02-15 14:20:57 +00:00
0que cf8a075529
Translated using Weblate (Russian)
Currently translated at 99.6% (977 of 980 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ru/
2024-02-15 10:56:36 +00:00
ghose dc88c3a5d6
Translated using Weblate (Galician)
Currently translated at 100.0% (980 of 980 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/gl/
2024-02-15 10:56:36 +00:00
Daniel Gultsch 956107511c
version bump to 2.13.3 + changelog 2024-02-15 08:39:00 +01:00
kosyak 6c8d9c30ab fix multiline reply quote 2024-02-14 18:18:16 +01:00
Daniel Gultsch 8598462737
remove address book integration from Conversations on PlayStore 2024-02-14 13:53:46 +01:00
Daniel Gultsch 33c63fb562
fixup: access Android_id only on push 2024-02-14 12:09:08 +01:00
Daniel Gultsch 59c23f5558
add ability to display privacy policy link in menu 2024-02-14 09:11:49 +01:00
Daniel Gultsch 617dd76d2f
add shortcut info only for 'messages' channel 2024-02-13 18:29:08 +01:00
Daniel Gultsch 1cfc5d426e
access Android_id only on push 2024-02-13 11:04:38 +01:00
SomeTr 5fa1caf7ee
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (55 of 55 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/uk/
2024-02-12 13:09:11 +00:00
ghose 0fb3fabd5f
Translated using Weblate (Galician)
Currently translated at 49.0% (27 of 55 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/gl/
2024-02-12 13:09:10 +00:00
SomeTr 65149c9f2d
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (980 of 980 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/uk/
2024-02-12 13:09:10 +00:00
ghose ce8ccd0c79
Translated using Weblate (Galician)
Currently translated at 100.0% (980 of 980 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/gl/
2024-02-12 13:09:10 +00:00
Daniel Gultsch 5cad2dccb2
version bump to 2.13.2 (and rename changelogs) 2024-02-11 16:40:17 +01:00
Daniel Gultsch 2833fc833c
fix JET spec compliance 2024-02-11 10:40:50 +01:00
Daniel Gultsch 93b5a099b0
add json mime type according to RFC4627 2024-02-11 08:45:46 +01:00
acioustick 6182ba9f93
Translated using Weblate (Japanese)
Currently translated at 100.0% (9 of 9 strings)

Translation: Conversations/Android App (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-quicksy/ja/
2024-02-11 06:58:20 +00:00
acioustick 9ad95ba402
Translated using Weblate (Japanese)
Currently translated at 98.6% (967 of 980 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ja/
2024-02-11 06:58:20 +00:00
acioustick 8181eb18bc
Translated using Weblate (Japanese)
Currently translated at 98.6% (967 of 980 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ja/
2024-02-10 12:54:09 +00:00
Outbreak2096 0ef071b65b
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (55 of 55 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/zh_Hans/
2024-02-10 12:53:24 +00:00
random_r f9e49b0fa6
Translated using Weblate (Italian)
Currently translated at 100.0% (55 of 55 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/it/
2024-02-10 12:53:24 +00:00
Outbreak2096 ad86d3317e
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (980 of 980 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2024-02-10 12:53:24 +00:00
acioustick 35cfc1028b
Translated using Weblate (Japanese)
Currently translated at 98.6% (967 of 980 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ja/
2024-02-10 12:53:24 +00:00
random_r 1dcf2a4732
Translated using Weblate (Italian)
Currently translated at 100.0% (980 of 980 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/it/
2024-02-10 12:53:23 +00:00
gallegonovato 899432c0e2
Translated using Weblate (Spanish)
Currently translated at 100.0% (55 of 55 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/es/
2024-02-10 12:53:23 +00:00
licaon-kter d4cdd3f39f
Translated using Weblate (Romanian)
Currently translated at 100.0% (980 of 980 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ro/
2024-02-10 12:53:23 +00:00
gallegonovato 431fe50fd6
Translated using Weblate (Spanish)
Currently translated at 100.0% (980 of 980 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/es/
2024-02-10 12:53:23 +00:00
licaon-kter 7fc79edef7
Translated using Weblate (Romanian)
Currently translated at 100.0% (979 of 979 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ro/
2024-02-10 12:53:23 +00:00
SomeTr 1118184ff0
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (979 of 979 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/uk/
2024-02-10 12:53:23 +00:00
ghose c1052c5811
Translated using Weblate (Galician)
Currently translated at 100.0% (979 of 979 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/gl/
2024-02-10 12:53:23 +00:00
gallegonovato ce997eb223
Translated using Weblate (Spanish)
Currently translated at 100.0% (979 of 979 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/es/
2024-02-10 12:53:23 +00:00
nautilusx 513ec57919
Translated using Weblate (German)
Currently translated at 100.0% (979 of 979 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/
2024-02-10 12:53:23 +00:00
Daniel Gultsch 9f1c4a42f8
default to microphone foreground service type during call 2024-02-10 13:46:07 +01:00
Daniel Gultsch 6ce052a337
check sasl success message no response 2024-02-10 10:20:40 +01:00
Daniel Gultsch 75f42d680d
lower bitrate for voice recordings 2024-02-09 17:54:25 +01:00
p42ity cd1821f2b6 Avoid truncated voice messages for affected devices
There were some devices which are known to produce truncated voice messages from time to time. I investigated this issue on a Fairphone 4 and found a configuration which did not show this behaviour in my experiments.

These devices are very sensitive on AAC settings. That's why I added an extra section for them. The list of devices can be adjusted.

Reviewed-on: https://codeberg.org/iNPUTmice/Conversations/pulls/192
Co-authored-by: p42ity <shopping@fpgas.de>
Co-committed-by: p42ity <shopping@fpgas.de>
2024-02-09 16:50:19 +00:00
Daniel Gultsch b93c5622df
modify quicksy user consent wording once more 2024-02-09 11:25:00 +01:00
Daniel Gultsch 03dc15158b
use modern Java in XmppConnection 2024-02-09 11:23:05 +01:00
Daniel Gultsch 0f34444b99
add next fastlane changelog (to be translated) 2024-02-08 11:47:25 +01:00
Licaon_Kter b08466d688 No one knows what a "2D Barcode" is
"stolen" from https://git.singpolyma.net/cheogram-android/commit/e9ce8ebbaf57067b71f11467312975b62c04cca7
2024-02-08 10:36:44 +00:00
Daniel Gultsch 5aff7d023c
slight modifications in quicksy onboard flow 2024-02-08 11:06:07 +01:00
Daniel Gultsch 3882ea669d
check server from on stream open 2024-02-07 10:55:33 +01:00
Daniel Gultsch 01ac2912f5
catch exception when hardware renderer can not be set up 2024-02-07 10:55:01 +01:00
Daniel Gultsch 7fe8be1adc
better work around for not processing race condition stanza 2024-02-07 10:07:53 +01:00
Daniel Gultsch 7455e99761
remove unnecessary resume call 2024-02-07 09:15:36 +01:00
Stephen Paul Weber 961a024aa2 XEP says thread means not the topic
Note: A message with a <subject/> and a <body/> or a <subject/> and a
<thread/> is a legitimate message, but it SHALL NOT be interpreted as a
subject change.
2024-02-07 07:22:57 +00:00
Daniel Gultsch a63b419e58
version bump to 2.13.1 (and rename changelogs) 2024-02-07 07:33:26 +01:00
nautilusx 600d6f6fc9
Translated using Weblate (German)
Currently translated at 100.0% (54 of 54 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/de/
2024-02-07 06:30:48 +00:00
nautilusx 38d0090782
Translated using Weblate (German)
Currently translated at 100.0% (979 of 979 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/
2024-02-07 06:30:48 +00:00
random_r 3ffa952d1f
Translated using Weblate (Italian)
Currently translated at 100.0% (54 of 54 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/it/
2024-02-07 06:30:48 +00:00
NLBRT 4fbfc22670
Translated using Weblate (Bengali (India))
Currently translated at 100.0% (13 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/bn_IN/
2024-02-07 06:30:48 +00:00
gallegonovato d223b4c65b
Translated using Weblate (Spanish)
Currently translated at 100.0% (54 of 54 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/es/
2024-02-07 06:30:48 +00:00
Outbreak2096 887ec3b33c
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (54 of 54 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/zh_Hans/
2024-02-07 06:30:48 +00:00
Outbreak2096 20f096aab8
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (2 of 2 strings)

Translation: Conversations/App Store Metadata (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-conversations/zh_Hans/
2024-02-07 06:30:48 +00:00
SomeTr b18d0f2351
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (54 of 54 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/uk/
2024-02-07 06:30:48 +00:00
ghose 371dfed57c
Translated using Weblate (Galician)
Currently translated at 48.1% (26 of 54 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/gl/
2024-02-07 06:30:48 +00:00
Outbreak2096 e857948500
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (979 of 979 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2024-02-07 06:30:48 +00:00
Application-Maker 37194c71a8
Translated using Weblate (Russian)
Currently translated at 100.0% (9 of 9 strings)

Translation: Conversations/Android App (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-quicksy/ru/
2024-02-07 06:30:48 +00:00
Application-Maker f80e11de92
Translated using Weblate (Russian)
Currently translated at 99.8% (978 of 979 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ru/
2024-02-07 06:30:48 +00:00
Daniel Gultsch ff082ab607
code clean up in IQ callback handling 2024-02-06 14:27:32 +01:00
Daniel Gultsch c2592d1417
fixup: simplify loginInfo null check 2024-02-05 15:59:06 +01:00
Daniel Gultsch 5ab8912cb4
simplify loginInfo null check 2024-02-04 19:49:55 +01:00
kosyak 7db4435e7c throw invalid viewType exception 2024-02-01 20:20:48 +01:00
Daniel Gultsch a6ba658494
version bump to 2.13.1-beta.3 2024-01-21 18:10:30 +01:00
kosyak a45e35bbcc new launcher icons 2024-01-20 02:03:47 +01:00
kosyak 07fdaac943 paint icons in new primary color 2024-01-20 01:40:55 +01:00
kosyak 366e5aa389 show real reply text instead of fallback 2024-01-20 00:56:22 +01:00
Daniel Gultsch 4168bc4666
less noisy logcat. catch illegal state exception as result of race condition 2024-01-19 15:20:47 +01:00
Daniel Gultsch b2e1f9b3d8
call current transport when receiving transport-replace 2024-01-19 11:43:23 +01:00
Daniel Gultsch 15a3c163eb
respond OK to JFT session terminate 2024-01-19 10:40:32 +01:00
Daniel Gultsch aa558f83f2
bump copyright year to 2024 2024-01-15 19:25:53 +01:00
Daniel Gultsch dd0c82302b
version bump to 2.13.1-beta.2 and changelog 2024-01-15 15:36:00 +01:00
MasoudAbkenar ce645bbd41
Translated using Weblate (Persian)
Currently translated at 100.0% (9 of 9 strings)

Translation: Conversations/Android App (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-quicksy/fa/
2024-01-15 12:58:30 +00:00
glut4 84fbd07fb2
Translated using Weblate (Dutch)
Currently translated at 92.3% (12 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/nl/
2024-01-15 12:58:30 +00:00
nautilusx e7e174f4d7
Translated using Weblate (German)
Currently translated at 100.0% (979 of 979 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/
2024-01-15 12:58:30 +00:00
MasoudAbkenar 85d582acf5
Translated using Weblate (Persian)
Currently translated at 100.0% (9 of 9 strings)

Translation: Conversations/Android App (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-quicksy/fa/
2024-01-15 12:58:30 +00:00
MasoudAbkenar 7b9b90df4d
Translated using Weblate (Persian)
Currently translated at 100.0% (979 of 979 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/fa/
2024-01-15 12:58:30 +00:00
MasoudAbkenar 53a7ea362d
Translated using Weblate (German)
Currently translated at 100.0% (979 of 979 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/
2024-01-15 12:58:30 +00:00
MasoudAbkenar 1cc13d175f
Translated using Weblate (Persian)
Currently translated at 100.0% (13 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/fa/
2024-01-15 12:58:30 +00:00
MasoudAbkenar 165617aa73
Translated using Weblate (Persian)
Currently translated at 57.9% (567 of 979 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/fa/
2024-01-15 12:58:30 +00:00
MasoudAbkenar dd4e9ee7cb
Added translation using Weblate (Persian) 2024-01-15 12:58:30 +00:00
nautilusx bc3843524b
Translated using Weblate (German)
Currently translated at 100.0% (53 of 53 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/de/
2024-01-15 12:58:29 +00:00
ghose fe0cbc48ae
Translated using Weblate (Galician)
Currently translated at 100.0% (979 of 979 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/gl/
2024-01-15 12:58:29 +00:00
Daniel Gultsch 88851ea12a
bundle letsencrypt root certificates
as per https://letsencrypt.org/2023/07/10/cross-sign-expiration.html
Letsencrypt is dropping support for Android <= 7 by removing cross signing.

to keep supporting older Android versions we need to bundle the root cert
ourselves. (Firefox for example does this too)

The KeyStore file is in BKS-V1 format. A good tools to edit the file is

https://keystore-explorer.org/

To keep the attack and bug surface as low as possible we only run the check
against the bundled keystore on Android <= 7
2024-01-15 10:57:07 +01:00
Daniel Gultsch 3cf59b4181
bump various dependencies 2024-01-12 16:51:46 +01:00
kosyak 869f92169d blue led color 2024-01-10 15:00:30 +01:00
kosyak f50bb56ad7 support conversations grouping on ShareWith screen 2024-01-10 00:27:08 +01:00
kosyak 31ac27f5ec fix not working accent color 2024-01-10 00:23:30 +01:00
kosyak 9b748a7b4f change notifications led color 2024-01-10 00:17:46 +01:00
kosyak 6872b36f74 save expanded items in persistent storage 2024-01-09 23:20:11 +01:00
kosyak 5f16051cf7 group conversation by tags 2024-01-01 23:36:07 +01:00
kosyak c4c5aaa6d6 Revert "debug led color"
This reverts commit 60805b28b6.
2024-01-01 18:57:55 +01:00
kosyak a6fbcd62c5 Revert "debug custom led color"
This reverts commit 2b129a1738.
2024-01-01 18:57:50 +01:00
kosyak 2b129a1738 debug custom led color 2024-01-01 12:25:15 +01:00
kosyak 60805b28b6 debug led color 2024-01-01 12:23:35 +01:00
kosyak 6a1499654a fix crash during state saving 2024-01-01 11:52:00 +01:00
kosyak a65f2ad98c another default primary color 2024-01-01 11:51:41 +01:00
kosyak 510f93ce78 split roster by different accounts 2023-12-27 04:09:18 +01:00
kosyak 54ac152019 fix reply icon tint 2023-12-27 01:11:16 +01:00
kosyak c862959e3f fix crash 2023-12-27 01:11:08 +01:00
kosyak 78410291b7 better self contact handling 2023-12-27 01:11:01 +01:00
kosyak 6284ee12af new ui category in expert settings 2023-12-27 01:10:23 +01:00
kosyak bef39f7e0c led color follows current app primary colort 2023-12-27 01:08:35 +01:00
kosyak 543146e94a change default accent color 2023-12-27 01:08:04 +01:00
kosyak 305ae7a288 reactions 2023-12-27 01:07:08 +01:00
Daniel Gultsch aeb805a3ca
abort socks candidate search if peer selected something with higher priority 2023-12-20 11:23:04 +01:00
Daniel Gultsch eec01c9e7b
disable quick log 2023-12-20 09:23:09 +01:00
Daniel Gultsch d3b38a5273
refactor Jingle File Transfer. add WebRTCDatachannel transport 2023-12-19 17:26:11 +01:00
kosyak 9467fc1789 support 'Save to downloads' action for attachments 2023-12-18 04:32:23 +01:00
kosyak f9027fa085 fix compilation 2023-12-18 04:25:06 +01:00
kosyak db27258c18 fix crop lib compatibility issues 2023-12-18 04:24:49 +01:00
kosyak 73bc3c2617 temporary disable domian jid sanity check 2023-12-17 23:54:58 +01:00
kosyak 7f7e4b10a4 info about pm on muc details screen 2023-12-17 23:46:06 +01:00
kosyak 3ddf8d33f0 fix compilation 2023-12-17 22:16:12 +01:00
kosyak b9f5286898 Merge branch 'master' of https://codeberg.org/iNPUTmice/Conversations
# Conflicts:
#	build.gradle
#	src/main/AndroidManifest.xml
#	src/main/java/eu/siacs/conversations/Config.java
#	src/main/java/eu/siacs/conversations/entities/Bookmark.java
#	src/main/java/eu/siacs/conversations/parser/MessageParser.java
#	src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
#	src/main/java/eu/siacs/conversations/ui/ConversationsActivity.java
#	src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java
#	src/main/java/eu/siacs/conversations/ui/XmppActivity.java
#	src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
#	src/main/java/eu/siacs/conversations/xmpp/jingle/ToneManager.java
#	src/main/java/eu/siacs/conversations/xmpp/pep/PublishOptions.java
#	src/main/res/values/strings.xml
2023-12-17 22:14:36 +01:00
kosyak e0231493d0 Merge branch 'master' of https://dev.narayana.im/narayana/Conversations 2023-12-17 20:57:16 +01:00
kosyak 9ebb719d71 allow to merge private messages 2023-12-17 18:44:35 +01:00
Bohdan Horbeshko 69dac97f88 show replies not starting with > 2023-12-16 19:06:40 +02:00
ghose 8208724172
Translated using Weblate (Galician)
Currently translated at 100.0% (2 of 2 strings)

Translation: Conversations/App Store Metadata (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-quicksy/gl/
2023-12-13 06:44:44 +00:00
Salif Mehmed 5a20273e1a
Translated using Weblate (Bulgarian)
Currently translated at 93.9% (920 of 979 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/bg/
2023-12-13 06:44:44 +00:00
RTRedreovic 9be4ddf4ce
Added translation using Weblate (Esperanto) 2023-12-13 06:44:43 +00:00
Daniel Gultsch 22a9e9ee5b remove some outdated information from readme 2023-12-10 11:37:04 +00:00
Daniel Gultsch 1a83c290a2
UnifiedPush: send unregistered to apps when 'none' account is selected 2023-12-05 10:59:50 +01:00
Daniel Gultsch 20c179c1a1
ensure will tell 'messenger' when UP registration fails or is delayed 2023-12-02 12:20:19 +01:00
Daniel Gultsch f7b5124fd3
support location attribute on enable 2023-12-01 18:29:52 +01:00
Daniel Gultsch a11d6638b0
bump gradle plugin version 2023-12-01 17:15:26 +01:00
Daniel Gultsch eb5994f80f
add quick log functionality to debug UP 2023-12-01 13:22:10 +01:00
Outbreak2096 f8e420ad37
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (2 of 2 strings)

Translation: Conversations/App Store Metadata (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-quicksy/zh_Hans/
2023-11-22 07:38:18 +00:00
Outbreak2096 ab0bc9296d
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (53 of 53 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/zh_Hans/
2023-11-22 07:38:18 +00:00
random_r 84f4f3c2f8
Translated using Weblate (Italian)
Currently translated at 100.0% (53 of 53 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/it/
2023-11-22 07:38:18 +00:00
Outbreak2096 efbdce8e10
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (979 of 979 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2023-11-22 07:38:18 +00:00
Daniel Gultsch 747f58c35b
JingleConnectionManager: code clean up 2023-11-22 08:37:17 +01:00
Daniel Gultsch b183f49977
update call UI after RTP connection has ended 2023-11-22 08:36:44 +01:00
Daniel Gultsch 402882389f
fixup: properly detect fast 2023-11-21 19:40:10 +01:00
Daniel Gultsch 59ff27062b
treat carbons as enabled when requested through bind 2 2023-11-21 16:50:46 +01:00
Daniel Gultsch 3dac9ef3f4
use stricter namespace matching in stream parser 2023-11-21 15:25:21 +01:00
Daniel Gultsch 5bb8f3f9aa
stricter bind 2 inline feature parsing 2023-11-21 15:24:31 +01:00
Daniel Gultsch acef5dbd3b
code clean up 2023-11-20 19:29:19 +01:00
RTRedreovic 796405a214
Translated using Weblate (Esperanto)
Currently translated at 50.0% (1 of 2 strings)

Translation: Conversations/App Store Metadata (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-quicksy/eo/
2023-11-20 04:13:39 +00:00
RTRedreovic 558ac385a5
Translated using Weblate (Esperanto)
Currently translated at 50.0% (1 of 2 strings)

Translation: Conversations/App Store Metadata (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-conversations/eo/
2023-11-20 04:13:39 +00:00
RTRedreovic 80d460bf18
Translated using Weblate (Esperanto)
Currently translated at 100.0% (9 of 9 strings)

Translation: Conversations/Android App (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-quicksy/eo/
2023-11-20 04:13:39 +00:00
RTRedreovic 6dbd079e93
Translated using Weblate (Esperanto)
Currently translated at 100.0% (13 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/eo/
2023-11-20 04:13:39 +00:00
Riku_V 1b39f3b421
Translated using Weblate (Finnish)
Currently translated at 100.0% (2 of 2 strings)

Translation: Conversations/App Store Metadata (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-conversations/fi/
2023-11-20 04:13:39 +00:00
Hund 1c46e59dfa
Translated using Weblate (Swedish)
Currently translated at 100.0% (979 of 979 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/sv/
2023-11-20 04:13:39 +00:00
SomeTr 4ee3501064
Translated using Weblate (Finnish)
Currently translated at 92.4% (905 of 979 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/fi/
2023-11-20 04:13:38 +00:00
Riku_V 480d7218da
Translated using Weblate (Finnish)
Currently translated at 92.4% (905 of 979 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/fi/
2023-11-20 04:13:38 +00:00
nautilusx c1ef22178e
Translated using Weblate (German)
Currently translated at 100.0% (979 of 979 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/
2023-11-20 04:13:38 +00:00
RTRedreovic ac3e249f96
Added translation using Weblate (Esperanto) 2023-11-19 03:47:24 +00:00
RTRedreovic 36d5552433
Added translation using Weblate (Esperanto) 2023-11-19 03:35:32 +00:00
Riku_V a6aaa25517
Translated using Weblate (Finnish)
Currently translated at 90.0% (882 of 979 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/fi/
2023-11-18 20:59:05 +00:00
ghose 9ab5de9774
Translated using Weblate (Galician)
Currently translated at 47.1% (25 of 53 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/gl/
2023-11-18 08:18:32 +00:00
0que 0587bcf676
Translated using Weblate (Russian)
Currently translated at 99.8% (978 of 979 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ru/
2023-11-18 04:17:29 +00:00
dieserniko c2db603c38
Translated using Weblate (German)
Currently translated at 100.0% (979 of 979 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/
2023-11-18 04:17:29 +00:00
gallegonovato a630bdab14
Translated using Weblate (Spanish)
Currently translated at 100.0% (53 of 53 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/es/
2023-11-17 14:14:20 +00:00
SomeTr 404cc1a21b
Translated using Weblate (German)
Currently translated at 100.0% (979 of 979 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/
2023-11-17 14:14:20 +00:00
dieserniko 6ea4a0a6fb
Translated using Weblate (German)
Currently translated at 100.0% (979 of 979 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/
2023-11-17 14:14:20 +00:00
Outbreak2096 26096b564a
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (53 of 53 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/zh_Hans/
2023-11-17 12:40:29 +00:00
SomeTr 8639581ba9
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (53 of 53 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/uk/
2023-11-17 12:40:29 +00:00
Eryk Michalak 6214c6e300
Translated using Weblate (Polish)
Currently translated at 100.0% (979 of 979 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/pl/
2023-11-16 21:13:39 +00:00
random_r 802167e925
Translated using Weblate (Italian)
Currently translated at 100.0% (979 of 979 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/it/
2023-11-16 21:13:39 +00:00
gallegonovato 35307c29d8
Translated using Weblate (Spanish)
Currently translated at 100.0% (979 of 979 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/es/
2023-11-16 21:13:39 +00:00
Daniel Gultsch 35c03c34b7
version bump to 2.13.0 + changelog 2023-11-15 09:19:46 +01:00
Daniel Gultsch 25dd775613
catch illegal argument exception when reading DNS 2023-11-14 15:13:45 +01:00
Daniel Gultsch c72a86a0a4
add timeout to ICE gathering 2023-11-14 08:57:22 +01:00
kosyak c09551b4c0 fix sorting 2023-11-13 22:27:18 +01:00
kosyak b2556daacd fix npe 2023-11-13 22:27:11 +01:00
kosyak abc42647ae fix primary color usages 2023-11-13 22:27:05 +01:00
Daniel Gultsch 5728cf13ea
RtpSessionActivity code clean up 2023-11-13 17:12:18 +01:00
Daniel Gultsch 622b569bbc
fix accepting senders both content adds 2023-11-13 13:46:30 +01:00
Daniel Gultsch 80c49955f0
JingleRtpConnection code clean up 2023-11-13 13:46:30 +01:00
Outbreak2096 1471969237
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (979 of 979 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2023-11-13 11:38:39 +00:00
licaon-kter 4b693fd86c
Translated using Weblate (Romanian)
Currently translated at 100.0% (979 of 979 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ro/
2023-11-13 11:38:39 +00:00
SomeTr f9d0cdb983
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (979 of 979 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/uk/
2023-11-13 11:38:39 +00:00
ghose 6dba7d01c8
Translated using Weblate (Galician)
Currently translated at 100.0% (979 of 979 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/gl/
2023-11-13 11:38:39 +00:00
gallegonovato bbcba68cfb
Translated using Weblate (Spanish)
Currently translated at 100.0% (977 of 977 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/es/
2023-11-13 11:38:39 +00:00
Daniel Gultsch 96dcc75ac3
add SDP Offer / Answer support 2023-11-13 12:36:20 +01:00
Daniel Gultsch 38ca53fcac
bump reporting xep and add ability to report messages 2023-11-12 19:29:15 +01:00
lionpancake 60dd710d43
Translated using Weblate (Chinese (Traditional))
Currently translated at 63.4% (33 of 52 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/zh_Hant/
2023-11-11 07:54:00 +00:00
Outbreak2096 2d4c54ab5a
Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (977 of 977 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hant/
2023-11-11 07:54:00 +00:00
lionpancake b47f53b3ed
Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (977 of 977 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hant/
2023-11-11 07:54:00 +00:00
Outbreak2096 54c590bf4a
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (977 of 977 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2023-11-11 07:54:00 +00:00
licaon-kter ad772fcc21
Translated using Weblate (Romanian)
Currently translated at 100.0% (977 of 977 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ro/
2023-11-11 07:54:00 +00:00
SomeTr 81e8f9df1e
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (977 of 977 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/uk/
2023-11-11 07:54:00 +00:00
ghose 1467dd37f9
Translated using Weblate (Galician)
Currently translated at 100.0% (977 of 977 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/gl/
2023-11-11 07:54:00 +00:00
Hund 2ecd035c47
Translated using Weblate (Swedish)
Currently translated at 97.8% (956 of 977 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/sv/
2023-11-11 07:54:00 +00:00
random_r bff1a52c46
Translated using Weblate (Italian)
Currently translated at 100.0% (977 of 977 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/it/
2023-11-11 07:54:00 +00:00
Daniel Gultsch 6b2b11b2dc
add xep-0440 to doap file 2023-11-10 15:30:58 +01:00
Daniel Gultsch e73fbac56f
enable 'PEP Native Bookmarks' 2023-11-10 15:29:26 +01:00
Stephen Paul Weber f798102978
Fix and updates Bookmarks2 support
Support the stable version namespace and the new elements, including preserving
any extension content.
2023-11-10 11:41:02 +01:00
Daniel Gultsch 2b1730568d
remove duplicate items from doap 2023-11-10 11:36:02 +01:00
Daniel Gultsch 49910aed0f
fix wording that broke in earlier commit
f739752f76 (Spelling: Language reworked) broke
the wording around trusting fingerprints. this commit fixes this.
2023-11-09 08:58:27 +01:00
SomeTr 58303a6b90
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (9 of 9 strings)

Translation: Conversations/Android App (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-quicksy/uk/
2023-11-07 12:13:38 +00:00
licaon-kter 233039ac65
Translated using Weblate (Romanian)
Currently translated at 100.0% (977 of 977 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ro/
2023-11-07 12:13:38 +00:00
SomeTr 12754d2fbe
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (977 of 977 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/uk/
2023-11-07 12:13:38 +00:00
Outbreak2096 e0188659cf
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (2 of 2 strings)

Translation: Conversations/App Store Metadata (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-conversations/zh_Hans/
2023-11-06 08:26:56 +00:00
Outbreak2096 e2fc8b8b22
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (977 of 977 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2023-11-06 08:26:55 +00:00
SomeTr c303fb9325
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (977 of 977 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/uk/
2023-11-06 08:26:55 +00:00
Hund 1fc4869022
Translated using Weblate (Swedish)
Currently translated at 97.8% (956 of 977 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/sv/
2023-11-06 08:26:55 +00:00
Outbreak2096 d3b778a32a
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (2 of 2 strings)

Translation: Conversations/App Store Metadata (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-conversations/zh_Hans/
2023-11-04 05:13:41 +00:00
ghose 9b61d8e2a8
Translated using Weblate (Galician)
Currently translated at 46.1% (24 of 52 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/gl/
2023-11-04 05:13:41 +00:00
Outbreak2096 661d75c4ae
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (13 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/zh_Hans/
2023-11-04 05:13:41 +00:00
Outbreak2096 3ce8c26479
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (977 of 977 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2023-11-04 05:13:40 +00:00
SomeTr a387eb1715
Translated using Weblate (Ukrainian)
Currently translated at 99.8% (976 of 977 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/uk/
2023-11-04 05:13:40 +00:00
Hund b6c511c71c
Translated using Weblate (Swedish)
Currently translated at 97.8% (956 of 977 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/sv/
2023-11-04 05:13:40 +00:00
0que 802224fbd3
Translated using Weblate (Russian)
Currently translated at 99.7% (975 of 977 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ru/
2023-11-04 05:13:40 +00:00
ghose 1c1379a798
Translated using Weblate (Galician)
Currently translated at 100.0% (977 of 977 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/gl/
2023-11-04 05:13:40 +00:00
gallegonovato 5abf075282
Translated using Weblate (Spanish)
Currently translated at 100.0% (977 of 977 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/es/
2023-11-04 05:13:40 +00:00
Outbreak2096 9f6bbea845
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (2 of 2 strings)

Translation: Conversations/App Store Metadata (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-quicksy/zh_Hans/
2023-11-04 05:13:40 +00:00
Outbreak2096 a87d5294bc
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (2 of 2 strings)

Translation: Conversations/App Store Metadata (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-conversations/zh_Hans/
2023-11-04 05:13:40 +00:00
ghose e253652348
Translated using Weblate (Galician)
Currently translated at 42.3% (22 of 52 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/gl/
2023-11-04 05:13:40 +00:00
Outbreak2096 1e84c0beb4
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (977 of 977 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2023-11-04 05:13:40 +00:00
SomeTr fb6867cf06
Translated using Weblate (Ukrainian)
Currently translated at 99.8% (976 of 977 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/uk/
2023-11-04 05:13:40 +00:00
jpb bc819d0dc3
Translated using Weblate (Dutch)
Currently translated at 80.4% (786 of 977 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/nl/
2023-11-04 05:13:40 +00:00
mmbd cdc5bbf0b8
Translated using Weblate (Japanese)
Currently translated at 97.9% (957 of 977 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ja/
2023-11-04 05:13:40 +00:00
ghose 5879aa36dc
Translated using Weblate (Galician)
Currently translated at 100.0% (977 of 977 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/gl/
2023-11-04 05:13:40 +00:00
gallegonovato 9eb88cff9b
Translated using Weblate (Spanish)
Currently translated at 100.0% (977 of 977 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/es/
2023-11-04 05:13:40 +00:00
nautilusx 5c5960dd33
Translated using Weblate (German)
Currently translated at 100.0% (977 of 977 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/
2023-11-04 05:13:40 +00:00
Daniel Gultsch a776aca1fa
do not set maxSdk on storage permissions
this might have bad interactions with legacy storage
2023-11-02 11:15:20 +01:00
Daniel Gultsch 14a6652618
version bump to 2.13.0-beta
this is just a beta release to test the Google Play policies wrt Android 14
2023-11-02 10:02:02 +01:00
Daniel Gultsch 695bdc329f
specifically only build known abis 2023-11-01 12:22:35 +01:00
Daniel Gultsch e76dfb96bf
bump some more dependencies 2023-11-01 10:19:59 +01:00
Daniel Gultsch c857ba7cb2
bump libwebrtc to m119 2023-11-01 09:59:04 +01:00
Daniel Gultsch 434736adf9
spelling 2023-11-01 08:21:13 +01:00
nautilusx aa5f21765d
Translated using Weblate (German)
Currently translated at 100.0% (970 of 970 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/
2023-11-01 06:22:35 +00:00
kosyak 2b4b0b1212 fix conversation swipe background color 2023-10-31 15:05:04 +01:00
Daniel Gultsch 0898ea77ff
jingle: do not send session-terminate after failed regneg when session already was 2023-10-31 13:16:17 +01:00
kosyak 53a4c6cbcb fix npe 2023-10-31 13:11:41 +01:00
Daniel Gultsch 438a228fc3
minor code clean up 2023-10-31 12:34:55 +01:00
Daniel Gultsch f6482c5a87
fix caps hash calculation for empty form fields 2023-10-31 11:03:59 +01:00
Daniel Gultsch fba7721cd5
fix rare concurrent modification in muc user search 2023-10-31 10:43:53 +01:00
SomeTr 4e5d65b183 Translated using Weblate (Basque)
Currently translated at 74.9% (727 of 970 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/eu/
2023-10-31 07:36:06 +00:00
SomeTr 630e65f139 Translated using Weblate (German)
Currently translated at 100.0% (970 of 970 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/
2023-10-31 07:36:06 +00:00
Daniel Gultsch e1b53cdaf6
catch outdated backup exception in ImportBackupActivity 2023-10-31 08:32:48 +01:00
kosyak 356d1e146e better messages highlighting 2023-10-30 21:32:42 +01:00
kosyak 1d7bd8f8da fix reply text color 2023-10-30 21:32:27 +01:00
kosyak 68d8e184fd fix crash during refresh feature discovery 2023-10-30 21:32:14 +01:00
kosyak f12c242628 note to self as separate context menu item 2023-10-30 21:31:54 +01:00
kosyak 11316a949d fix crash 2023-10-30 19:59:43 +01:00
kosyak b5b47f8d7d fix expandable contact clicks handling 2023-10-30 19:59:37 +01:00
Daniel Gultsch dfd7410b1a
make copy omemo fp button a show qr code button 2023-10-30 14:26:28 +01:00
Daniel Gultsch 71b6492d61
show unverified devices warning in contact and account details 2023-10-30 13:06:04 +01:00
Daniel Gultsch 0bbc1193e3
allow background activity start for OpenKeyChain intents 2023-10-29 08:54:19 +01:00
Daniel Gultsch ea5ffe92ea
improve logging when PGP decryption fails 2023-10-29 08:54:19 +01:00
Daniel Gultsch 30d681bcb8
enable Java 17 language features 2023-10-29 08:54:18 +01:00
Daniel Gultsch 10c47d86e9
add dataSync fgs type for backup import/export 2023-10-29 08:54:18 +01:00
Daniel Gultsch c636401232
add proguard rules to fix issue in retrofit 2023-10-29 08:54:18 +01:00
Daniel Gultsch 48ffde9656
toggle foreground service to set correct type when gaining permissions 2023-10-29 08:54:18 +01:00
Daniel Gultsch cec8a7ec55
bump various dependencies 2023-10-29 08:54:18 +01:00
Daniel Gultsch 9cd88f00cf
fix some linter warnings 2023-10-29 08:54:18 +01:00
Daniel Gultsch e83a0af277
ignore false positive warning wrt foreground service 2023-10-29 08:54:17 +01:00
Daniel Gultsch 35c8d31d42
update gradle and gradle plugin 2023-10-29 08:54:17 +01:00
Daniel Gultsch 417afe6eb1
bump various dependencies 2023-10-29 08:54:17 +01:00
Daniel Gultsch e422b89df5
use aggressive reconnects for see-other-host 2023-10-29 08:54:17 +01:00
Daniel Gultsch 1732ab5cc7
add internal ping timer in case alarm manager fails 2023-10-29 08:54:17 +01:00
Daniel Gultsch a162d72c2a
code clean up in processAccountState() 2023-10-29 08:54:17 +01:00
Daniel Gultsch 928db01ae8
do not init connection for disabled accounts 2023-10-29 08:54:16 +01:00
Daniel Gultsch 2dd8896dc2
stop service on log out when UI is not active 2023-10-29 08:54:16 +01:00
Daniel Gultsch d1f648f2e3
code clean up in onStartCommand 2023-10-29 08:54:16 +01:00
Daniel Gultsch 69425e677c
remove soft disable flag when attempting call 2023-10-29 08:54:16 +01:00
Daniel Gultsch f042efd550
add 'log out' button to foreground notifcation
this temporarily disconnects all accounts until the user opens the app again.

essentially this akin to an 'Exit' button

Users previously had the option to 'disable' accounts but this provides a
faster way to "free up resources" until the next time the app is opened.
2023-10-29 08:54:16 +01:00
Daniel Gultsch 418d6b09a0
explicitly declare foreground service type 2023-10-29 08:54:16 +01:00
Daniel Gultsch 7e5bf623ae
bump target SDK to 34 (Android 14) 2023-10-29 08:54:12 +01:00
Daniel Gultsch 1504287287
version bump to 2.12.12
fastlane changelog version codes now use the highest version code after abi split (the F-Droid website displays that one)
2023-10-29 08:32:56 +01:00
Outbreak2096 b8c5bcf559
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (9 of 9 strings)

Translation: Conversations/Android App (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-quicksy/zh_Hans/
2023-10-29 07:13:39 +00:00
Outbreak2096 67eea304c6
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (13 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/zh_Hans/
2023-10-29 07:13:39 +00:00
Outbreak2096 774d2a07ce
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (970 of 970 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2023-10-29 07:13:39 +00:00
SomeTr df87786177
Translated using Weblate (Czech)
Currently translated at 97.9% (950 of 970 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/cs/
2023-10-29 07:13:39 +00:00
Outbreak2096 1b3d01d014
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (52 of 52 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/zh_Hans/
2023-10-27 16:13:40 +00:00
gallegonovato b0795db9ab
Translated using Weblate (Spanish)
Currently translated at 100.0% (52 of 52 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/es/
2023-10-27 16:13:40 +00:00
Outbreak2096 4f82f8b816
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (9 of 9 strings)

Translation: Conversations/Android App (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-quicksy/zh_Hans/
2023-10-27 16:13:40 +00:00
Outbreak2096 e9883574a5
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (970 of 970 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2023-10-27 16:13:40 +00:00
SomeTr c5af91a4f7
Translated using Weblate (Turkish)
Currently translated at 97.5% (946 of 970 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/tr/
2023-10-27 16:13:40 +00:00
SomeTr 7337c804bf
Translated using Weblate (Catalan)
Currently translated at 92.1% (894 of 970 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ca/
2023-10-27 16:13:40 +00:00
Outbreak2096 3e13ac1b05
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (9 of 9 strings)

Translation: Conversations/Android App (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-quicksy/zh_Hans/
2023-10-27 16:13:40 +00:00
Outbreak2096 2a8eafa624
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (13 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/zh_Hans/
2023-10-27 16:13:40 +00:00
Outbreak2096 28455ac9d5
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (970 of 970 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2023-10-27 16:13:40 +00:00
kosyak 2d92736810 clickable replies 2023-10-27 05:31:52 +02:00
Daniel Gultsch 981dc2df6d
fix RtpSessionActivity not asking for permissions 2023-10-26 09:38:24 +02:00
kosyak f2012bc7f5 commands tab in conversation 2023-10-25 23:38:54 +02:00
Daniel Gultsch c178910e4d
version bump to 2.12.12-beta.3 2023-10-25 09:59:14 +02:00
Daniel Gultsch f6c9df00de
run account deletion callback on ui thread 2023-10-25 09:41:05 +02:00
kosyak 43870114d9 group contacts by tag 2023-10-25 04:13:36 +02:00
Outbreak2096 fb75d4cf28
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (970 of 970 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2023-10-24 19:22:05 +00:00
Outbreak2096 1dd23585c5
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (52 of 52 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/zh_Hans/
2023-10-24 19:22:05 +00:00
Outbreak2096 bb98ed503b
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (2 of 2 strings)

Translation: Conversations/App Store Metadata (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-conversations/zh_Hans/
2023-10-24 19:22:05 +00:00
SomeTr 6e6bc40199
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (52 of 52 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/uk/
2023-10-24 19:22:05 +00:00
nautilusx 1017a49f2a
Translated using Weblate (German)
Currently translated at 100.0% (52 of 52 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/de/
2023-10-24 19:22:05 +00:00
random_r 5bd7be82ad
Translated using Weblate (Italian)
Currently translated at 100.0% (52 of 52 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/it/
2023-10-24 19:22:05 +00:00
Outbreak2096 07c934b8f6
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (9 of 9 strings)

Translation: Conversations/Android App (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-quicksy/zh_Hans/
2023-10-24 19:22:05 +00:00
Outbreak2096 6855069810
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (13 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/zh_Hans/
2023-10-24 19:22:05 +00:00
Outbreak2096 20b1cb2bd7
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (970 of 970 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2023-10-24 19:22:05 +00:00
SomeTr ce051e32b1
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (970 of 970 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/uk/
2023-10-24 19:22:05 +00:00
Daniel Gultsch 6d519cd447
pick proper TTL for non existent DNS entries 2023-10-24 21:05:35 +02:00
Daniel Gultsch 1da9d42065
retrieve DNS response from cache 2023-10-24 19:16:47 +02:00
Daniel Gultsch 97acd16422
keep cache of DNS messages 2023-10-24 18:53:14 +02:00
kosyak d1d23e4627 support primary color picker 2023-10-24 02:28:07 +02:00
Daniel Gultsch 55a77c083f
store SSLSocket after starttls to fix channel binding mechanism detection 2023-10-23 14:40:40 +02:00
Daniel Gultsch 68eb17d400
create missed call notification when device is busy 2023-10-23 11:22:00 +02:00
Outbreak2096 89c59909b5 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (2 of 2 strings)

Translation: Conversations/App Store Metadata (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-quicksy/zh_Hans/
2023-10-21 15:27:04 +00:00
Outbreak2096 2ea01c0be1 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (52 of 52 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/zh_Hans/
2023-10-21 15:27:04 +00:00
Outbreak2096 b36e482287 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (2 of 2 strings)

Translation: Conversations/App Store Metadata (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-conversations/zh_Hans/
2023-10-21 15:27:04 +00:00
ghose 15dd4b75a1 Translated using Weblate (Galician)
Currently translated at 30.7% (16 of 52 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/gl/
2023-10-21 15:27:04 +00:00
nautilusx 1fda935463 Translated using Weblate (German)
Currently translated at 100.0% (52 of 52 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/de/
2023-10-21 15:27:04 +00:00
random_r 3239a98677 Translated using Weblate (Italian)
Currently translated at 100.0% (52 of 52 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/it/
2023-10-21 15:27:04 +00:00
Outbreak2096 0ae15f3482 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (9 of 9 strings)

Translation: Conversations/Android App (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-quicksy/zh_Hans/
2023-10-21 15:27:04 +00:00
Outbreak2096 39465a9f72 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (13 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/zh_Hans/
2023-10-21 15:27:04 +00:00
Outbreak2096 f524163b56 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (970 of 970 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2023-10-21 15:27:04 +00:00
ghose 284934148e Translated using Weblate (Galician)
Currently translated at 100.0% (970 of 970 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/gl/
2023-10-21 15:27:04 +00:00
Daniel Gultsch c44f4b102a
do not accept fast token w/o channel binding for channel bound login 2023-10-21 14:22:38 +02:00
Daniel Gultsch 822f3f4d22
consider going from unique or exporter to endpoint a downgrade 2023-10-21 14:21:29 +02:00
Daniel Gultsch a5f51d69e1
version bump to 2.12.12-beta.2 2023-10-20 10:33:22 +02:00
Outbreak2096 a3b2b24b11
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (51 of 51 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/zh_Hans/
2023-10-20 05:53:17 +00:00
Outbreak2096 17e8e4cff7
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (2 of 2 strings)

Translation: Conversations/App Store Metadata (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-conversations/zh_Hans/
2023-10-20 05:53:17 +00:00
Outbreak2096 072d68c0ba
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (9 of 9 strings)

Translation: Conversations/Android App (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-quicksy/zh_Hans/
2023-10-20 05:53:17 +00:00
Outbreak2096 5d52d205e8
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (970 of 970 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2023-10-20 05:53:17 +00:00
SomeTr eed71c44de
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (970 of 970 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/uk/
2023-10-20 05:53:17 +00:00
0que c37036e67a
Translated using Weblate (Russian)
Currently translated at 99.8% (969 of 970 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ru/
2023-10-20 05:53:17 +00:00
Outbreak2096 8d264fdce4
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (2 of 2 strings)

Translation: Conversations/App Store Metadata (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-quicksy/zh_Hans/
2023-10-20 05:53:17 +00:00
Outbreak2096 fecf81285b
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (51 of 51 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/zh_Hans/
2023-10-20 05:53:17 +00:00
Outbreak2096 61b1e6bbe4
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (2 of 2 strings)

Translation: Conversations/App Store Metadata (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-conversations/zh_Hans/
2023-10-20 05:53:17 +00:00
Outbreak2096 af933da91d
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (9 of 9 strings)

Translation: Conversations/Android App (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-quicksy/zh_Hans/
2023-10-20 05:53:17 +00:00
Outbreak2096 73a5c4df55
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (13 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/zh_Hans/
2023-10-20 05:53:17 +00:00
Outbreak2096 c420b6a3b1
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (970 of 970 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2023-10-20 05:53:17 +00:00
Eryk Michalak d042924afe
Translated using Weblate (Polish)
Currently translated at 100.0% (970 of 970 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/pl/
2023-10-20 05:53:17 +00:00
Daniel Gultsch d1f4fbd9de
add connect timeout to DoT 2023-10-19 20:51:19 +02:00
Daniel Gultsch 71ebca40e8
add ķ as irregular unicode 2023-10-19 18:11:08 +02:00
Daniel Gultsch f944fa0c96
add -beta to version name 2023-10-18 18:43:33 +02:00
Daniel Gultsch 8631c2f9f9
version bump to 2.12.12 + changelog 2023-10-18 11:50:10 +02:00
Daniel Gultsch 063f30ded2
ignore outgoing invite
outgoing, mediated invites are of type=normal and can end up in MAM.
We want to ignore those if they get mirrored back to us
2023-10-18 11:50:10 +02:00
Outbreak2096 fda6693b6d
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (2 of 2 strings)

Translation: Conversations/App Store Metadata (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-quicksy/zh_Hans/
2023-10-17 16:26:23 +00:00
Outbreak2096 173c438cb8
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (51 of 51 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/zh_Hans/
2023-10-17 16:26:22 +00:00
Outbreak2096 4fec00f842
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (2 of 2 strings)

Translation: Conversations/App Store Metadata (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-conversations/zh_Hans/
2023-10-17 16:26:21 +00:00
Outbreak2096 c30bc10ce2
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (9 of 9 strings)

Translation: Conversations/Android App (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-quicksy/zh_Hans/
2023-10-17 16:26:21 +00:00
Outbreak2096 55637836cc
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (13 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/zh_Hans/
2023-10-17 16:26:21 +00:00
Outbreak2096 b7e68a20b9
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (970 of 970 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2023-10-17 16:26:21 +00:00
licaon-kter 8f75e2f1e4
Translated using Weblate (Romanian)
Currently translated at 100.0% (970 of 970 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ro/
2023-10-17 16:26:20 +00:00
Outbreak2096 57ad9d21cb
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (9 of 9 strings)

Translation: Conversations/Android App (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-quicksy/zh_Hans/
2023-10-16 16:18:31 +00:00
Outbreak2096 6416df65b1
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (970 of 970 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2023-10-16 16:18:31 +00:00
SomeTr 664f9491a7
Translated using Weblate (Turkish)
Currently translated at 97.6% (947 of 970 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/tr/
2023-10-16 16:18:31 +00:00
random_r bd0c9602f3
Translated using Weblate (Italian)
Currently translated at 100.0% (970 of 970 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/it/
2023-10-16 16:18:31 +00:00
gallegonovato 8ad1ad338e
Translated using Weblate (Spanish)
Currently translated at 100.0% (970 of 970 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/es/
2023-10-16 16:18:31 +00:00
SomeTr 8da46c1658
Translated using Weblate (Catalan)
Currently translated at 92.2% (895 of 970 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ca/
2023-10-16 16:18:31 +00:00
Outbreak2096 fbf6ef962a Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (51 of 51 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/zh_Hans/
2023-10-15 15:39:38 +00:00
Outbreak2096 7eef73c0b8 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (2 of 2 strings)

Translation: Conversations/App Store Metadata (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-conversations/zh_Hans/
2023-10-15 15:39:38 +00:00
SomeTr 07032e2a80 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (51 of 51 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/uk/
2023-10-15 15:39:38 +00:00
Outbreak2096 98d36cbd7d Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (13 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/zh_Hans/
2023-10-15 15:39:38 +00:00
Outbreak2096 451f102046 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (970 of 970 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2023-10-15 15:39:38 +00:00
SomeTr 92a500bf11 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (970 of 970 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/uk/
2023-10-15 15:39:38 +00:00
ghose 5da883730f Translated using Weblate (Galician)
Currently translated at 100.0% (970 of 970 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/gl/
2023-10-15 15:39:38 +00:00
Daniel Gultsch 1b49e6b3c1
disable password edit unless unauthorized 2023-10-15 11:23:12 +02:00
Daniel Gultsch 3350ea8b5b
ensure we are in session accepted when processing content-modify 2023-10-15 08:51:15 +02:00
Daniel Gultsch 01b44948c1
support data extraction rules
Data Extraction Rules have replaced Backup Content on Android 12
2023-10-15 08:51:14 +02:00
Outbreak2096 55551610ec Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (970 of 970 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2023-10-14 14:13:59 +00:00
Outbreak2096 66415e3f76 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (51 of 51 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/zh_Hans/
2023-10-14 14:13:59 +00:00
Outbreak2096 26fbde5608 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (2 of 2 strings)

Translation: Conversations/App Store Metadata (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-conversations/zh_Hans/
2023-10-14 14:13:59 +00:00
Outbreak2096 8edf835872 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (13 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/zh_Hans/
2023-10-14 14:13:59 +00:00
Outbreak2096 67b10504e3 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (970 of 970 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2023-10-14 14:13:59 +00:00
nautilusx b5e7a9e7f9 Translated using Weblate (German)
Currently translated at 100.0% (970 of 970 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/
2023-10-14 14:13:59 +00:00
Outbreak2096 01053d1ad2 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (2 of 2 strings)

Translation: Conversations/App Store Metadata (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-quicksy/zh_Hans/
2023-10-14 14:13:59 +00:00
Outbreak2096 8c53f19458 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (51 of 51 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/zh_Hans/
2023-10-14 14:13:59 +00:00
Outbreak2096 1543dee6e6 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (2 of 2 strings)

Translation: Conversations/App Store Metadata (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-conversations/zh_Hans/
2023-10-14 14:13:59 +00:00
Outbreak2096 73f1c93863 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (9 of 9 strings)

Translation: Conversations/Android App (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-quicksy/zh_Hans/
2023-10-14 14:13:59 +00:00
Outbreak2096 6ab58fe1f7 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (13 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/zh_Hans/
2023-10-14 14:13:59 +00:00
Outbreak2096 ffde9336c7 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (969 of 969 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2023-10-14 14:13:59 +00:00
Daniel Gultsch 49b0550d9b
Do not export EventReceiver
Conversations only listens for system events which do not need the receiver to
be exported.
2023-10-14 10:44:03 +02:00
Daniel Gultsch 73994bebd2
support themable icons on Android 13
closes #44
2023-10-13 22:15:03 +02:00
Daniel Gultsch 9a922ffe5d
quietly ignore mediated invites from blocked contacts 2023-10-13 17:36:12 +02:00
Daniel Gultsch 894ff1918c
update woodpecker syntax 2023-10-13 08:36:15 +02:00
Daniel Gultsch 5b2444ea13
implement see-other-host stream error 2023-10-13 08:29:23 +02:00
Daniel Gultsch a40d244bf5
remove unnecessary in resolver 2023-10-12 11:59:21 +02:00
Outbreak2096 7f278202c6
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (51 of 51 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/zh_Hans/
2023-10-12 04:08:47 +00:00
Outbreak2096 e5b915d8b4
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (969 of 969 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2023-10-12 04:08:46 +00:00
random_r 1392250ede
Translated using Weblate (Italian)
Currently translated at 100.0% (2 of 2 strings)

Translation: Conversations/App Store Metadata (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-quicksy/it/
2023-10-11 13:53:17 +00:00
Outbreak2096 da413b3771
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (2 of 2 strings)

Translation: Conversations/App Store Metadata (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-quicksy/zh_Hans/
2023-10-11 13:53:17 +00:00
Outbreak2096 8af1c24788
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (51 of 51 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/zh_Hans/
2023-10-11 13:53:16 +00:00
Outbreak2096 1f63ec97f6
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (2 of 2 strings)

Translation: Conversations/App Store Metadata (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-conversations/zh_Hans/
2023-10-11 13:53:16 +00:00
ghose 0c23f8e33a
Translated using Weblate (Galician)
Currently translated at 100.0% (2 of 2 strings)

Translation: Conversations/App Store Metadata (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-quicksy/gl/
2023-10-11 13:53:16 +00:00
random_r 59ea3e60d8
Translated using Weblate (Italian)
Currently translated at 100.0% (51 of 51 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/it/
2023-10-11 13:53:16 +00:00
Outbreak2096 e30e0ef583
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (13 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/zh_Hans/
2023-10-11 13:53:16 +00:00
Outbreak2096 d1b6a3ccca
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (969 of 969 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2023-10-11 13:53:16 +00:00
random_r c8b511deab
Translated using Weblate (Italian)
Currently translated at 100.0% (969 of 969 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/it/
2023-10-11 13:53:16 +00:00
butterflyoffire 5db067d09d
Translated using Weblate (French)
Currently translated at 100.0% (969 of 969 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/fr/
2023-10-11 13:53:16 +00:00
butterflyoffire 336e454f2c
Translated using Weblate (Arabic)
Currently translated at 64.6% (626 of 969 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ar/
2023-10-11 13:53:16 +00:00
Daniel Gultsch c9ffade5b0
no longer recommend ejabberd. most servers are fine 2023-10-11 14:47:05 +02:00
Daniel Gultsch 788565b299
upgrade to 'both' upon accepting recvonly content-add 2023-10-11 11:38:09 +02:00
Daniel Gultsch dbf71e5d54
handle senders modification via content-modify
Dino uses this to enable/disable video when a video content is already present
2023-10-10 18:47:29 +02:00
hamburger1024 8cb802e7c1
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (969 of 969 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2023-10-10 07:37:46 +00:00
Outbreak2096 458cd4bf8d
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (969 of 969 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2023-10-10 07:37:46 +00:00
alextecplayz c15b4f8ac8
Translated using Weblate (Romanian)
Currently translated at 29.4% (15 of 51 strings)

Translation: Conversations/App Store Metadata (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/ro/
2023-10-10 07:37:46 +00:00
Outbreak2096 3104cee602
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (13 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/zh_Hans/
2023-10-10 07:37:46 +00:00
hamburger1024 138462f13f
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (969 of 969 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2023-10-10 07:37:46 +00:00
Outbreak2096 72aca8941a
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (969 of 969 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2023-10-10 07:37:46 +00:00
alextecplayz 7d59a2da42
Translated using Weblate (Romanian)
Currently translated at 100.0% (969 of 969 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ro/
2023-10-10 07:37:46 +00:00
SomeTr 1bd6defd53
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (969 of 969 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/uk/
2023-10-10 07:37:46 +00:00
Eryk Michalak f5a57574e6
Translated using Weblate (Polish)
Currently translated at 100.0% (969 of 969 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/pl/
2023-10-10 07:37:46 +00:00
ghose a1edb1804e
Translated using Weblate (Galician)
Currently translated at 100.0% (969 of 969 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/gl/
2023-10-10 07:37:46 +00:00
gallegonovato 4121b9746d
Translated using Weblate (Spanish)
Currently translated at 100.0% (969 of 969 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/es/
2023-10-10 07:37:45 +00:00
nautilusx 1e898b022f
Translated using Weblate (German)
Currently translated at 100.0% (969 of 969 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/
2023-10-10 07:37:45 +00:00
Daniel Gultsch 39194d111c
QR code scan: take disabled accounts into consideration
when making register or add contact decision
2023-10-10 09:36:08 +02:00
Daniel Gultsch c53e035935
do not use JMI if any rtp capable device does not support it 2023-10-09 16:23:02 +02:00
Daniel Gultsch 8f014d5525
implement Private DNS (DoT)
due to limitations in the MiniDNS library this does not work when
'Validate hostname with DNSSEC' is enabled in the expert settings
2023-10-09 13:27:44 +02:00
Daniel Gultsch d3d582759f
support omemo verification in non stub transport content modifications
Dino (and this is probably correct behaviour) expects a fingerprint in the
content-add message. (and not a stub transport as indicated in the examples).

however if we start to include them we also need to encrypt and verify them
properly.
2023-10-09 13:19:26 +02:00
SomeTr 48bd845323 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (2 of 2 strings)

Translation: Conversations/App Store Metadata (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-quicksy/uk/
2023-10-09 06:31:38 +00:00
Outbreak2096 1ef40377ce Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (968 of 968 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2023-10-09 06:31:38 +00:00
Arne-Brün Vogelsang 1a7e75235a Update src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java
just a space
2023-10-08 18:25:47 +00:00
Arne-Brün Vogelsang 77631c97a7 Update src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java
Add Fairphone 4 to AEC Blacklist to fix Echo problems: https://github.com/iNPUTmice/Conversations/issues/4439
2023-10-08 18:25:20 +00:00
Daniel Gultsch 1c5a1b8c71
keep order of rtp contents 2023-10-06 12:34:41 +02:00
Daniel Gultsch 541c8ba80d
modify mime type detection for shared files
add support for audiobooks
2023-10-06 12:34:13 +02:00
kosyak 663a0feecb get rid of soprani.ca leftofvers 2023-10-06 11:14:10 +02:00
kosyak c9c98d98e6 get rid of soprani.ca bookmark 2023-10-06 11:13:24 +02:00
Daniel Gultsch 95aea4291e
don't attempt to figure out file path on Android 11+
fixes #117
2023-10-05 20:29:42 +02:00
ghose d9c9baf516
Translated using Weblate (Galician)
Currently translated at 50.0% (1 of 2 strings)

Translation: Conversations/App Store Metadata (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-quicksy/gl/
2023-10-05 14:31:26 +00:00
nautilusx 6f0ff17665
Translated using Weblate (German)
Currently translated at 100.0% (2 of 2 strings)

Translation: Conversations/App Store Metadata (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-quicksy/de/
2023-10-05 14:31:26 +00:00
licaon-kter 8a1aa41263
Translated using Weblate (Romanian)
Currently translated at 100.0% (2 of 2 strings)

Translation: Conversations/App Store Metadata (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-quicksy/ro/
2023-10-05 14:31:26 +00:00
licaon-kter 9ecfb7a765
Translated using Weblate (Romanian)
Currently translated at 100.0% (2 of 2 strings)

Translation: Conversations/App Store Metadata (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-conversations/ro/
2023-10-05 14:31:26 +00:00
Daniel Gultsch 601a8cb3bc
process content-modify for pending content-adds 2023-10-05 16:23:43 +02:00
Daniel Gultsch a8241c72df
use url safe jingle session ids
Movim does not like slashes (/) in jingle session ids.
When proposing a session called 'wBKabx1kRIfkgNxAShip/w' Movim will
accept (proceed) a session called 'wBKabx1kRIfkgNxAShip' which the initiator of course does not know about. (Conversations will get stuck at ringing/discovering devices)

This is likely because a click on 'Reply' (accept call) in Movim opens upa pop up window where both the full jid as well as the session id are transmitted as part of the URL.

(Full jids can contain more than on slash btw)
2023-10-04 13:30:53 +02:00
Daniel Gultsch 1b5d2151d0
warn early when SDP is likely to be invalid 2023-10-04 13:07:28 +02:00
Daniel Gultsch 6bc3cad7de
apply ice-options when adding content or restarting ice 2023-10-04 10:43:45 +02:00
Outbreak2096 a98738ae9d
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (968 of 968 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2023-10-04 06:42:34 +00:00
Daniel Gultsch af0f9bfd78
add fastlane description for Quicksy 2023-10-04 08:37:01 +02:00
Daniel Gultsch ef80b92c88
create fastlane folders per flavor 2023-10-04 08:30:57 +02:00
SomeTr bda4d5ecbb
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (53 of 53 strings)

Translation: Conversations/App Store Metadata
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/uk/
2023-10-03 19:25:20 +00:00
ghose a986fbd20c
Translated using Weblate (Galician)
Currently translated at 32.0% (17 of 53 strings)

Translation: Conversations/App Store Metadata
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/gl/
2023-10-03 19:25:20 +00:00
nautilusx a91b1ed4b6
Translated using Weblate (German)
Currently translated at 100.0% (53 of 53 strings)

Translation: Conversations/App Store Metadata
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/de/
2023-10-03 19:25:19 +00:00
SomeTr 112d80a392
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (52 of 52 strings)

Translation: Conversations/App Store Metadata
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/uk/
2023-10-03 12:53:12 +00:00
ghose fbd7931cc7
Translated using Weblate (Galician)
Currently translated at 30.7% (16 of 52 strings)

Translation: Conversations/App Store Metadata
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/gl/
2023-10-03 12:53:12 +00:00
nautilusx 8e7d0b9e4e
Translated using Weblate (German)
Currently translated at 100.0% (52 of 52 strings)

Translation: Conversations/App Store Metadata
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/de/
2023-10-03 12:53:12 +00:00
Daniel Gultsch 1aeae9c7f6
set local-only flag on ongoing call notification 2023-10-03 14:16:48 +02:00
random_r cfac58c8af Translated using Weblate (Italian)
Currently translated at 100.0% (51 of 51 strings)

Translation: Conversations/App Store Metadata
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/it/
2023-10-03 10:57:08 +00:00
random_r b689b46fa8 Translated using Weblate (Italian)
Currently translated at 100.0% (968 of 968 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/it/
2023-10-03 10:57:08 +00:00
Daniel Gultsch 8570c9f912
use more aggressive reconnect intervals during rtp session 2023-10-03 12:56:10 +02:00
Daniel Gultsch fd4b8ba188
bring back ICE Renomination via negotiation 2023-10-03 12:55:44 +02:00
Daniel Gultsch d235633cc7
version bump to 2.12.11 + changelog 2023-10-03 10:50:22 +02:00
Daniel Gultsch 17856a47db
hold back candidates until after content-add 2023-10-02 13:54:36 +02:00
Daniel Gultsch 7e9980d997
catch illegal state exception in TrackWrapper 2023-10-02 11:48:03 +02:00
Daniel Gultsch 09993b8319
fetch local description on its own executor 2023-10-02 11:03:08 +02:00
Daniel Gultsch 0dca7f8a5a
JMI: send 'ringing' and receipts only for contacts
fixes #110
2023-10-01 08:05:40 +02:00
Daniel Gultsch ac3ce93c56
fix stun url generation 2023-10-01 08:02:55 +02:00
Daniel Gultsch c9b80254e4
add more logging to unroutable jingle messages 2023-09-30 15:56:06 +02:00
Daniel Gultsch 860723810b
support per-app language settings 2023-09-30 10:02:22 +02:00
Daniel Gultsch 6660877bcf
jingle: trim media attribute values
Movim has trailing whitespace around some of their media attributes

<source ssrc="1892824964" xmlns="urn:xmpp:jingle:apps:rtp:ssma:0">
  <parameter name="msid" value="{a98821d7-b298-4130-925a-ff6c510734c0} {f45dfc5c-2fa7-42b4-85e5-935e786b3feb} " xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"/>
  <parameter name="cname" value="{63b1042b-5cb5-4411-b2a5-bdff92ae45be}" xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"/>
</source>

our WebRTC doesn’t like that. We trim the value even though this seems to be a Movim bug.
2023-09-30 07:55:08 +02:00
Daniel Gultsch 86f46ece83
revert back to AAC for voice messages
iOS can not do opus out of the box
2023-09-29 18:42:31 +02:00
ghose 503b04e8e0 Translated using Weblate (Galician)
Currently translated at 100.0% (968 of 968 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/gl/
2023-09-29 16:21:43 +00:00
Daniel Gultsch 05c79ff29d
version bump webrtc to m117 2023-09-29 16:19:01 +02:00
Daniel Gultsch 5371b100de
version bump to 2.12.10 + changelog 2023-09-26 12:30:28 +02:00
Daniel Gultsch 3e9c08a4aa
include date in backup file name
since `account.ceb` might have previously been created by a different application
(for example f-droid and user is on play now) or copied over via the file
manager Conversations might not have permission to write over an existing file.

we include the date so we always get a new file

fixes #105
2023-09-25 10:56:24 +02:00
Daniel Gultsch 2adff4a92c
catch rare instances of foreground service not allowed to start 2023-09-25 09:22:29 +02:00
hamburger1024 205472bcc7
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (968 of 968 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2023-09-24 12:53:09 +00:00
licaon-kter 1750811978
Translated using Weblate (Romanian)
Currently translated at 100.0% (968 of 968 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ro/
2023-09-24 12:53:09 +00:00
SomeTr 5eafc26392
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (968 of 968 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/uk/
2023-09-24 12:53:09 +00:00
Eryk Michalak bc4fb100fa
Translated using Weblate (Polish)
Currently translated at 100.0% (968 of 968 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/pl/
2023-09-24 12:53:09 +00:00
gallegonovato caec09ec7e
Translated using Weblate (Spanish)
Currently translated at 100.0% (968 of 968 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/es/
2023-09-24 12:53:09 +00:00
nautilusx 78298a2df3
Translated using Weblate (German)
Currently translated at 100.0% (968 of 968 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/
2023-09-24 12:53:09 +00:00
ghose c791d5baa7
Translated using Weblate (Galician)
Currently translated at 29.4% (15 of 51 strings)

Translation: Conversations/App Store Metadata
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/gl/
2023-09-22 13:39:33 +00:00
ghose 6d1d7bd5c4
Translated using Weblate (Galician)
Currently translated at 29.4% (15 of 51 strings)

Translation: Conversations/App Store Metadata
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/gl/
2023-09-22 13:39:33 +00:00
Sergio Varela 81eb94ee46
Translated using Weblate (Spanish)
Currently translated at 31.3% (16 of 51 strings)

Translation: Conversations/App Store Metadata
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/es/
2023-09-22 13:39:33 +00:00
Daniel Gultsch 8ba90f266e
add explicit error message for outdated backup files 2023-09-20 17:00:04 +02:00
kosyak b83d224652 bump mam limits 2023-09-18 13:29:01 +02:00
kosyak 13c22ad5db append author and day for multi select copy value 2023-09-15 17:56:07 +02:00
kosyak e3ce350077 improve message selection mode title 2023-09-15 17:38:19 +02:00
kosyak 5d30742a13 fix message archive issues 2023-09-15 16:14:32 +02:00
kosyak 6770f7de61 separate PM fixes 2023-09-15 14:11:43 +02:00
kosyak 1ca354c208 muc PM in the separate conversation 2023-09-15 11:28:50 +02:00
Daniel Gultsch b4c3334d7e
add a few TODOs wrt tie breaks 2023-09-14 14:39:32 +02:00
Daniel Gultsch fdd7f2926f
support 'ringing' jingle message 2023-09-14 11:22:19 +02:00
Millesimus 381a058db0 Update migration docs. 2023-09-11 16:24:42 +00:00
Daniel Gultsch 00ae1ca762
fix group chat shortcuts 2023-09-11 16:38:57 +02:00
Daniel Gultsch c6501a3ad4
fix UUIDv4 calculation 2023-09-08 14:19:18 +02:00
Stephen Paul Weber 11e6cb9693
Use conversation notifications
Allows for per-conversation priority and sound
2023-09-07 13:08:23 +02:00
Daniel Gultsch 04b5744a2a
update doap file 2023-09-06 20:10:51 +02:00
SomeTr 51a8877475 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (9 of 9 strings)

Translation: Conversations/Android App (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-quicksy/uk/
2023-09-06 11:48:26 +00:00
SomeTr 8138330ca2 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (51 of 51 strings)

Translation: Conversations/App Store Metadata
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/uk/
2023-09-06 11:48:26 +00:00
botorfj 585c5151a1 Translated using Weblate (Turkish)
Currently translated at 100.0% (9 of 9 strings)

Translation: Conversations/Android App (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-quicksy/tr/
2023-09-06 11:48:26 +00:00
botorfj cb9a52dfb3 Translated using Weblate (Turkish)
Currently translated at 100.0% (13 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/tr/
2023-09-06 11:48:26 +00:00
botorfj 610877cd41 Translated using Weblate (Slovak)
Currently translated at 84.6% (11 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/sk/
2023-09-06 11:48:26 +00:00
botorfj a509e6f887 Translated using Weblate (Greek)
Currently translated at 100.0% (13 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/el/
2023-09-06 11:48:26 +00:00
botorfj 033bdfa58f Translated using Weblate (Turkish)
Currently translated at 97.9% (947 of 967 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/tr/
2023-09-06 11:48:26 +00:00
Nikita Karamov 24280959e3 Translated using Weblate (Russian)
Currently translated at 99.8% (966 of 967 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ru/
2023-09-06 11:48:26 +00:00
ghose a4e5ad8684 Translated using Weblate (Galician)
Currently translated at 100.0% (967 of 967 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/gl/
2023-09-06 11:48:26 +00:00
SomeTr e3d8e54c93 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (967 of 967 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/uk/
2023-09-06 11:48:26 +00:00
gallegonovato 8de354e3cd Translated using Weblate (Spanish)
Currently translated at 100.0% (967 of 967 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/es/
2023-09-06 11:48:26 +00:00
ghose c4b92a55bb Translated using Weblate (Galician)
Currently translated at 13.7% (7 of 51 strings)

Translation: Conversations/App Store Metadata
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/gl/
2023-09-06 11:48:26 +00:00
nautilusx 296894bfa0 Translated using Weblate (German)
Currently translated at 100.0% (51 of 51 strings)

Translation: Conversations/App Store Metadata
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/de/
2023-09-06 11:48:26 +00:00
hamburger1024 f12d7ef78f Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (967 of 967 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2023-09-06 11:48:26 +00:00
licaon-kter a3e4329259 Translated using Weblate (Romanian)
Currently translated at 100.0% (967 of 967 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ro/
2023-09-06 11:48:26 +00:00
Eryk Michalak 4c679af736 Translated using Weblate (Polish)
Currently translated at 100.0% (967 of 967 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/pl/
2023-09-06 11:48:26 +00:00
random_r 03dee0d8c2 Translated using Weblate (Italian)
Currently translated at 100.0% (967 of 967 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/it/
2023-09-06 11:48:26 +00:00
ghose 4346609914 Translated using Weblate (Galician)
Currently translated at 100.0% (967 of 967 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/gl/
2023-09-06 11:48:26 +00:00
nautilusx 088f88f4b0 Translated using Weblate (German)
Currently translated at 100.0% (967 of 967 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/
2023-09-06 11:48:26 +00:00
SomeTr 74931f3af6 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (51 of 51 strings)

Translation: Conversations/App Store Metadata
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/uk/
2023-09-06 11:48:26 +00:00
SomeTr 648cf3b4a6 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (13 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/uk/
2023-09-06 11:48:26 +00:00
SomeTr 2901275d43 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (967 of 967 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/uk/
2023-09-06 11:48:26 +00:00
SomeTr de8294c2b3 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (48 of 48 strings)

Translation: Conversations/App Store Metadata
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/uk/
2023-09-06 11:48:26 +00:00
ghose b64998bc04 Translated using Weblate (Galician)
Currently translated at 6.2% (3 of 48 strings)

Translation: Conversations/App Store Metadata
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/gl/
2023-09-06 11:48:26 +00:00
Daniel Gultsch d5ae2f4b41
look at roomconfig_changesubject and roominfo_changesubject
fixes #90
2023-09-06 13:47:35 +02:00
kosyak c62cc344b5 tags for muc 2023-09-06 02:49:41 +02:00
kosyak 9aac6d99e7 fix crash 2023-09-06 02:49:04 +02:00
kosyak 71ede7c31e bump version code 2023-08-21 16:58:20 +02:00
Daniel Gultsch 1b05cbd665
use opus for voice messages on Android 10
recipients need at least Android 5 to play the message; however
Conversations has been Android 5+ for a while now
2023-08-20 07:59:45 +02:00
Daniel Gultsch 6323b14bc0
version bump to 2.12.9 + changelog 2023-08-18 18:21:56 +02:00
Daniel Gultsch 459d559a34
check column name pattern 2023-08-18 09:01:46 +02:00
Daniel Gultsch 09f6343ced
Security: Introduce backup file format v2
This switches the SQL based backup format to something JSON based.

The SQL based format has always been prone to SQL injections that, for example, could delete other messages or preexisting accounts in the app. This hasn’t been a concern this far because why would anyone purposely try to restore a faulty backup? However the argument has been made that a user can be socially engineered to restore an exploited backup file.
Before version 2.12.8 a third party app could even trigger the restore process, leaving the backup password entry dialog the only hurdle.
On top of that it has been demonstrated that a backup file can be crafted in a way that puts preexisting credentials into a 'pending' message to an attacker ultimately leading to that information being leaked.

While destorying information has always been deemed an acceptable risk, leaking information is one step too far.

Starting with Conversations 2.12.9 Conversations will no longer be able to read v1 backup files. This means if you are restoring on a new device and you have a v1 backup file you must first install Conversations <= 2.12.8, restore the backup, and then upgrade to Conversations >= 2.12.9.

ceb2txt¹ has support for v2 backup files. Conceivably ceb2txt could be extended to convert between v1 and v2 file formats. (ceb2txt already recreates the database from v1 files; It is relatively straight forward to create v2 files from that database. Pull requests welcome.)

¹: https://github.com/iNPUTmice/ceb2txt/
2023-08-17 12:07:51 +02:00
Daniel Gultsch 0677ddc59b
version bump to 2.12.8 + changelog 2023-08-16 14:10:48 +02:00
Daniel Gultsch 9a662a7e55
disable opeing ceb files from file manager 2023-08-16 14:00:42 +02:00
Daniel Gultsch 0a956bcf9b
version bump to 2.12.7 + changelog 2023-08-16 14:00:42 +02:00
kosyak bd6b316ab6 long tap on conversation 2023-08-15 01:30:56 +02:00
kosyak 8ea335501b logs sender 2023-08-15 01:23:31 +02:00
kosyak 5ce1e80e73 fix service discovery has identity 2023-08-15 01:23:18 +02:00
kosyak fee1a67332 tags editor 2023-08-15 01:22:56 +02:00
kosyak dca3e80bad modify entities for proper tags navigation 2023-08-15 01:22:40 +02:00
kosyak b26adc84b0 don't hide indicator on merged messages 2023-08-15 01:22:10 +02:00
kosyak a2a943c36a tags navigation and self contact 2023-08-15 01:21:39 +02:00
kosyak 18c41eb05e dialpad and gateway interaction 2023-08-15 01:21:20 +02:00
kosyak 8ff365613a disable omemo by default 2023-08-15 01:20:53 +02:00
kosyak a1e354fa72 fix reply id 2023-08-15 01:20:05 +02:00
SomeTr 76655f05b3
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (48 of 48 strings)

Translation: Conversations/App Store Metadata
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/uk/
2023-08-13 19:53:04 +00:00
SomeTr dc83eb9f42
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (966 of 966 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/uk/
2023-08-13 19:53:04 +00:00
SomeTr 6f4ad677d7
Translated using Weblate (Ukrainian)
Currently translated at 56.2% (27 of 48 strings)

Translation: Conversations/App Store Metadata
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/uk/
2023-08-12 05:50:46 +00:00
0eoc 510f9d8019
Translated using Weblate (Russian)
Currently translated at 99.5% (962 of 966 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ru/
2023-08-12 05:50:45 +00:00
hamburger1024 892b53090f
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (966 of 966 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2023-08-09 23:53:04 +00:00
SomeTr 0a973b6678 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (966 of 966 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/uk/
2023-08-08 08:40:21 +00:00
Daniel Gultsch b4a07d0093
remove channel discovery from Google Play build flavor 2023-08-08 10:39:36 +02:00
Daniel Gultsch 7a9f18f223
play tones as music when silent only on android 12+ 2023-08-08 09:04:26 +02:00
SomeTr febaea0131
Translated using Weblate (Ukrainian)
Currently translated at 4.1% (2 of 48 strings)

Translation: Conversations/App Store Metadata
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/uk/
2023-08-05 12:53:03 +00:00
SomeTr b265a430d2
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (9 of 9 strings)

Translation: Conversations/Android App (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-quicksy/uk/
2023-08-04 05:53:03 +00:00
SomeTr 826ff0cd28
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (13 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/uk/
2023-08-04 05:53:03 +00:00
SomeTr 7c710e4c5d
Translated using Weblate (Ukrainian)
Currently translated at 99.7% (964 of 966 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/uk/
2023-08-04 05:53:03 +00:00
0eoc 814216e42f
Translated using Weblate (Russian)
Currently translated at 100.0% (9 of 9 strings)

Translation: Conversations/Android App (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-quicksy/ru/
2023-07-27 12:53:01 +00:00
random_r adf96f5f6a
Translated using Weblate (Italian)
Currently translated at 100.0% (966 of 966 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/it/
2023-07-25 09:38:07 +00:00
nautilusx b4052cb5b6
Translated using Weblate (German)
Currently translated at 100.0% (48 of 48 strings)

Translation: Conversations/App Store Metadata
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/de/
2023-07-14 19:38:05 +00:00
ghose fb8b526108
Translated using Weblate (Galician)
Currently translated at 4.2% (2 of 47 strings)

Translation: Conversations/App Store Metadata
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/gl/
2023-07-13 12:38:05 +00:00
Daniel Gultsch d1ba36cfdf
version bump to 2.12.6 + changelog 2023-07-05 12:25:41 +02:00
Daniel Gultsch 836f048bc6
add CI via woodpecker
based on docker image provided by Gadgetbridge

focus for now is getting something build. we can optimize this later and
provide our own image
2023-07-05 10:52:46 +02:00
ghose 5db3ca06b6
Translated using Weblate (Galician)
Currently translated at 4.2% (2 of 47 strings)

Translation: Conversations/App Store Metadata
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/gl/
2023-07-04 18:38:04 +00:00
nautilusx e9f6280718
Translated using Weblate (German)
Currently translated at 100.0% (47 of 47 strings)

Translation: Conversations/App Store Metadata
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/de/
2023-07-03 00:45:54 +00:00
Karma78 f7d5635c32
Translated using Weblate (Vietnamese)
Currently translated at 100.0% (13 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/vi/
2023-07-02 16:38:05 +00:00
Karma78 8d89c33da4
Translated using Weblate (Vietnamese)
Currently translated at 99.8% (965 of 966 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/vi/
2023-07-02 16:38:05 +00:00
kosyak 847ef66216 fix saving path 2023-06-29 11:11:54 +03:00
Daniel Gultsch 12b34426fc
add ј to list 2023-06-29 09:56:49 +02:00
Daniel Gultsch d588e942e6
q is not cyrillic but ԛ and х are 2023-06-29 09:45:50 +02:00
kosyak 21e6f4f023 separate app id from upstream 2023-06-29 10:38:53 +03:00
kosyak 983ef0d369 fix messages table creation 2023-06-29 00:23:53 +03:00
Daniel Gultsch d9fb88ceda
version bump to 2.12.5 + changelog 2023-06-27 17:27:36 +02:00
tuongdai252 0de4939f7f Translated using Weblate (Vietnamese)
Currently translated at 99.8% (965 of 966 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/vi/
2023-06-27 15:03:49 +00:00
Stephen Paul Weber 4c38c480fa
Use libidn for stringprep
Which actually validates according to spec instead of just being lazy.
2023-06-27 16:31:01 +02:00
Daniel Gultsch e3a121121b
UP: add custom extensions for app<->distributor interaction
On registration the app can pass in a 'Messenger' to get a direct response
instead of having to somehow wait for the broadcast receiver to fire.

The app name can be passed as a pending intent which allows the distributor
to validate the sender.
2023-06-26 16:09:01 +02:00
Daniel Gultsch ca1ee4a565
process stream features after success when we inlined bind but not sm 2023-06-25 22:15:49 +02:00
VTCuong 3e20358701 Translated using Weblate (Vietnamese)
Currently translated at 95.8% (926 of 966 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/vi/
2023-06-23 16:10:18 +00:00
Zash ec8a574bdc Translated using Weblate (Swedish)
Currently translated at 98.9% (956 of 966 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/sv/
2023-06-23 16:10:18 +00:00
Jasper 3af2d1f79a Translated using Weblate (French)
Currently translated at 100.0% (13 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/fr/
2023-06-23 16:10:18 +00:00
Jasper ea3087ee51 Translated using Weblate (French)
Currently translated at 99.8% (965 of 966 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/fr/
2023-06-23 16:10:18 +00:00
TheCanine 00d7cbf066 Translated using Weblate (Czech)
Currently translated at 98.3% (950 of 966 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/cs/
2023-06-23 16:10:18 +00:00
Daniel Gultsch 3b26948a9d
replace libraries hosted on jcenter 2023-06-23 18:00:58 +02:00
kosyak 8f0fb0f2a3 prevent vertical scrolling during swipe 2023-06-22 11:14:56 +03:00
kosyak 5bb72ec049 improve replies 2023-06-22 03:38:26 +03:00
kosyak b1c3001a97 update corners and stack bubbles from last message 2023-06-11 22:50:59 +03:00
kosyak f9bd73a066 fix layout 2023-06-11 22:50:45 +03:00
Daniel Gultsch 6289e048b3
catch runtime exception when trying to stop tone manager 2023-06-05 10:05:59 +02:00
kosyak 7c5826c945 embedded photo editor 2023-05-29 00:29:55 +03:00
kosyak e1161bcb22 implement multi selection for messages 2023-05-29 00:29:02 +03:00
Adeptus2 963f8ccee4
Translated using Weblate (Chinese (Traditional))
Currently translated at 10.8% (5 of 46 strings)

Translation: Conversations/App Store Metadata
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/zh_Hant/
2023-05-28 08:37:36 +00:00
Adeptus2 1258519cfa
Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (966 of 966 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hant/
2023-05-28 08:37:36 +00:00
ghose c5824e43ac
Translated using Weblate (Galician)
Currently translated at 100.0% (966 of 966 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/gl/
2023-05-28 08:37:36 +00:00
gallegonovato 52246b0f35
Translated using Weblate (Spanish)
Currently translated at 100.0% (966 of 966 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/es/
2023-05-20 12:37:35 +00:00
random_r fae6a12ca1
Translated using Weblate (Italian)
Currently translated at 100.0% (966 of 966 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/it/
2023-05-17 11:37:35 +00:00
hamburger1024 c242885ecd
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (966 of 966 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2023-05-15 02:37:35 +00:00
licaon-kter 1eaff064b7
Translated using Weblate (Romanian)
Currently translated at 100.0% (966 of 966 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ro/
2023-05-15 02:37:35 +00:00
Eryk Michalak b84b31b715
Translated using Weblate (Polish)
Currently translated at 100.0% (966 of 966 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/pl/
2023-05-15 02:37:35 +00:00
ghose 1dc7093307
Translated using Weblate (Galician)
Currently translated at 100.0% (966 of 966 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/gl/
2023-05-15 02:37:35 +00:00
nautilusx be70433487
Translated using Weblate (German)
Currently translated at 100.0% (966 of 966 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/
2023-05-15 02:37:34 +00:00
Daniel Holmgaard afccd6b609
Translated using Weblate (Danish)
Currently translated at 2.1% (1 of 46 strings)

Translation: Conversations/App Store Metadata
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/da/
2023-05-12 20:37:35 +00:00
Daniel Holmgaard eb7ca01ebe
Translated using Weblate (Danish)
Currently translated at 100.0% (9 of 9 strings)

Translation: Conversations/Android App (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-quicksy/da/
2023-05-12 20:37:35 +00:00
Daniel Holmgaard f2cfe0fed4
Translated using Weblate (Danish)
Currently translated at 100.0% (13 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/da/
2023-05-12 20:37:35 +00:00
Daniel Holmgaard 0acfa9062f
Translated using Weblate (Danish)
Currently translated at 100.0% (965 of 965 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/da/
2023-05-12 20:37:35 +00:00
Daniel Gultsch 1188a89f2a
sanity check push server url 2023-05-07 14:43:13 +02:00
Daniel Gultsch f7f34c6bdd
fix regressions after target sdk 33 update 2023-05-07 10:13:14 +02:00
Daniel Gultsch 291091dbe4
revert target sdk bump 2023-05-03 16:10:44 +02:00
Daniel Gultsch cc16811444
rename bookmarks tab to 'group chats'
fixes #38

exposing bookmarks like this was a mistake that Conversations 3 will not repeat

in the meantime we rename this to group chats which might be more broadly understood
2023-05-03 08:00:40 +02:00
Daniel Gultsch aa8e0b3c4d
remember bound stream features. fixes #45 2023-05-02 15:35:27 +02:00
Adeptus2 d4c323d7e6 Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (965 of 965 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hant/
2023-04-28 07:37:51 +00:00
Daniel Gultsch 5228cdab15
bump libraries 2023-04-19 09:00:43 +02:00
Daniel Gultsch acbf1c0ecf
version bump to 2.12.3 + changelog 2023-04-12 13:28:18 +02:00
Daniel Gultsch bc00f6f629
bump target sdk to 33 2023-04-12 13:28:16 +02:00
Adeptus2 772b2c4294
Translated using Weblate (Chinese (Traditional))
Currently translated at 2.1% (1 of 46 strings)

Translation: Conversations/App Store Metadata
Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/zh_Hant/
2023-04-12 09:37:31 +00:00
Adeptus2 1a46378264
Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (9 of 9 strings)

Translation: Conversations/Android App (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-quicksy/zh_Hant/
2023-04-12 09:37:31 +00:00
Adeptus2 ad46658dd9
Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (13 of 13 strings)

Translation: Conversations/Android App (Conversations)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/zh_Hant/
2023-04-12 09:37:31 +00:00
Adeptus2 b83093866f
Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (965 of 965 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hant/
2023-04-12 09:37:31 +00:00
licaon-kter e60e557733
Translated using Weblate (Romanian)
Currently translated at 100.0% (965 of 965 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ro/
2023-04-12 09:37:31 +00:00
Eryk Michalak 4541dbf72f
Translated using Weblate (Polish)
Currently translated at 100.0% (965 of 965 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/pl/
2023-04-12 09:37:31 +00:00
random_r 698801fa39
Translated using Weblate (Italian)
Currently translated at 100.0% (965 of 965 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/it/
2023-04-12 09:37:30 +00:00
ghose bde5fab5ae
Translated using Weblate (Galician)
Currently translated at 100.0% (965 of 965 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/gl/
2023-04-12 09:37:30 +00:00
gallegonovato 015514dbba
Translated using Weblate (Spanish)
Currently translated at 100.0% (965 of 965 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/es/
2023-04-12 09:37:30 +00:00
Zimbelstern 6054c5178a
Translated using Weblate (German)
Currently translated at 100.0% (965 of 965 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/
2023-04-12 09:37:30 +00:00
nautilusx c015b8cbc3
Translated using Weblate (German)
Currently translated at 100.0% (965 of 965 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/
2023-04-12 09:37:30 +00:00
Adeptus2 dd19f920af
Translated using Weblate (Chinese (Traditional))
Currently translated at 93.8% (905 of 964 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hant/
2023-04-10 17:37:31 +00:00
Daniel Gultsch 1fff1a0649
add ability to remove account from server 2023-04-08 09:31:17 +02:00
Eryk Michalak 253cc9392e
Translated using Weblate (Polish)
Currently translated at 100.0% (964 of 964 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/pl/
2023-04-07 12:37:30 +00:00
hamburger1024 8cc17260f3
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (964 of 964 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2023-04-05 20:37:30 +00:00
licaon-kter fa22d0adb1
Translated using Weblate (Romanian)
Currently translated at 100.0% (964 of 964 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ro/
2023-04-05 20:37:30 +00:00
random_r 21bd740537
Translated using Weblate (Italian)
Currently translated at 100.0% (964 of 964 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/it/
2023-04-05 20:37:30 +00:00
ghose 1259cba90b
Translated using Weblate (Galician)
Currently translated at 100.0% (964 of 964 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/gl/
2023-04-05 20:37:30 +00:00
gallegonovato 6f5f7ba34f
Translated using Weblate (Spanish)
Currently translated at 100.0% (964 of 964 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/es/
2023-04-05 20:37:30 +00:00
nautilusx 6932247e1a
Translated using Weblate (German)
Currently translated at 100.0% (964 of 964 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/
2023-04-05 20:37:29 +00:00
hamburger1024 38debe1687
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (963 of 963 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2023-04-04 12:37:31 +00:00
licaon-kter 1430a3a844
Translated using Weblate (Romanian)
Currently translated at 100.0% (963 of 963 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ro/
2023-04-04 12:37:31 +00:00
ghose 9dadcdd1d5
Translated using Weblate (Galician)
Currently translated at 100.0% (963 of 963 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/gl/
2023-04-04 12:37:31 +00:00
nautilusx d577f2de90
Translated using Weblate (German)
Currently translated at 100.0% (963 of 963 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/
2023-04-04 12:37:30 +00:00
Daniel Gultsch 9187739450
play dial tones on STREAM_MUSIC when phone is silent
when the phone is silent only the first ~three tones are played when
attempting to play out the tone over STREAM_VOICE_CALL

it’s unclear exactly why this is the case (in the past we went back and forth
between STREAM_VOICE_CALL and STREAM_MUSIC) exactly to fix issues around silent
mode.
Apparently we failed to test this past three sounds.

This commit changes the stream back to music - but not generally as this was in
the past - but only for when the phone is on silent
2023-04-03 16:00:06 +02:00
Daniel Gultsch d38c264e7d
lock call activity in portrait mode during audio call 2023-04-03 13:38:22 +02:00
Daniel Gultsch 9456ba6f56
put timestamp in all call logs 2023-04-03 13:15:57 +02:00
Daniel Gultsch 7f5bce4b96
remove extra translations 2023-04-03 11:19:52 +02:00
tygyh e7f89fc232
Translated using Weblate (Swedish)
Currently translated at 100.0% (962 of 962 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/sv/
2023-04-03 08:39:56 +00:00
gallegonovato 58e0e3244f
Translated using Weblate (Spanish)
Currently translated at 100.0% (9 of 9 strings)

Translation: Conversations/Android App (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-quicksy/es/
2023-04-03 08:39:56 +00:00
random_r 1f9861eb73
Translated using Weblate (Italian)
Currently translated at 100.0% (962 of 962 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/it/
2023-04-03 08:39:56 +00:00
gallegonovato 348cbfd33d
Translated using Weblate (Spanish)
Currently translated at 100.0% (9 of 9 strings)

Translation: Conversations/Android App (Quicksy)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-quicksy/es/
2023-04-03 08:39:56 +00:00
ewm 266b90543f
Translated using Weblate (Polish)
Currently translated at 100.0% (962 of 962 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/pl/
2023-04-03 08:39:56 +00:00
hamburger1024 2ce24d5b8a
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (962 of 962 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/
2023-04-03 08:39:56 +00:00
licaon-kter c0c7cf8f84
Translated using Weblate (Romanian)
Currently translated at 100.0% (962 of 962 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ro/
2023-04-03 08:39:56 +00:00
ghose 54ae06eed6
Translated using Weblate (Galician)
Currently translated at 100.0% (962 of 962 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/gl/
2023-04-03 08:39:56 +00:00
gallegonovato 73e0a0af17
Translated using Weblate (Spanish)
Currently translated at 100.0% (962 of 962 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/es/
2023-04-03 08:39:56 +00:00
nautilusx 3c106ed8e7
Translated using Weblate (German)
Currently translated at 100.0% (962 of 962 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/
2023-04-03 08:39:56 +00:00
tonya15115 587cf22e5e
Translated using Weblate (Russian)
Currently translated at 96.7% (931 of 962 strings)

Translation: Conversations/Android App (shared)
Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ru/
2023-04-03 08:39:56 +00:00
Daniel Gultsch 2cb21bcb87
use static (not translated) text for Privacy policy and TOS
For a long time Quicksy had a privacy policy written by myself that explains
in plain English what data we store and how we use it.
https://quicksy.im/#privacy

Google doesn’t like that and prefers that we use some bullshit template that
is extremely vague, doesn’t explain anything and gives us permission to do
basically everything. (At least I think so. I don’t understand the text I
copy pasted)

Apparantly the text in the app is important as well (BARD didn’t explain
that very well when it reviewed our app) therfor we need a static text (not
allow translations)

Furthermore the data safety section on Google Play now claims we store the
users address book even though we don’t actually. But who cares; nobody reads
this and we just do this to make the machine happy. Cool!
2023-04-03 10:29:55 +02:00
Daniel Gultsch fbf8b09fe6
point to a privacy policy that doesn’t use anchors 2023-03-06 08:06:46 +01:00
Daniel Gultsch 9715271e92
expand emoji range to cover e14 2023-03-05 20:50:06 +01:00
Daniel Gultsch 45664766e6
bump agp 2023-03-05 20:49:10 +01:00
2951 changed files with 138698 additions and 73285 deletions

7
.woodpecker.yml Normal file
View file

@ -0,0 +1,7 @@
steps:
build:
image: codeberg.org/freeyourgadget/android-fdroid-tools:latest
commands:
- ./gradlew clean
- ./gradlew assembleConversationsFreeDebug
- ./gradlew assembleQuicksyFreeDebug

View file

@ -1,5 +1,60 @@
# Changelog
### Version 2.13.0
* Easier access to 'Show QR code'
* Support PEP Native Bookmarks
* Add support for SDP Offer / Answer Model (Used by SIP gateways)
* Raise target API to Android 14
### Version 2.12.12
* Support Private DNS (DNS over TLS)
* Support themed launcher icon
* Fix rare permission issue when sharing files on Android 11+
### Version 2.12.11
* Bump libwebrtc dependency to M117 and bump libvpx
* Go back to AAC for voice messages
* Support per app language settings
### Version 2.12.10
* support per conversation notification settings
* use opus for voice messages on Android 10
### Version 2.12.9
* Introduce new backup file format
### Version 2.12.8
* Disable opening backup files (.ceb) from file manager
### Version 2.12.7
* Remove channel discovery feature from Google Play version
### Version 2.12.6
* Fix 'q' falsely being recognized as cyrillic
### Version 2.12.5
* Bump Target SDK to 33 again
* Fix issues on servers supporting SASL2 w/o inline Stream Management
### Version 2.12.4
* Revert Target SDK bump (back to 32) to fix various issues on Android 13
### Version 2.12.3
* Improve support for new emojis
* Add ability to remove account from server
* Show timestamp for calls
### Version 2.12.2
* Increase corner radius on profile pictures

235
README.md
View file

@ -1,17 +1,6 @@
<h1 align="center">Conversations</h1>
<h1 align="center">Conversations Classic</h1>
<p align="center">Conversations: the very last word in instant messaging</p>
<p align="center">
<a href="https://play.google.com/store/apps/details?id=eu.siacs.conversations&amp;referrer=utm_source%3Dcodeberg">
<img src="https://conversations.im/images/get-it-on-play.png" alt="Get it on Google Play" height="80">
</a>
<a href="https://f-droid.org/packages/eu.siacs.conversations">
<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" alt="Get it on F-Droid" height="80">
</a>
</p>
![screenshots](https://codeberg.org/iNPUTmice/Conversations/raw/branch/master/screenshots.png)
<p align="center">Conversations Classic: the very last word in instant messaging</p>
## Design principles
@ -22,9 +11,9 @@
## Features
* End-to-end encryption with [OMEMO](http://conversations.im/omemo/) or [OpenPGP](http://openpgp.org/about/)
* End-to-end encryption with [OMEMO](https://en.wikipedia.org/wiki/OMEMO) or [OpenPGP](https://openpgp.org/about/)
* Send and receive images as well as other kind of files
* [Encrypted audio and video calls (DTLS-SRTP)](https://help.conversations.im)
* Encrypted audio and video calls (DTLS-SRTP) with DTMF dialer support
* Share your location
* Send voice messages
* Indication when your contact has read your message
@ -39,14 +28,15 @@
### XMPP Features
Conversations works with every XMPP server out there. However XMPP is an
Conversations Classic works with every XMPP server out there. However XMPP is an
extensible protocol. These extensions are standardized as well in so called
XEP's. Conversations supports a couple of these to make the overall user
XEP's. Conversations Classic supports a couple of these to make the overall user
experience better. There is a chance that your current XMPP server does not
support these extensions; therefore to get the most out of Conversations you
support these extensions; therefore to get the most out of Conversations Classic you
should consider either switching to an XMPP server that does or — even better —
run your own XMPP server for you and your friends. These XEP's are:
* [XEP-0050: Ad-Hoc Commands](http://xmpp.org/extensions/xep-0050.html) lets to interact with gateways
* [XEP-0065: SOCKS5 Bytestreams](http://xmpp.org/extensions/xep-0065.html) will be used to transfer
files if both parties are behind a firewall (NAT).
* [XEP-0163: Personal Eventing Protocol](http://xmpp.org/extensions/xep-0163.html) for avatars and OMEMO.
@ -55,18 +45,21 @@ run your own XMPP server for you and your friends. These XEP's are:
* [XEP-0198: Stream Management](http://xmpp.org/extensions/xep-0198.html) allows XMPP to survive small network outages and
changes of the underlying TCP connection.
* [XEP-0215: External Service Discovery](https://xmpp.org/extensions/xep-0215.html) will be used to discover STUN and TURN servers which facilitate P2P A/V calls.
* [XEP-0237: Roster Versioning](http://xmpp.org/extensions/xep-0237.html) mainly to save bandwidth on poor mobile connections
* [XEP-0280: Message Carbons](http://xmpp.org/extensions/xep-0280.html) which automatically syncs the messages you send to
your desktop client and thus allows you to switch seamlessly from your mobile
client to your desktop client and back within one conversation.
* [XEP-0237: Roster Versioning](http://xmpp.org/extensions/xep-0237.html) mainly to save bandwidth on poor mobile connections
* [XEP-0308: Last Message Correction](https://xmpp.org/extensions/xep-0308.html) allows you to edit last message as well as retract it
* [XEP-0313: Message Archive Management](http://xmpp.org/extensions/xep-0313.html) synchronize message history with the
server. Catch up with messages that were sent while Conversations was
server. Catch up with messages that were sent while Conversations Classic was
offline.
* [XEP-0352: Client State Indication](http://xmpp.org/extensions/xep-0352.html) lets the server know whether or not
Conversations is in the background. Allows the server to save bandwidth by
withholding unimportant packages.
* [XEP-0363: HTTP File Upload](http://xmpp.org/extensions/xep-0363.html) allows you to share files in conferences
and with offline contacts.
* [XEP-0461: Message Replies](https://xmpp.org/extensions/xep-0461.html) provides support of native replies, which also works in many transports (gateways) as well
## FAQ
@ -74,44 +67,30 @@ run your own XMPP server for you and your friends. These XEP's are:
#### How do I install Conversations?
Conversations is entirely open source and licensed under GPLv3. So if you are a
Conversations Classic is entirely open source and licensed under GPLv3. So if you are a
software developer you can check out the sources from GitHub and use Gradle to
build your apk file.
The more convenient way — which not only gives you automatic updates but also
supports the further development of Conversations — is to buy the App in the
Google [Play Store](https://play.google.com/store/apps/details?id=eu.siacs.conversations&referrer=utm_source%3Dcodeberg).
Buying the App from the Play Store will also give you access to our [beta test](#beta).
#### I don't have a Google Account but I would still like to make a donation
Im listing several options to support me financially on [my website](https://gultsch.de/donate.html). Among other things [Liberapay](https://liberapay.com/iNPUTmice/donate), [GitHub Sponsors](https://github.com/sponsors/inputmice) and bank transfer.
#### How do I create an account?
XMPP, like email, is a federated protocol, which means that there is not one company you can create an *official XMPP account* with. Instead there are hundreds, or even thousands, of providers out there. One of those providers is our very own [conversations.im](https://account.conversations.im). If you dont like to use *conversations.im* use a web search engine of your choice to find another provider. Or maybe your university has one. Or you can run your own. Or ask a friend to run one. Once you've found one, you can use Conversations to create an account. Just select *register new account* on server within the create account dialog.
XMPP, like email, is a federated protocol, which means that there is not one company you can create an *official XMPP account* with. Instead there are hundreds, or even thousands, of providers out there. One of those providers is [conversations.im](https://account.conversations.im). If you dont like to use *conversations.im* use a web search engine of your choice to find another provider. Or maybe your university has one. Or you can run your own. Or ask a friend to run one. Once you've found one, you can use Conversations to create an account. Just select *register new account* on server within the create account dialog.
##### Domain hosting
Using your own domain not only gives you a more recognizable Jabber ID, it also gives you the flexibility to migrate your account between different XMPP providers. This is a good compromise between the responsibilities of having to operate your own server and the downsides of being dependent on a single provider.
Learn more about [conversations.im Jabber/XMPP domain hosting](https://account.conversations.im/domain/).
##### Running your own
If you already have a server somewhere and are willing and able to put the necessary work in you can run your own XMPP server.
As of 2019 we recommend you use [ejabberd](https://ejabberd.im). The default configuration file already enables everything you need to pass the [Conversations Compliance Suite](https://compliance.conversations.im). Make sure your Linux distribution ships a fairly recent version.
As of 2023 XMPP has reached a level of maturity where all major XMPP servers ([ejabberd](https://ejabberd.im), [Prosody](https://prosody.im), [Openfire](https://www.igniterealtime.org/projects/openfire/), [Tigase](https://tigase.net/xmpp-server/)) should work well with Conversations.
With a little bit of effort [Prosody](https://prosody.im) can be configured to support all necessary extensions as well. However you will have to rely on so called [Community Modules](https://modules.prosody.im/) of varying quality. Prosody can be interesting to people who like to modify their server and create / prototype own modules.
Performance wise - for small deployments - both ejabberd and Prosody should be fine.
Interoperability with Prosody and ejabberd is tested fairly regularly just because of their market share but we occasionally test with other servers too and fix issues as soon as we are being made aware of them.
#### Where can I set up a custom hostname / port
Conversations will automatically look up the SRV records for your domain name
Conversations Classic will automatically look up the SRV records for your domain name
which can point to any hostname port combination. If your server doesnt provide
those please contact your admin and have them read
[this](http://prosody.im/doc/dns#srv_records). If your server operator is unwilling
to fix this you can enable advanced server settings in the expert settings of
Conversations.
Conversations Classic.
#### I get 'Incompatible Server'
@ -122,15 +101,15 @@ If you are a server administrator you should make sure that your server provides
either STARTTLS or [XEP-0368: SRV records for XMPP over TLS](https://xmpp.org/extensions/xep-0368.html).
On rare occasions this error message might also be caused by a server not providing
a login (SASL) mechanism that Conversations is able to handle. Conversations supports
a login (SASL) mechanism that Conversations Classic is able to handle. Conversations Classic supports
SCRAM-SHA1, PLAIN, EXTERNAL (client certs) and DIGEST-MD5.
#### I get 'Bind failure'. What does that mean?
Some Bind failures are transient and resolve themselves after a reconnect.
When trying to connect to OpenFire the bind failure can be a permanent problem when the domain part of the Jabber ID entered in Conversations doesnt match the domain the OpenFire server feels responsible for. For example OpenFire is configured to use the domain `a.tld` but the Jabber ID entered is `user@b.tld` where `b.tld` also points to the same host. During bind OpenFire tries to reassign the Jabber to `user@a.tld`. Conversations doesnt like that.
This can be fixed by creating a new account in Conversations that uses the Jabber ID `user@a.tld`.
When trying to connect to OpenFire the bind failure can be a permanent problem when the domain part of the Jabber ID entered in Conversations Classic doesnt match the domain the OpenFire server feels responsible for. For example OpenFire is configured to use the domain `a.tld` but the Jabber ID entered is `user@b.tld` where `b.tld` also points to the same host. During bind OpenFire tries to reassign the Jabber to `user@a.tld`. Conversations Classic doesnt like that.
This can be fixed by creating a new account in Conversations Classic that uses the Jabber ID `user@a.tld`.
Note: This is kind of a weird quirk in OpenFire. Most other servers would just throw a 'Server not responsible for domain' error instead of attempting to reassign the Jabber ID.
@ -140,16 +119,10 @@ Maybe you attempted to use the Jabber ID `test@b.tld` because `a.tld` doesnt
In most cases this error is caused by ejabberd advertising support for TLSv1.3 but not properly supporting it. This can happen if the OpenSSL version on the server already supports TLSv1.3 but the fast\_tls wrapper library used by ejabberd not (properly) support it. Upgrading fast\_tls and ejabberd or - theoretically - downgrading OpenSSL should fix the issue. A work around is to explicitly disable TLSv1.3 support in the ejabberd configuration. More information can be found on [this issue on the ejabberd issue tracker](https://github.com/processone/ejabberd/issues/2614).
**The battery consumption and the entire behavior of Conversations Classic will remain the same (as good or as bad as it was before). Why is Google doing this to you? We have no idea.**
#### Im getting this annoying permanent notification
Starting with Conversations 2.3.6 Conversations releases distributed over the Google Play Store will display a permanent notification if you are running it on Android 8 and above. This is a rule that it is essentially enforced by the Google Play Store. (You wont have the problem of a *forced* foreground notification if you are getting your app from F-Droid.)
However you can disable the notification via settings of the operating system. (Not settings in Conversations.)
**The battery consumption and the entire behavior of Conversations will remain the same (as good or as bad as it was before). Why is Google doing this to you? We have no idea.**
##### Android &lt;= 7.1 or Conversations from F-Droid (all Android versions)
The foreground notification is still controlled over the expert settings within Conversations as it always has been. Whether or not you need to enable it depends on how aggressive the non-standard 'power saving' features are that your phone vendor has built into the operating system.
##### Android &lt;= 7.1 or Conversations Classic from F-Droid (all Android versions)
The foreground notification is still controlled over the expert settings within Conversations Classic as it always has been. Whether or not you need to enable it depends on how aggressive the non-standard 'power saving' features are that your phone vendor has built into the operating system.
##### Android 8.x
Long press the permanent notification and disable that particular type of notification by moving the slider to the left. This will make the notification disappear but create another notification (this time created by the operating system itself.) that will complain about Conversations (and other apps) using battery. Starting with Android 8.1 you can disable that notification again with the same method described above.
@ -157,43 +130,24 @@ Long press the permanent notification and disable that particular type of notifi
##### Android 9.0+
Long press the permanent notification and press the info `(i)` button to get into the App info screen. In that screen touch the 'Notification' entry. In the next screen remove the checkbox for the 'Foreground service' entry.
#### How do XEP-0357: Push Notifications work?
You need to be running the Play Store version of Conversations and your server needs to support push notifications.¹ Because *Googles Firebase Cloud Messaging (FCM)* are tied with an API key to a specific app your server can not initiate the push message directly. Instead your server will send the push notification to the [Conversations App server](https://github.com/iNPUTmice/p2) (operated by us) which then acts as a proxy and initiates the push message for you. The push message sent from our App server through FCM doesnt contain any personal information. It is just an empty message which will wake up your device and tell Conversations to reconnect to your server. The information sent from your server to our App server depends on the configuration of your server but can be limited to your account name. (In any case the Conversations App server won't redirect any information through FCM even if your server sends this information.)
In summary Google will never get hold of any personal information besides that *something* happened. (Which doesnt even have to be a message but can be some automated event as well.) We - as the operator of the App server - will just get hold of your account name (without being able to tie this to your specific device).
If you dont want this simply pick a server which does not offer Push Notifications or build Conversations yourself without support for push notifications. (This is available via a gradle build flavor.) Non-play store source of Conversations like the Amazon App store will also offer a version without push notifications. Conversations will just work as before and maintain its own TCP connection in the background.
You can find a detailed description of how your server, the app server and FCM are interacting with each other in the [README](https://github.com/iNPUTmice/p2/blob/master/README.md) of the Conversations App Server.
¹ If you use the Play Store version you do **not** need to run your own app server. Your server only needs to support the server side of [XEP-0357: Push Notifications](http://xmpp.org/extensions/xep-0357.html) and [XEP-0198: Stream Management](https://xmpp.org/extensions/xep-0198.html). The prosody server modules are called *mod_cloud_notify* and *mod_smacks*. The ejabberd server modules are called *mod_push* and *mod_stream_mgmt*.
#### But why do I need a permanent notification if I use Google Push?
FCM (Google Push) allows an app to wake up from *Doze* which is (as the name suggests) a hibernation feature of the Android operating system that cuts the network connection and also reduces the number of times the app is allowed to wake up (to ping the server for example). The app can ask to be excluded from doze. Non push variants of the app (from F-Droid or if the server doesnt support it) will do this on first start up. So if you get exemption from *Doze*, or if you get regular push events sent to you, Doze should not pose a threat to Conversatons working properly. But even with *Doze* the app is still open in the background (kept in memory); it is just limited in the actions it can do. Conversations needs to stay in memory to hold certain session state (online status of contacts, join status of group chats, …). However with Android 8 Google changed all of this again and now an App that wants to stay in memory needs to have a foreground service which is visible to the user via the annoying notification. But why does Conversations need to hold that state? XMPP is a statefull protocol that has a lot of per-session information; packets need to be counted, presence information needs to be held, some features like Message Carbons get activated once per session, MAM catch-up happens once, service discovery happens only once; the list goes on. When Conversations was created in early 2014 none of this was a problem because apps were just allowed to stay in memory. Basically every XMPP client out there holds that information in memory because it would be a lot more complicated trying to persist it to disk. An entire rewrite of Conversations in the year 2019 would attempt to do that and would probably succeed however it would require exactly that; a complete rewrite which is not feasible right now. Thats by the way also the reason why it is difficult to write an XMPP client on iOS. Or more broadly put this is also the reason why other protocols are designed as or migrated to stateless protocols (often based on HTTP); take for example the migration of IMAP to [JMAP](https://jmap.io/).
#### Conversations doesnt work for me. Where can I get help?
You can join our conference room on [`conversations@conference.siacs.eu`](https://conversations.im/j/conversations@conference.siacs.eu).
You can join our conference room on [`xmppclient-dev@conference.narayana.im`](xmpp:xmppclient-dev@conference.narayana.im).
A lot of people in there are able to answer basic questions about the usage of
Conversations or can provide you with tips on running your own XMPP server. If
Conversations Classic or can provide you with tips on running your own XMPP server. If
you found a bug or your app crashes please read the Developer / Report Bugs
section of this document.
#### I need professional support with Conversations or setting up my server
I'm available for hire. Contact information can be found on [my website](https://gultsch.de).
#### How does the address book integration work?
The address book integration was designed to protect your privacy. Conversations
The address book integration was designed to protect your privacy. Conversations Classic
neither uploads contacts from your address book to your server nor fills your
address book with unnecessary contacts from your online roster. If you manually
add a Jabber ID to your phones address book Conversations will use the name and
add a Jabber ID to your phones address book Conversations Classic will use the name and
the profile picture of this contact. To make the process of adding Jabber IDs to
your address book easier you can click on the profile picture in the contact
details within Conversations. This will start an "add to address book" intent
with the JID as the payload. This doesn't require Conversations to have write
details within Conversations Classic. This will start an "add to address book" intent
with the JID as the payload. This doesn't require Conversations Classic to have write
permissions on your address book but also doesn't require you to copy/paste a
JID from one app to another.
@ -249,45 +203,23 @@ changeable on the fly. Metrics like last active client (the client which sent
the last message) are much better.
Unfortunately these modern replacements for legacy XMPP features are not widely
adopted. However Conversations should be an instant messenger for the future and
instead of making Conversations compatible with the past we should work on
adopted. However Conversations Classic should be an instant messenger for the future and
instead of making Conversations Classic compatible with the past we should work on
implementing new, improved technologies and getting them into other XMPP clients
as well.
Making these status and priority optional isn't a solution either because
Conversations is trying to get rid of old behaviours and set an example for
Conversations Classic is trying to get rid of old behaviours and set an example for
other clients.
#### Translations
Translations are managed on [Weblate](https://translate.codeberg.org/projects/conversations/).
You can log in with your Codeberg account and start translating.
#### How do I backup / move Conversations Classic to a new device?
#### How do I backup / move Conversations to a new device?
Use the Backup button in the Settings.
See the dedicated guides for
- [backups](docs/user/backup.md)
- [migrations](docs/user/migrating_to_new_device.md)
#### Conversations Classic is missing a certain feature
#### Conversations is missing a certain feature
I'm open for new feature suggestions. You can use the [issue tracker][https://codeberg.org/iNPUTmice/Conversations/issues]
on Codeberg. Please take some time to browse through the issues to see if someone
else already suggested it. Be assured that I read each and every ticket. If I
like it I will leave it open until it's implemented. If I don't like it I will
close it (usually with a short comment). If I don't comment on an feature
request that's probably a good sign because this means I agree with you.
Commenting with +1 on either open or closed issues won't change my mind, nor
will it accelerate the development.
#### You closed my feature request but I want it really really badly
Just write it yourself and send me a pull request. If I like it I will happily
merge it if I don't at least you and like minded people get to enjoy it.
#### I need a feature and I need it now!
I am available for hire. Find contact information on [my website](https://gultsch.de).
Please report it to our XMPP conference [`xmppclient-dev@conference.narayana.im`](xmpp:xmppclient-dev@conference.narayana.im)
### Security
@ -299,7 +231,7 @@ I am available for hire. Find contact information on [my website](https://gultsc
#### How do I use OpenPGP
Before you continue reading you should note that the OpenPGP support in
Conversations is experimental. This is not because it will make the app unstable
Conversations Classic is experimental. This is not because it will make the app unstable
but because the fundamental concepts of PGP aren't ready for widespread use.
The way PGP works is that you trust Key IDs instead of JID's or email addresses.
So in theory your contact list should consist of Public-Key-IDs instead of
@ -337,12 +269,12 @@ details and hit the settings button (the one with the gears) and select both *pr
Every participant has to announce their OpenPGP key (see answer above).
If you would like to send encrypted messages to a conference you have to make
sure that you have every participant's public key in your OpenKeychain.
Right now there is no check in Conversations to ensure that.
Right now there is no check in Conversations Classic to ensure that.
You have to take care of that yourself. Go to the conference details and
touch every key id (The hexadecimal number below a contact). This will send you
to OpenKeychain which will assist you on adding the key. This works best in
very small conferences with contacts you are already using OpenPGP with. This
feature is regarded experimental. Conversations is the only client that uses
feature is regarded experimental. Conversations Classic is the only client that uses
XEP-0027 with conferences. (The XEP neither specifically allows nor disallows
this.)
@ -350,89 +282,6 @@ this.)
Read more about the concept on https://gultsch.de/trust.html
#### What happened to OTR support?
OTR was removed because it was highly unreliable. It didnt work with multiple devices and was never really specified to work with XMPP. The codebase was a mess (There was an HTML parser in there for crying out loud to deal with the garbage some OTR clients would send.) Verification was implemented in a non-blocking way. It would tell you if the current session was using an unknown fingerprint but it didnt actively stopped you from sending messages until you have confirmed the new fingerprint. (Like Conversations would do now with BTBV after verification or when BTBV is turned off.) Considering the previous points there was little to no desire from my point to fix this potential security issue or clean up the code base. Another reason for the removal was that people would use it *accidentally* even to communicate between two Conversations clients because they read somewhere that OTR is good.
### What clients do I use on other platforms
There are XMPP Clients available for all major platforms.
#### Windows / Linux
For your desktop computer we recommend that you use [Gajim](https://gajim.org). You need to install the `OMEMO` plugin to get the best compatibility with Conversations. Plugins can be installed from within the app, from your distribution, or from flatpak if you installed it from there.
#### iOS
Unfortunately we dont have a recommendation for iPhones right now. There are three clients available [Siskin](https://siskin.im/), [ChatSecure](https://chatsecure.org/) and [Monal](https://monal.im/). Each with their own pros and cons.
### Development
<a name="beta"></a>
#### Beta testing
If you bought the App on [Google Play](https://play.google.com/store/apps/details?id=eu.siacs.conversations)
you can get access to the the latest beta version by signing up using [this link](https://play.google.com/apps/testing/eu.siacs.conversations).
#### How do I build Conversations
Make sure to have ANDROID_HOME point to your Android SDK. Use the Android SDK Manager to install missing dependencies.
Alternatively (and to avoid thinking about environment variables), create a file called local.properties, in the root of the Conversations build tree,
with the following contents:
```
## This file must *NOT* be checked into Version Control Systems,
# as it contains information specific to your local configuration.
#
# Location of the SDK. This is only used by Gradle.
# For customization when using a Version Control System, please read the
# header note.
#Wed May 20 16:21:35 CST 2020
ndk.dir=Path-To-Ndk
sdk.dir=Path-To-Sdk
```
Then issue the following commands in order to build the apk.
git clone https://codeberg.org/iNPUTmice/Conversations.git
cd Conversations
./gradlew assembleConversationsFreeDebug
There are two build flavors available. *free* and *playstore*. Unless you know what you are doing you only need *free*.
You will find the apks in the `./build/outputs/apk/conversationsFree/debug/` directory.
Be careful, the resulting apks will not install unless you delete your existing Conversations installation (which will delete all the messages from your phone, and if you have used OMEMO, you will not be able to restore them from the server).
Do it at your own risk.
You, though, can make your own build a "test build", that can be installed alongside the normal (F-Droid or Google Play) Conversations:
In the file `build.gradle`, find the line `applicationId "eu.siacs.conversations"` , and replace it with `applicationId "my.conversations.fork"`, also below replace "Conversations" appName with "MyCFork".
Then the resulting APK can be installed ALONGSIDE normal Conversations. And have a different name so it's not confusing
WARNING: DO NOT REPLACE ANYTHING ELSE ANYWHERE ELSE, DO NOT REPLACE THIS PROJECT WIDE. JUST 2 strings in THAT specific file!
#### How do I debug Conversations
If something goes wrong Conversations usually exposes very little information in
the UI (other than the fact that something didn't work). However with adb
(android debug bridge) you can squeeze some more information out of Conversations.
These information are especially useful if you are experiencing trouble with
your connection or with file transfer.
To use adb you have to connect your mobile phone to your computer with an USB cable
and install `adb`. Most Linux systems have prebuilt packages for that tool. On
Debian/Ubuntu for example it is called `android-tools-adb`.
Furthermore you might have to enable 'USB debugging' in the Developer options of your
phone. After that you can just execute the following on your computer:
adb -d logcat -v time -s conversations
If need be there are also some Apps on the PlayStore that can be used to show the logcat
directly on your rooted phone. (Search for logcat). However in regards to further processing
(for example to create an issue here on Codeberg) it is more convenient to just use your PC.
#### I found a bug
Please report it to our [issue tracker](https://codeberg.org/iNPUTmice/Conversations/issues). If your app crashes please
provide a stack trace. If you are experiencing misbehavior please provide
detailed steps to reproduce. Always mention whether you are running the latest
Play Store version or the current HEAD. If you are having problems connecting to
your XMPP server your file transfer doesnt work as expected please always
include a logcat debug output with your issue (see above).
Please report it to our XMPP conference [`xmppclient-dev@conference.narayana.im`](xmpp:xmppclient-dev@conference.narayana.im).

View file

@ -1,15 +0,0 @@
apply plugin: "java-library"
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
dependencies {
implementation project(':annotation')
annotationProcessor 'com.google.auto.service:auto-service:1.0.1'
api 'com.google.auto.service:auto-service-annotations:1.0.1'
implementation 'com.google.guava:guava:31.1-jre'
}

View file

@ -1,160 +0,0 @@
package im.conversations.android.annotation.processor;
import com.google.auto.service.AutoService;
import com.google.common.base.CaseFormat;
import com.google.common.base.Objects;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import im.conversations.android.annotation.XmlElement;
import im.conversations.android.annotation.XmlPackage;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.tools.JavaFileObject;
@AutoService(Processor.class)
@SupportedSourceVersion(SourceVersion.RELEASE_17)
@SupportedAnnotationTypes("im.conversations.android.annotation.XmlElement")
public class XmlElementProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
final Set<? extends Element> elements =
roundEnvironment.getElementsAnnotatedWith(XmlElement.class);
final ImmutableMap.Builder<Id, String> builder = ImmutableMap.builder();
for (final Element element : elements) {
if (element instanceof final TypeElement typeElement) {
final Id id = of(typeElement);
builder.put(id, typeElement.getQualifiedName().toString());
}
}
final ImmutableMap<Id, String> maps = builder.build();
if (maps.isEmpty()) {
return false;
}
final JavaFileObject extensionFile;
try {
extensionFile =
processingEnv
.getFiler()
.createSourceFile("im.conversations.android.xmpp.Extensions");
} catch (final IOException e) {
throw new RuntimeException(e);
}
try (final PrintWriter out = new PrintWriter(extensionFile.openWriter())) {
out.println("package im.conversations.android.xmpp;");
out.println("import com.google.common.collect.BiMap;");
out.println("import com.google.common.collect.ImmutableBiMap;");
out.println("import im.conversations.android.xmpp.ExtensionFactory;");
out.println("import im.conversations.android.xmpp.model.Extension;");
out.print("\n");
out.println("public final class Extensions {");
out.println(
"public static final BiMap<ExtensionFactory.Id, Class<? extends Extension>>"
+ " EXTENSION_CLASS_MAP;");
out.println("static {");
out.println(
"final var builder = new ImmutableBiMap.Builder<ExtensionFactory.Id, Class<?"
+ " extends Extension>>();");
for (final Map.Entry<Id, String> entry : maps.entrySet()) {
Id id = entry.getKey();
String clazz = entry.getValue();
out.format(
"builder.put(new ExtensionFactory.Id(\"%s\",\"%s\"),%s.class);",
id.name, id.namespace, clazz);
out.print("\n");
}
out.println("EXTENSION_CLASS_MAP = builder.build();");
out.println("}");
out.println(" private Extensions() {}");
out.println("}");
// writing generated file to out
} catch (IOException e) {
throw new RuntimeException(e);
}
return true;
}
private static Id of(final TypeElement typeElement) {
final XmlElement xmlElement = typeElement.getAnnotation(XmlElement.class);
PackageElement packageElement = getPackageElement(typeElement);
XmlPackage xmlPackage =
packageElement == null ? null : packageElement.getAnnotation(XmlPackage.class);
if (xmlElement == null) {
throw new IllegalStateException(
String.format(
"%s is not annotated as @XmlElement",
typeElement.getQualifiedName().toString()));
}
final String packageNamespace = xmlPackage == null ? null : xmlPackage.namespace();
final String elementName = xmlElement.name();
final String elementNamespace = xmlElement.namespace();
final String namespace;
if (!Strings.isNullOrEmpty(elementNamespace)) {
namespace = elementNamespace;
} else if (!Strings.isNullOrEmpty(packageNamespace)) {
namespace = packageNamespace;
} else {
throw new IllegalStateException(
String.format(
"%s does not declare a namespace",
typeElement.getQualifiedName().toString()));
}
final String name;
if (Strings.isNullOrEmpty(elementName)) {
name =
CaseFormat.UPPER_CAMEL.to(
CaseFormat.LOWER_HYPHEN, typeElement.getSimpleName().toString());
} else {
name = elementName;
}
return new Id(name, namespace);
}
private static PackageElement getPackageElement(final TypeElement typeElement) {
final Element parent = typeElement.getEnclosingElement();
if (parent instanceof PackageElement) {
return (PackageElement) parent;
} else {
final Element nextParent = parent.getEnclosingElement();
if (nextParent instanceof PackageElement) {
return (PackageElement) nextParent;
} else {
return null;
}
}
}
public static class Id {
public final String name;
public final String namespace;
public Id(String name, String namespace) {
this.name = name;
this.namespace = namespace;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Id id = (Id) o;
return Objects.equal(name, id.name) && Objects.equal(namespace, id.namespace);
}
@Override
public int hashCode() {
return Objects.hashCode(name, namespace);
}
}
}

View file

@ -1,6 +0,0 @@
apply plugin: "java-library"
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}

View file

@ -1,15 +0,0 @@
package im.conversations.android.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE})
public @interface XmlElement {
String name() default "";
String namespace() default "";
}

View file

@ -1,12 +0,0 @@
package im.conversations.android.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.PACKAGE)
public @interface XmlPackage {
String namespace();
}

View file

@ -1,147 +0,0 @@
apply plugin: "com.android.application"
apply plugin: "androidx.navigation.safeargs"
apply plugin: "com.diffplug.spotless"
android {
namespace 'im.conversations.android'
compileSdk 33
defaultConfig {
minSdk 23
targetSdk 33
versionCode 1
versionName "3.0.0-alpha"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
javaCompileOptions {
annotationProcessorOptions {
arguments += ["room.schemaLocation": "$projectDir/schemas".toString()]
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
buildFeatures {
dataBinding true
}
flavorDimensions "product"
productFlavors {
quicksy {
dimension "product"
applicationId = "im.quicksy.client"
def appName = "Quicksy"
resValue "string", "applicationId", applicationId
resValue "string", "app_name", appName
buildConfigField "String", "APP_NAME", "\"$appName\""
}
conversations {
dimension "product"
applicationId "im.conversations.android"
def appName = "Conversations"
resValue "string", "applicationId", applicationId
resValue "string", "app_name", appName
buildConfigField "String", "APP_NAME", "\"$appName\""
}
}
}
spotless {
java {
target '**/*.java'
googleJavaFormat().aosp().reflowLongStrings()
}
}
dependencies {
implementation project(':annotation')
annotationProcessor project(':annotation-processor')
// make Java 8 API available
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3'
// Jetpack / AndroidX libraries
implementation "androidx.appcompat:appcompat:$rootProject.ext.appcompatVersion"
implementation "androidx.lifecycle:lifecycle-extensions:$rootProject.ext.lifecycleVersion"
implementation "androidx.navigation:navigation-fragment:$rootProject.ext.navVersion"
implementation "androidx.navigation:navigation-ui:$rootProject.ext.navVersion"
implementation "androidx.room:room-runtime:$rootProject.ext.roomVersion"
implementation "androidx.room:room-guava:$rootProject.ext.roomVersion"
implementation "androidx.room:room-paging:$rootProject.ext.roomVersion"
annotationProcessor "androidx.room:room-compiler:$rootProject.ext.roomVersion"
implementation "androidx.paging:paging-runtime:$rootProject.ext.pagingVersion"
implementation "androidx.preference:preference:$rootProject.ext.preferenceVersion"
implementation "androidx.security:security-crypto:1.0.0"
// Google material design libraries
implementation "com.google.android.material:material:$rootProject.ext.material"
// LeakCanary to detect memory leaks in debug builds
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.10'
// crypto libraries
implementation 'org.whispersystems:signal-protocol-java:2.6.2'
implementation 'org.conscrypt:conscrypt-android:2.5.2'
implementation 'org.bouncycastle:bcmail-jdk15on:1.64'
// XMPP Address library
implementation 'org.jxmpp:jxmpp-jid:1.0.3'
// WebRTC
implementation 'im.conversations.webrtc:webrtc-android:104.0.0'
// Consistent Color Generation
implementation 'org.hsluv:hsluv:0.2'
// DNS library (XMPP needs to resolve SRV records)
implementation 'de.measite.minidns:minidns-hla:0.2.4'
// Guava
implementation 'com.google.guava:guava:31.1-android'
// HTTP library
implementation "com.squareup.okhttp3:okhttp:4.10.0"
// JSON parser
implementation 'com.google.code.gson:gson:2.10.1'
// logging framework + logging api
implementation 'org.slf4j:slf4j-api:1.7.36'
implementation 'com.github.tony19:logback-android:2.0.1'
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.robolectric:robolectric:4.9.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation "androidx.test.espresso:espresso-core:$rootProject.ext.espressoVersion"
}

View file

@ -1,21 +0,0 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View file

@ -1,214 +0,0 @@
package im.conversations.android.xmpp;
import static org.hamcrest.Matchers.*;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import im.conversations.android.database.model.Account;
import im.conversations.android.database.model.StanzaId;
import im.conversations.android.transformer.MessageTransformation;
import im.conversations.android.transformer.Transformer;
import im.conversations.android.xmpp.manager.ArchiveManager;
import im.conversations.android.xmpp.model.jabber.Body;
import im.conversations.android.xmpp.model.stanza.Message;
import java.time.Instant;
import java.util.Collections;
import java.util.concurrent.ExecutionException;
import org.hamcrest.MatcherAssert;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class ArchivePagingTest extends BaseTransformationTest {
@Test
public void initialQuery() throws ExecutionException, InterruptedException {
final var ranges = database.archiveDao().resetLivePage(account(), ACCOUNT);
final Range range = Iterables.getOnlyElement(ranges);
Assert.assertNull(range.id);
Assert.assertEquals(Range.Order.REVERSE, range.order);
}
@Test
public void queryAfterSingleLiveMessage() throws ExecutionException, InterruptedException {
final var stub = new StubMessage(2);
transformer.transform(stub.messageTransformation(), stub.stanzaId());
final var ranges = database.archiveDao().resetLivePage(account(), ACCOUNT);
Assert.assertEquals(2, ranges.size());
MatcherAssert.assertThat(
ranges,
contains(new Range(Range.Order.REVERSE, "2"), new Range(Range.Order.NORMAL, "2")));
}
@Test
public void twoLiveMessageQueryNoSubmitAndQuery()
throws ExecutionException, InterruptedException {
final var stub2 = new StubMessage(2);
transformer.transform(stub2.messageTransformation(), stub2.stanzaId());
final var stub3 = new StubMessage(3);
transformer.transform(stub3.messageTransformation(), stub3.stanzaId());
final var ranges = database.archiveDao().resetLivePage(account(), ACCOUNT);
Assert.assertEquals(2, ranges.size());
MatcherAssert.assertThat(
ranges,
contains(new Range(Range.Order.REVERSE, "2"), new Range(Range.Order.NORMAL, "3")));
final var stub4 = new StubMessage(4);
transformer.transform(stub4.messageTransformation(), stub4.stanzaId());
final var rangesSecondAttempt = database.archiveDao().resetLivePage(account(), ACCOUNT);
Assert.assertEquals(2, rangesSecondAttempt.size());
MatcherAssert.assertThat(
rangesSecondAttempt,
contains(new Range(Range.Order.REVERSE, "2"), new Range(Range.Order.NORMAL, "3")));
}
@Test
public void liveMessageQuerySubmitAndQuery() throws ExecutionException, InterruptedException {
final var stub2 = new StubMessage(2);
transformer.transform(stub2.messageTransformation(), stub2.stanzaId());
final var stub3 = new StubMessage(3);
transformer.transform(stub3.messageTransformation(), stub3.stanzaId());
final var ranges = database.archiveDao().resetLivePage(account(), ACCOUNT);
Assert.assertEquals(2, ranges.size());
MatcherAssert.assertThat(
ranges,
contains(new Range(Range.Order.REVERSE, "2"), new Range(Range.Order.NORMAL, "3")));
final var stub4 = new StubMessage(4);
transformer.transform(stub4.messageTransformation(), stub4.stanzaId());
for (final Range range : ranges) {
database.archiveDao()
.submitPage(
account(),
ACCOUNT,
range,
new ArchiveManager.QueryResult(
true, Page.emptyWithCount(range.id, null)),
false);
}
final var rangesSecondAttempt = database.archiveDao().resetLivePage(account(), ACCOUNT);
// we mark the reversing range as complete in the submit above; hence it is not included in
// the second ranges
Assert.assertEquals(1, rangesSecondAttempt.size());
MatcherAssert.assertThat(rangesSecondAttempt, contains(new Range(Range.Order.NORMAL, "4")));
}
@Test
public void liveMessageQuerySubmitTwice() throws ExecutionException, InterruptedException {
final var stub2 = new StubMessage(2);
transformer.transform(stub2.messageTransformation(), stub2.stanzaId());
final var ranges = database.archiveDao().resetLivePage(account(), ACCOUNT);
Assert.assertEquals(2, ranges.size());
MatcherAssert.assertThat(
ranges,
contains(new Range(Range.Order.REVERSE, "2"), new Range(Range.Order.NORMAL, "2")));
final var account = account();
final var transformer =
new Transformer(account, ApplicationProvider.getApplicationContext(), database);
transformer.transform(
Collections.emptyList(),
ACCOUNT,
new Range(Range.Order.REVERSE, "2"),
new ArchiveManager.QueryResult(true, Page.emptyWithCount("2", null)),
true);
transformer.transform(
Collections.emptyList(),
ACCOUNT,
new Range(Range.Order.NORMAL, "2"),
new ArchiveManager.QueryResult(false, new Page("3", "4", 2)),
false);
transformer.transform(
Collections.emptyList(),
ACCOUNT,
new Range(Range.Order.NORMAL, "4"),
new ArchiveManager.QueryResult(true, new Page("5", "6", 2)),
false);
final var rangesSecondAttempt = database.archiveDao().resetLivePage(account(), ACCOUNT);
// we mark the reversing range as complete in the submit above; hence it is not included in
// the second ranges
Assert.assertEquals(1, rangesSecondAttempt.size());
MatcherAssert.assertThat(rangesSecondAttempt, contains(new Range(Range.Order.NORMAL, "6")));
}
@Test
public void liveMessageQuerySubmitTwiceWithDuplicates()
throws ExecutionException, InterruptedException {
final var stub2 = new StubMessage(2);
transformer.transform(stub2.messageTransformation(), stub2.stanzaId());
final var ranges = database.archiveDao().resetLivePage(account(), ACCOUNT);
Assert.assertEquals(2, ranges.size());
MatcherAssert.assertThat(
ranges,
contains(new Range(Range.Order.REVERSE, "2"), new Range(Range.Order.NORMAL, "2")));
final var account = account();
final var transformer =
new Transformer(account, ApplicationProvider.getApplicationContext(), database);
transformer.transform(
Collections.emptyList(),
ACCOUNT,
new Range(Range.Order.REVERSE, "2"),
new ArchiveManager.QueryResult(true, Page.emptyWithCount("2", null)),
true);
transformer.transform(
ImmutableList.of(stub2.messageTransformation()),
ACCOUNT,
new Range(Range.Order.NORMAL, "2"),
new ArchiveManager.QueryResult(false, new Page("3", "4", 2)),
false);
transformer.transform(
Collections.emptyList(),
ACCOUNT,
new Range(Range.Order.NORMAL, "4"),
new ArchiveManager.QueryResult(true, new Page("5", "6", 2)),
false);
}
private Account account() throws ExecutionException, InterruptedException {
return this.database.accountDao().getEnabledAccount(ACCOUNT).get();
}
private static class StubMessage {
public final int id;
private StubMessage(int id) {
this.id = id;
}
public StanzaId stanzaId() {
return new StanzaId(String.valueOf(id), ACCOUNT);
}
public MessageTransformation messageTransformation() {
final var message = new Message();
message.setTo(ACCOUNT);
message.setFrom(REMOTE);
message.addExtension(new Body()).setContent(String.format("%s (%d)", GREETING, id));
return MessageTransformation.of(
message,
Instant.ofEpochSecond(id * 2000L),
REMOTE,
String.valueOf(id),
message.getFrom().asBareJid(),
null);
}
}
}

View file

@ -1,42 +0,0 @@
package im.conversations.android.xmpp;
import android.content.Context;
import androidx.room.Room;
import androidx.test.core.app.ApplicationProvider;
import im.conversations.android.IDs;
import im.conversations.android.database.ConversationsDatabase;
import im.conversations.android.database.entity.AccountEntity;
import im.conversations.android.transformer.Transformer;
import java.util.concurrent.ExecutionException;
import org.junit.Before;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.impl.JidCreate;
public abstract class BaseTransformationTest {
protected static final BareJid ACCOUNT = JidCreate.bareFromOrThrowUnchecked("user@example.com");
protected static final BareJid REMOTE =
JidCreate.bareFromOrThrowUnchecked("juliet@example.com");
protected static final BareJid REMOTE_2 =
JidCreate.bareFromOrThrowUnchecked("romeo@example.com");
protected static final String GREETING = "Hi Juliet. How are you?";
protected ConversationsDatabase database;
protected Transformer transformer;
@Before
public void setupTransformer() throws ExecutionException, InterruptedException {
final Context context = ApplicationProvider.getApplicationContext();
this.database = Room.inMemoryDatabaseBuilder(context, ConversationsDatabase.class).build();
final var account = new AccountEntity();
account.address = ACCOUNT;
account.enabled = true;
account.randomSeed = IDs.seed();
final long id = database.accountDao().insert(account);
this.transformer =
new Transformer(
database.accountDao().getEnabledAccount(id).get(), context, database);
}
}

View file

@ -1,555 +0,0 @@
package im.conversations.android.xmpp;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.common.collect.Iterables;
import im.conversations.android.database.model.Encryption;
import im.conversations.android.database.model.MessageEmbedded;
import im.conversations.android.database.model.Modification;
import im.conversations.android.database.model.PartType;
import im.conversations.android.transformer.MessageTransformation;
import im.conversations.android.xmpp.model.correction.Replace;
import im.conversations.android.xmpp.model.jabber.Body;
import im.conversations.android.xmpp.model.reactions.Reaction;
import im.conversations.android.xmpp.model.reactions.Reactions;
import im.conversations.android.xmpp.model.receipts.Received;
import im.conversations.android.xmpp.model.reply.Reply;
import im.conversations.android.xmpp.model.retract.Retract;
import im.conversations.android.xmpp.model.stanza.Message;
import java.time.Instant;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.jid.parts.Resourcepart;
import org.jxmpp.stringprep.XmppStringprepException;
@RunWith(AndroidJUnit4.class)
public class MessageTransformationTest extends BaseTransformationTest {
@Test
public void reactionBeforeOriginal() throws XmppStringprepException {
final var reactionMessage = new Message();
reactionMessage.setId("2");
reactionMessage.setTo(ACCOUNT);
reactionMessage.setFrom(JidCreate.fullFrom(REMOTE, Resourcepart.from("junit")));
final var reactions = reactionMessage.addExtension(new Reactions());
reactions.setId("1");
final var reaction = reactions.addExtension(new Reaction());
reaction.setContent("Y");
this.transformer.transform(
MessageTransformation.of(
reactionMessage,
Instant.now(),
REMOTE,
"stanza-b",
reactionMessage.getFrom().asBareJid(),
null));
final var originalMessage = new Message();
originalMessage.setId("1");
originalMessage.setTo(REMOTE);
originalMessage.setFrom(JidCreate.fullFrom(ACCOUNT, Resourcepart.from(("junit"))));
final var body = originalMessage.addExtension(new Body());
body.setContent(GREETING);
this.transformer.transform(
MessageTransformation.of(
originalMessage,
Instant.now(),
REMOTE,
"stanza-a",
originalMessage.getFrom().asBareJid(),
null));
final var messages = database.messageDao().getMessagesForTesting(1L);
Assert.assertEquals(1, messages.size());
final var message = Iterables.getOnlyElement(messages);
final var onlyContent = Iterables.getOnlyElement(message.contents);
Assert.assertEquals(GREETING, onlyContent.body);
Assert.assertEquals(Encryption.CLEARTEXT, message.encryption);
final var onlyReaction = Iterables.getOnlyElement(message.reactions);
Assert.assertEquals("Y", onlyReaction.reaction);
Assert.assertEquals(REMOTE, onlyReaction.reactionBy);
}
@Test
public void multipleReactions() throws XmppStringprepException {
final var group = JidCreate.bareFrom("a@group.example.com");
final var message = new Message(Message.Type.GROUPCHAT);
message.addExtension(new Body("Please give me a thumbs up"));
message.setFrom(JidCreate.fullFrom(group, Resourcepart.from("user-a")));
this.transformer.transform(
MessageTransformation.of(
message, Instant.now(), REMOTE, "stanza-a", null, "id-user-a"));
final var reactionA = new Message(Message.Type.GROUPCHAT);
reactionA.setFrom(JidCreate.fullFrom(group, Resourcepart.from("user-b")));
reactionA.addExtension(Reactions.to("stanza-a")).addExtension(new Reaction("Y"));
this.transformer.transform(
MessageTransformation.of(
reactionA, Instant.now(), REMOTE, "stanza-b", null, "id-user-b"));
final var reactionB = new Message(Message.Type.GROUPCHAT);
reactionB.setFrom(JidCreate.fullFrom(group, Resourcepart.from("user-c")));
reactionB.addExtension(Reactions.to("stanza-a")).addExtension(new Reaction("Y"));
this.transformer.transform(
MessageTransformation.of(
reactionB, Instant.now(), REMOTE, "stanza-c", null, "id-user-c"));
final var reactionC = new Message(Message.Type.GROUPCHAT);
reactionC.setFrom(JidCreate.fullFrom(group, Resourcepart.from("user-d")));
final var reactions = reactionC.addExtension(Reactions.to("stanza-a"));
reactions.addExtension(new Reaction("Y"));
reactions.addExtension(new Reaction("Z"));
this.transformer.transform(
MessageTransformation.of(
reactionC, Instant.now(), REMOTE, "stanza-d", null, "id-user-d"));
final var messages = database.messageDao().getMessagesForTesting(1L);
Assert.assertEquals(1, messages.size());
final var dbMessage = Iterables.getOnlyElement(messages);
Assert.assertEquals(4, dbMessage.reactions.size());
final var aggregated = dbMessage.getAggregatedReactions();
final var mostFrequentReaction = Iterables.get(aggregated, 0);
Assert.assertEquals("Y", mostFrequentReaction.getKey());
Assert.assertEquals(3L, (long) mostFrequentReaction.getValue());
final var secondReaction = Iterables.get(aggregated, 1);
Assert.assertEquals("Z", secondReaction.getKey());
Assert.assertEquals(1L, (long) secondReaction.getValue());
}
@Test
public void correctionBeforeOriginal() throws XmppStringprepException {
final var messageCorrection = new Message();
messageCorrection.setId("2");
messageCorrection.setTo(ACCOUNT);
messageCorrection.setFrom(JidCreate.fullFrom(REMOTE, Resourcepart.from("junit")));
messageCorrection.addExtension(new Body()).setContent("Hi example!");
messageCorrection.addExtension(new Replace()).setId("1");
this.transformer.transform(
MessageTransformation.of(
messageCorrection,
Instant.now(),
REMOTE,
"stanza-a",
messageCorrection.getFrom().asBareJid(),
null));
// the correction should not show up as a message
Assert.assertEquals(0, database.messageDao().getMessagesForTesting(1L).size());
final var messageWithTypo = new Message();
messageWithTypo.setId("1");
messageWithTypo.setTo(ACCOUNT);
messageWithTypo.setFrom(JidCreate.fullFrom(REMOTE, Resourcepart.from("junit")));
messageWithTypo.addExtension(new Body()).setContent("Hii example!");
this.transformer.transform(
MessageTransformation.of(
messageWithTypo,
Instant.now(),
REMOTE,
"stanza-b",
messageWithTypo.getFrom().asBareJid(),
null));
final var messages = database.messageDao().getMessagesForTesting(1L);
Assert.assertEquals(1, messages.size());
final var message = Iterables.getOnlyElement(messages);
final var onlyContent = Iterables.getOnlyElement(message.contents);
Assert.assertEquals(Modification.CORRECTION, message.modification);
Assert.assertEquals("Hi example!", onlyContent.body);
}
@Test
public void correctionAfterOriginal() throws XmppStringprepException {
final var messageWithTypo = new Message();
messageWithTypo.setId("1");
messageWithTypo.setTo(ACCOUNT);
messageWithTypo.setFrom(JidCreate.fullFrom(REMOTE, Resourcepart.from("junit")));
messageWithTypo.addExtension(new Body()).setContent("Hii example!");
this.transformer.transform(
MessageTransformation.of(
messageWithTypo,
Instant.now(),
REMOTE,
"stanza-a",
messageWithTypo.getFrom().asBareJid(),
null));
Assert.assertEquals(1, database.messageDao().getMessagesForTesting(1L).size());
final var messageCorrection = new Message();
messageCorrection.setId("2");
messageCorrection.setTo(ACCOUNT);
messageCorrection.setFrom(JidCreate.fullFrom(REMOTE, Resourcepart.from("junit")));
messageCorrection.addExtension(new Body()).setContent("Hi example!");
messageCorrection.addExtension(new Replace()).setId("1");
this.transformer.transform(
MessageTransformation.of(
messageCorrection,
Instant.now(),
REMOTE,
"stanza-b",
messageCorrection.getFrom().asBareJid(),
null));
final var messages = database.messageDao().getMessagesForTesting(1L);
Assert.assertEquals(1, messages.size());
final var message = Iterables.getOnlyElement(messages);
final var onlyContent = Iterables.getOnlyElement(message.contents);
Assert.assertEquals(Modification.CORRECTION, message.modification);
Assert.assertEquals("Hi example!", onlyContent.body);
}
@Test
public void replacingReactions() throws XmppStringprepException {
final var group = JidCreate.bareFrom("a@group.example.com");
final var message = new Message(Message.Type.GROUPCHAT);
message.addExtension(new Body("Please give me a thumbs up"));
message.setFrom(JidCreate.fullFrom(group, Resourcepart.from("user-a")));
this.transformer.transform(
MessageTransformation.of(
message, Instant.now(), REMOTE, "stanza-a", null, "id-user-a"));
final var reactionA = new Message(Message.Type.GROUPCHAT);
reactionA.setFrom(JidCreate.fullFrom(group, Resourcepart.from("user-b")));
reactionA.addExtension(Reactions.to("stanza-a")).addExtension(new Reaction("N"));
this.transformer.transform(
MessageTransformation.of(
reactionA, Instant.now(), REMOTE, "stanza-b", null, "id-user-b"));
final var reactionB = new Message(Message.Type.GROUPCHAT);
reactionB.setFrom(JidCreate.fullFrom(group, Resourcepart.from("user-b")));
reactionB.addExtension(Reactions.to("stanza-a")).addExtension(new Reaction("Y"));
this.transformer.transform(
MessageTransformation.of(
reactionB, Instant.now(), REMOTE, "stanza-c", null, "id-user-b"));
final var messages = database.messageDao().getMessagesForTesting(1L);
Assert.assertEquals(1, messages.size());
final var dbMessage = Iterables.getOnlyElement(messages);
Assert.assertEquals(1, dbMessage.reactions.size());
}
@Test
public void twoCorrectionsOneReactionBeforeOriginalInGroupChat()
throws XmppStringprepException {
final var group = JidCreate.bareFrom("a@group.example.com");
final var ogStanzaId = "og-stanza-id";
final var ogMessageId = "og-message-id";
// first correction
final var m1 = new Message(Message.Type.GROUPCHAT);
// m1.setId(ogMessageId);
m1.addExtension(new Body("Please give me an thumbs up"));
m1.addExtension(new Replace()).setId(ogMessageId);
m1.setFrom(JidCreate.fullFrom(group, Resourcepart.from("user-a")));
this.transformer.transform(
MessageTransformation.of(
m1,
Instant.ofEpochMilli(2000),
REMOTE,
"irrelevant-stanza-id1",
null,
"id-user-a"));
// second correction
final var m2 = new Message(Message.Type.GROUPCHAT);
// m2.setId(ogMessageId);
m2.addExtension(new Body("Please give me a thumbs up"));
m2.addExtension(new Replace()).setId(ogMessageId);
m2.setFrom(JidCreate.fullFrom(group, Resourcepart.from("user-a")));
this.transformer.transform(
MessageTransformation.of(
m2,
Instant.ofEpochMilli(3000),
REMOTE,
"irrelevant-stanza-id2",
null,
"id-user-a"));
// a reaction
final var reactionB = new Message(Message.Type.GROUPCHAT);
reactionB.setFrom(JidCreate.fullFrom(group, Resourcepart.from("user-b")));
reactionB.addExtension(Reactions.to(ogStanzaId)).addExtension(new Reaction("Y"));
this.transformer.transform(
MessageTransformation.of(
reactionB,
Instant.now(),
REMOTE,
"irrelevant-stanza-id3",
null,
"id-user-b"));
// the original message
final var m4 = new Message(Message.Type.GROUPCHAT);
m4.setId(ogMessageId);
m4.addExtension(new Body("Please give me thumbs up"));
m4.setFrom(JidCreate.fullFrom(group, Resourcepart.from("user-a")));
this.transformer.transform(
MessageTransformation.of(
m4, Instant.ofEpochMilli(1000), REMOTE, ogStanzaId, null, "id-user-a"));
final var messages = database.messageDao().getMessagesForTesting(1L);
Assert.assertEquals(1, messages.size());
final var dbMessage = Iterables.getOnlyElement(messages);
Assert.assertEquals(1, dbMessage.reactions.size());
Assert.assertEquals(Modification.CORRECTION, dbMessage.modification);
Assert.assertEquals(
"Please give me a thumbs up", Iterables.getOnlyElement(dbMessage.contents).body);
}
@Test
public void twoReactionsOneCorrectionBeforeOriginalInGroupChat()
throws XmppStringprepException {
final var group = JidCreate.bareFrom("a@group.example.com");
final var ogStanzaId = "og-stanza-id";
final var ogMessageId = "og-message-id";
// first reaction
final var reactionA = new Message(Message.Type.GROUPCHAT);
reactionA.setFrom(JidCreate.fullFrom(group, Resourcepart.from("user-b")));
reactionA.addExtension(Reactions.to(ogStanzaId)).addExtension(new Reaction("Y"));
this.transformer.transform(
MessageTransformation.of(
reactionA,
Instant.now(),
REMOTE,
"irrelevant-stanza-id1",
null,
"id-user-b"));
// second reaction
final var reactionB = new Message(Message.Type.GROUPCHAT);
reactionB.setFrom(JidCreate.fullFrom(group, Resourcepart.from("user-c")));
reactionB.addExtension(Reactions.to(ogStanzaId)).addExtension(new Reaction("Y"));
this.transformer.transform(
MessageTransformation.of(
reactionB,
Instant.now(),
REMOTE,
"irrelevant-stanza-id2",
null,
"id-user-c"));
// a correction
final var m1 = new Message(Message.Type.GROUPCHAT);
m1.addExtension(new Body("Please give me a thumbs up"));
m1.addExtension(new Replace()).setId(ogMessageId);
m1.setFrom(JidCreate.fullFrom(group, Resourcepart.from("user-a")));
this.transformer.transform(
MessageTransformation.of(
m1,
Instant.ofEpochMilli(2000),
REMOTE,
"irrelevant-stanza-id3",
null,
"id-user-a"));
// the original message
final var m4 = new Message(Message.Type.GROUPCHAT);
m4.setId(ogMessageId);
m4.addExtension(new Body("Please give me thumbs up (Typo)"));
m4.setFrom(JidCreate.fullFrom(group, Resourcepart.from("user-a")));
this.transformer.transform(
MessageTransformation.of(
m4, Instant.ofEpochMilli(1000), REMOTE, ogStanzaId, null, "id-user-a"));
final var messages = database.messageDao().getMessagesForTesting(1L);
Assert.assertEquals(1, messages.size());
final var dbMessage = Iterables.getOnlyElement(messages);
Assert.assertEquals(2, dbMessage.reactions.size());
final var onlyReaction = Iterables.getOnlyElement(dbMessage.getAggregatedReactions());
Assert.assertEquals(2L, (long) onlyReaction.getValue());
Assert.assertEquals(Modification.CORRECTION, dbMessage.modification);
Assert.assertEquals(
"Please give me a thumbs up", Iterables.getOnlyElement(dbMessage.contents).body);
}
@Test
public void twoReactionsInGroupChat() throws XmppStringprepException {
final var group = JidCreate.bareFrom("a@group.example.com");
final var ogStanzaId = "og-stanza-id";
final var ogMessageId = "og-message-id";
// the original message
final var m4 = new Message(Message.Type.GROUPCHAT);
m4.setId(ogMessageId);
m4.addExtension(new Body("Please give me a thumbs up"));
m4.setFrom(JidCreate.fullFrom(group, Resourcepart.from("user-a")));
this.transformer.transform(
MessageTransformation.of(
m4, Instant.ofEpochMilli(1000), REMOTE, ogStanzaId, null, "id-user-a"));
// first reaction
final var reactionA = new Message(Message.Type.GROUPCHAT);
reactionA.setFrom(JidCreate.fullFrom(group, Resourcepart.from("user-b")));
reactionA.addExtension(Reactions.to(ogStanzaId)).addExtension(new Reaction("Y"));
this.transformer.transform(
MessageTransformation.of(
reactionA,
Instant.now(),
REMOTE,
"irrelevant-stanza-id1",
null,
"id-user-b"));
// second reaction
final var reactionB = new Message(Message.Type.GROUPCHAT);
reactionB.setFrom(JidCreate.fullFrom(group, Resourcepart.from("user-c")));
reactionB.addExtension(Reactions.to(ogStanzaId)).addExtension(new Reaction("Y"));
this.transformer.transform(
MessageTransformation.of(
reactionB,
Instant.now(),
REMOTE,
"irrelevant-stanza-id2",
null,
"id-user-c"));
final var messages = database.messageDao().getMessagesForTesting(1L);
Assert.assertEquals(1, messages.size());
final var dbMessage = Iterables.getOnlyElement(messages);
Assert.assertEquals(2, dbMessage.reactions.size());
final var onlyReaction = Iterables.getOnlyElement(dbMessage.getAggregatedReactions());
Assert.assertEquals(2L, (long) onlyReaction.getValue());
Assert.assertEquals(Modification.ORIGINAL, dbMessage.modification);
Assert.assertEquals(
"Please give me a thumbs up", Iterables.getOnlyElement(dbMessage.contents).body);
}
@Test
public void inReplyTo() throws XmppStringprepException {
final var m1 = new Message();
m1.setId("1");
m1.setTo(ACCOUNT);
m1.setFrom(JidCreate.fullFrom(REMOTE, Resourcepart.from("junit")));
m1.addExtension(new Body("Hi. How are you?"));
this.transformer.transform(
MessageTransformation.of(
m1, Instant.now(), REMOTE, "stanza-a", m1.getFrom().asBareJid(), null));
final var m2 = new Message();
m2.setId("2");
m2.setTo(REMOTE);
m2.setFrom(ACCOUNT);
m2.addExtension(new Body("I am fine."));
final var reply = m2.addExtension(new Reply());
reply.setId("1");
reply.setTo(REMOTE);
this.transformer.transform(
MessageTransformation.of(
m2, Instant.now(), REMOTE, "stanza-b", m2.getFrom().asBareJid(), null));
final var messages = database.messageDao().getMessagesForTesting(1L);
Assert.assertEquals(2, messages.size());
final var response = Iterables.get(messages, 1);
Assert.assertNotNull(response.inReplyToMessageEntityId);
final MessageEmbedded embeddedMessage = response.inReplyTo;
Assert.assertNotNull(embeddedMessage);
Assert.assertEquals(REMOTE, embeddedMessage.fromBare);
Assert.assertEquals(1L, embeddedMessage.contents.size());
Assert.assertEquals(
"Hi. How are you?", Iterables.getOnlyElement(embeddedMessage.contents).body);
Assert.assertNull(response.identityKey);
Assert.assertNull(response.trust);
}
@Test
public void messageWithReceipt() throws XmppStringprepException {
final var m1 = new Message();
m1.setId("1");
m1.setTo(REMOTE);
m1.setFrom(JidCreate.fullFrom(ACCOUNT, Resourcepart.from("junit")));
m1.addExtension(new Body("Hi. How are you?"));
this.transformer.transform(
MessageTransformation.of(
m1, Instant.now(), REMOTE, null, m1.getFrom().asBareJid(), null));
final var m2 = new Message();
m2.setTo(JidCreate.fullFrom(ACCOUNT, Resourcepart.from("junit")));
m2.setFrom(JidCreate.fullFrom(REMOTE, Resourcepart.from("junit")));
m2.addExtension(new Received()).setId("1");
this.transformer.transform(
MessageTransformation.of(
m2, Instant.now(), REMOTE, null, m2.getFrom().asBareJid(), null));
final var messages = database.messageDao().getMessagesForTesting(1L);
final var message = Iterables.getOnlyElement(messages);
Assert.assertEquals(1L, message.states.size());
}
@Test
public void messageAndRetraction() throws XmppStringprepException {
final var m1 = new Message();
m1.setTo(ACCOUNT);
m1.setFrom(JidCreate.fullFrom(REMOTE, Resourcepart.from("junit")));
m1.setId("m1");
m1.addExtension(new Body("It is raining outside"));
this.transformer.transform(
MessageTransformation.of(
m1, Instant.now(), REMOTE, null, m1.getFrom().asBareJid(), null));
final var m2 = new Message();
m2.setTo(ACCOUNT);
m2.setFrom(JidCreate.fullFrom(REMOTE, Resourcepart.from("junit")));
m2.addExtension(new Retract()).setId("m1");
this.transformer.transform(
MessageTransformation.of(
m2, Instant.now(), REMOTE, null, m2.getFrom().asBareJid(), null));
final var messages = database.messageDao().getMessagesForTesting(1L);
final var message = Iterables.getOnlyElement(messages);
Assert.assertEquals(Modification.RETRACTION, message.modification);
Assert.assertEquals(
PartType.RETRACTION, Iterables.getOnlyElement(message.contents).partType);
}
@Test
public void twoChatThreeMessages() throws XmppStringprepException {
final var m1 = new Message();
m1.setId("1");
m1.setTo(REMOTE);
m1.setFrom(JidCreate.fullFrom(ACCOUNT, Resourcepart.from("junit")));
m1.addExtension(new Body("Hi. How are you?"));
this.transformer.transform(
MessageTransformation.of(
m1, Instant.now(), REMOTE, null, m1.getFrom().asBareJid(), null));
final var m2 = new Message();
m2.setId("2");
m2.setTo(REMOTE);
m2.setFrom(JidCreate.fullFrom(ACCOUNT, Resourcepart.from("junit")));
m2.addExtension(new Body("Please answer"));
this.transformer.transform(
MessageTransformation.of(
m2, Instant.now(), REMOTE, null, m2.getFrom().asBareJid(), null));
final var m3 = new Message();
m3.setId("3");
m3.setTo(REMOTE_2);
m3.setFrom(JidCreate.fullFrom(ACCOUNT, Resourcepart.from("junit")));
m3.addExtension(new Body("Another message"));
this.transformer.transform(
MessageTransformation.of(
m3, Instant.now(), REMOTE, null, m3.getFrom().asBareJid(), null));
}
}

View file

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:bottom="2dp"
android:end="4dp"
android:start="4dp"
android:top="2dp">
<shape>
<solid android:color="?colorSurfaceVariant" />
<corners android:radius="12dp" />
</shape>
</item>
</layer-list>

View file

@ -1,24 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="1146.7721"
android:viewportHeight="1146.7721">
<group android:translateX="322.69516"
android:translateY="317.38605">
<path
android:pathData="M253.219,17.719C126.144,17.719 22.469,118.884 22.469,243.75C22.469,368.616 126.138,469.844 253.219,469.844C292.739,469.844 323.216,461.736 358,449.094L468.469,493.625A14.556,14.562 0,0 0,488.063 476.625L458.125,355.656C477.356,321.886 483.938,283.416 483.938,243.75C483.938,118.887 380.293,17.719 253.219,17.719zM143.844,222C157.651,222 168.844,233.193 168.844,247C168.844,260.807 157.651,272 143.844,272C130.037,272 118.844,260.807 118.844,247C118.844,233.193 130.037,222 143.844,222zM253.563,222C267.37,222 278.563,233.193 278.563,247C278.563,260.807 267.37,272 253.563,272C239.755,272 228.563,260.807 228.563,247C228.563,233.193 239.755,222 253.563,222zM363.563,222C377.37,222 388.563,233.193 388.563,247C388.563,260.807 377.37,272 363.563,272C349.755,272 338.563,260.807 338.563,247C338.563,233.193 349.755,222 363.563,222z"
android:fillColor="#ffffff"
android:strokeColor="#00000000"
android:fillAlpha="1"/>
<path
android:pathData="M478.641,484.856 L447.361,358.245c19.89,-31.998 26.743,-69.572 26.743,-109.762 0,-116.817 -96.799,-211.484 -216.184,-211.484 -119.384,0 -216.184,94.667 -216.184,211.484 0,116.817 96.799,211.554 216.184,211.554 39.636,0 68.588,-8.142 105.194,-21.761z"
android:strokeAlpha="0"
android:strokeLineJoin="round"
android:strokeWidth="23.55835724"
android:fillColor="#00000000"
android:strokeColor="#000000"
android:fillAlpha="0"
android:strokeLineCap="butt"/>
</group>
</vector>

View file

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@mipmap/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>

View file

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@mipmap/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

View file

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pick_a_server">Vælg din XMPP-udbyder</string>
<string name="use_conversations.im">Brug conversations.im</string>
<string name="create_new_account">Opret ny konto</string>
<string name="do_you_have_an_account">Har du allerede en XMPP-konto? Dette kan være tilfældet, hvis du allerede bruger en anden XMPP-klient eller har brugt Conversations før. Hvis ikke, kan du lige nu oprette en ny XMPP-konto.\nTip: Nogle e-mail-udbydere leverer også XMPP-konti.</string>
<string name="server_select_text">XMPP er et udbyderuafhængigt onlinemeddelelsesnetværk. Du kan bruge denne klient med hvilken XMPP-server du end vælger.\nMen for din nemhedsskyld har vi gjort vi det let at oprette en konto på conversations.im; en udbyder, der er specielt velegnet til brug med Conversations.</string>
<string name="magic_create_text_on_x">Du er blevet inviteret til %1$s. Vi guider dig gennem processen med at oprette en konto.\nNår du vælger %1$s som udbyder, kan du kommunikere med brugere fra andre udbydere ved at give dem din fulde XMPP-adresse.</string>
<string name="magic_create_text_fixed">Du er blevet inviteret til %1$s. Der er allerede valgt et brugernavn til dig. Vi guider dig gennem processen med at oprette en konto.\nDu vil være i stand til at kommunikere med brugere fra andre udbydere ved at give dem din fulde XMPP-adresse.</string>
<string name="your_server_invitation">Din server invitation</string>
<string name="improperly_formatted_provisioning">Forkert formateret klargøringskode</string>
<string name="tap_share_button_send_invite">Tryk på deleknappen for at sende din kontakt en invitation til %1$s.</string>
<string name="if_contact_is_nearby_use_qr">Hvis din kontakt er i nærheden, kan de også skanne koden nedenfor for at acceptere din invitation.</string>
<string name="easy_invite_share_text">Deltag med %1$s og chat med mig: %2$s</string>
<string name="share_invite_with">Del invitation med...</string>
</resources>

View file

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pick_a_server">Επιλέξτε τον πάροχο XMPP σας</string>
<string name="use_conversations.im">Χρήση του conversations.im</string>
<string name="create_new_account">Δημιουργία νέου λογαριασμού</string>
<string name="do_you_have_an_account">Έχετε ήδη λογαριασμό XMPP; Αυτό μπορεί να συμβαίνει αν ήδη χρησιμοποιείτε ένα άλλο πρόγραμμα XMPP ή έχετε χρησιμοποιήσει το Conversations παλιότερα. Αν όχι, μπορείτε να δημιουργήσετε ένα νέο λογαριασμό XMPP τώρα.\nΧρήσιμη πληροφορία: Κάποιοι πάροχοι e-mail παρέχουν επίσης και λογαριασμούς XMPP.</string>
<string name="server_select_text">Το XMPP είναι ένα δίκτυο άμεσης ανταλλαγής μηνυμάτων ανεξάρτητο παρόχου. Μπορείτε να χρησιμοποιήσετε αυτό το πρόγραμμα με όποιον διακομιστή XMPP επιθυμείτε.\nΓια διευκόλυνση πάντως μπορείτε να δημιουργήσετε έναν λογαριασμό στο conversations.im, έναν πάροχο ειδικά σχεδιασμένο για χρήση με το Conversations.</string>
<string name="magic_create_text_on_x">Έχετε προσκληθεί στο %1$s. Θα σας καθοδηγήσουμε στη διαδικασία δημιουργίας λογαριασμού.\nΕπιλέγοντας τον %1$s ως πάροχο θα μπορείτε να επικοινωνείτε με χρήστες άλλων παρόχων δίνοντάς τους την πλήρη διεύθυνση XMPP σας.</string>
<string name="magic_create_text_fixed">Έχετε προσκληθεί στο %1$s. Ένα όνομα χρήστη έχει ήδη επιλεγεί για εσάς. Θα σας καθοδηγήσουμε στη διαδικασία δημιουργίας λογαριασμού.\nΘα μπορείτε να επικοινωνείτε με χρήστες άλλων παρόχων δίνοντάς τους την πλήρη διεύθυνση XMPP σας.</string>
<string name="your_server_invitation">Η πρόσκλησή σας στον διακομιστή</string>
<string name="improperly_formatted_provisioning">Λάθος μορφοποίηση κώδικα παροχής</string>
<string name="tap_share_button_send_invite">Πατήστε το πλήκτρο διαμοιρασμού για να στείλετε στην επαφή σας μια πρόσκληση στο %1$s.</string>
<string name="if_contact_is_nearby_use_qr">Αν η επαφή σας βρίσκεται κοντά σας, μπορεί επίσης να σαρώσει τον κωδικό παρακάτω για να αποδεχτεί την πρόσκλησή σας.</string>
<string name="easy_invite_share_text">Μπείτε στο %1$s και συνομιλήστε μαζί μου: %2$s</string>
<string name="share_invite_with">Διαμοιρασμός πρόσκλησης με...</string>
</resources>

View file

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pick_a_server">Choisissez votre fournisseur XMPP</string>
<string name="use_conversations.im">Utiliser conversations.im</string>
<string name="create_new_account">Créer un nouveau compte</string>
<string name="do_you_have_an_account">Avez-vous déjà un compte XMPP? Cela peut être le cas si vous utilisez déjà un autre client XMPP ou si vous avez déjà utilisé Conversations auparavant. Sinon, vous pouvez créer un nouveau compte XMPP dès maintenant.\nRemarque : Certains fournisseurs de messagerie proposent également des comptes XMPP.</string>
<string name="server_select_text">XMPP est un réseau de messagerie instantanée indépendant du fournisseur. Vous pouvez utiliser ce client avec nimporte quel serveur XMPP de votre choix.\nToutefois, pour votre commodité, nous avons facilité la création dun compte sur conversations.im; un fournisseur spécialement conçu pour Conversations.</string>
<string name="magic_create_text_on_x">Vous avez été invité à %1$s. Nous allons vous guider à travers le processus de création dun compte.\nEn choisissant %1$s comme fournisseur, vous pourrez communiquer avec les utilisateurs des autres fournisseurs en leur donnant votre adresse XMPP complète.</string>
<string name="magic_create_text_fixed">Vous avez été invité à %1$s. Un nom dutilisateur a déjà été choisi pour vous. Nous allons vous guider à travers le processus de création dun compte.\nVous pourrez communiquer avec les utilisateurs des autres fournisseurs en leur donnant votre adresse XMPP complète.</string>
<string name="your_server_invitation">Votre invitation au serveur</string>
<string name="improperly_formatted_provisioning">Code de provisionnement mal formaté</string>
<string name="tap_share_button_send_invite">Appuyez sur le bouton partager pour envoyer à votre contact une invitation pour %1$s</string>
<string name="if_contact_is_nearby_use_qr">Si vos contacts sont à votre côté, ils peuvent aussi scanner le code ci dessous pour accepter votre invitation</string>
<string name="easy_invite_share_text">Rejoignez %1$set discutez avec moi : %2$s</string>
<string name="share_invite_with">Partager une invitation avec ...</string>
</resources>

View file

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pick_a_server">Vyberte si svojho XMPP poskytovateľa</string>
<string name="use_conversations.im">Použiť conversations.im</string>
<string name="create_new_account">Vytvoriť nové konto</string>
<string name="do_you_have_an_account">Máte už svoje XMPP konto? Môže to tak byť v prípade, že už používate iného klienta XMPP alebo ste predtým používali Conversations. Ak nie, môžete si vytvoriť nové XMPP konto práve teraz.\nHint: Niektorí poskytovatelia emailu zároveň poskytujú aj XMPP kontá.</string>
<string name="server_select_text">XMPP je sieť pre okamžité správy nezávislá od poskytovateľa. Tohto klienta môžete používať s akýmkoľvek XMPP serverom, ktorý si vyberiete..\nAvšak pre vaše pohodlie sme zjednodušili vytvorenie konta na conversations.im; poskytovateľ špeciálne vhodný na používanie s Conversations.</string>
<string name="magic_create_text_on_x">Boli ste pozvaný do %1$s. Prevedieme vás procesom vytvorenia konta..\nPo výbere %1$s ako poskytovateľa, budete môcť komunikovať s užívateľmi iných poskytovateľov tak, že im dáte vašu úplnú XMPP adresu.</string>
<string name="magic_create_text_fixed">Boli ste pozvaný do %1$s . Užívateľské meno vám už bolo vopred vybrané. Prevedieme vás procesom vytvorenia konta..\nBudete môcť komunikovať s užívateľmi iných poskytovateľov tak, že im dáte vašu úplnú XMPP adresu.</string>
<string name="tap_share_button_send_invite">Ťuknite na tlačidlo zdieľať na odoslanie pozvánky do %1$s vášmu kontaktu.</string>
<string name="if_contact_is_nearby_use_qr">Ak je váš kontakt blízko, na prijatie vašej pozvánky si môže nasnímať kód nižšie.</string>
<string name="easy_invite_share_text">Pripojte sa k %1$sa rozprávajte sa so mnou: %2$s</string>
<string name="share_invite_with">Zdieľať pozvánku s...</string>
</resources>

View file

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pick_a_server">XMPP sağlayıcınızı seçin</string>
<string name="use_conversations.im">conversations.im kullan</string>
<string name="create_new_account">Yeni hesap oluştur</string>
<string name="do_you_have_an_account">Zaten bir XMPP hesabınız var mı? Bunun sebebi, zaten başka bir XMPP istemcisi kullanıyor oluşunuz veya Conversations\'ı önceden kullanmış olmanız olabilir. Eğer durum bu değilse şimdi yeni bir XMPP hesabı oluşturabilirsiniz.\nİpucu: Bağzı e-posta sağlayıcıları da XMPP hesapları kullanabilir.</string>
<string name="server_select_text">XMPP; anlık yazışmalar için bağımsız bir sağlayıcıdır. Bu istemciyi istediğiniz herhangi bir XMPP sunucusu ile birlikte kullanabilirsiniz.\nAncak kullanım rahatlığı adına sizin için conversations.im; Conversations için özellikle tasarlanmış bir sağlayıcıda hesap açmanızı kolaylaştırdık.</string>
<string name="magic_create_text_on_x">%1$s sağlayıcısına davet edildiniz. Sizi hesap oluşturulması konusunda yönlendireceğiz.\n%1$s bir sağlayıcı olark seçildiğinde, başka sağlayıcılar kullanan kullanıcılarla, onlara tam XMPP adresinizi vererek iletişim kurabileceksiniz.</string>
<string name="magic_create_text_fixed">%1$s sağlayıcısına davet edildiniz. Sizin için zaten bir kullanıcı adı seçildi. Sizi hesap oluşturulması konusunda yönlendireceğiz.\nBaşka sağlayıcılar kullanan kullanıcılarla, onlara tam XMPP adresinizi vererek iletişim kurabileceksiniz.</string>
<string name="your_server_invitation">Sunucu davetiyeniz</string>
<string name="improperly_formatted_provisioning">Yanlış ayarlanmış düzenleme kodu</string>
<string name="tap_share_button_send_invite">Kişinize, %1$s grubuna davet etmek için Paylaş düğmesine basın.</string>
<string name="if_contact_is_nearby_use_qr">Kişiniz yakınınızda ise, aşağıdaki kodu tarayak daveti kabul edebilirler.</string>
<string name="easy_invite_share_text">%1$s grubuna katıl ve benimle sohpet et: %2$s</string>
<string name="share_invite_with">Daveti şununla paylaş...</string>
</resources>

View file

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pick_a_server">Виберіть постачальника послуг обміну повідомленнями XMPP</string>
<string name="use_conversations.im">Скористатися conversations.im</string>
<string name="create_new_account">Створити новий обліковий запис</string>
<string name="do_you_have_an_account">Вже маєте обліковий запис XMPP? Можливо, користуєтеся іншою програмою XMPP або користувалися цією програмою раніше. Якщо ні, можете створити новий обліковий запис XMPP просто зараз.\nЗверніть увагу, що деякі постачальники електронної пошти у той же час надають облікові записи XMPP.</string>
<string name="server_select_text">XMPP — це мережа обміну повідомленнями, незалежна від постачальників. Можете використовувати цю програму з будь-яким XMPP сервером, який оберете.\nПроте, для зручності, ми спростили створення облікового запису на conversations.im — у постачальника, який спеціально налаштований на роботу з цією програмою.</string>
<string name="magic_create_text_on_x">Вас запросили до %1$s. Ми проведемо вас крок за кроком, щоб створити обліковий запис.\nОбираючи %1$s в якості свого постачальника, ви зможете спілкуватися з користувачами інших постачальників, для цього повідомте їм свою повну адресу XMPP.</string>
<string name="magic_create_text_fixed">Вас запросили до %1$s. Для вас створено ім\'я користувача. Ми проведемо вас крок за кроком, щоб створити обліковий запис.\nВи зможете спілкуватися з користувачами інших постачальників, для цього повідомите їм свою повну адресу XMPP.</string>
<string name="your_server_invitation">Ваше запрошення до сервера</string>
<string name="improperly_formatted_provisioning">Неправильно відформатований код забезпечення</string>
</resources>

View file

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pick_a_server">Chọn nhà cung cấp XMPP của bạn</string>
<string name="use_conversations.im">Sử dụng conversations.im</string>
<string name="create_new_account">Tạo tài khoản mới</string>
<string name="do_you_have_an_account">Bạn đã có tài khoản XMPP chưa? Điều này có thể đúng nếu bạn đang dùng một ứng dụng khách cho XMPP khác hoặc đã sử dụng Conversations trước đó. Nếu không, bạn có thể tạo tài khoản XMPP mới ngay bây giờ.\nGợi ý: Một số nhà cung cấp email cũng cung cấp tài khoản XMPP.</string>
<string name="server_select_text">XMPP là một mạng nhắn tin ngay lập tức không phụ thuộc vào nhà cung cấp. Bạn có thể sử dụng ứng dụng khách này với bất kỳ máy chủ XMPP nào mà bạn chọn.\nTuy nhiên, vì sự thuận tiện của bạn, chúng tôi đã làm cho việc tạo tài khoản trên conversations.im được dễ dàng; một nhà cung cấp đặc biệt phù hợp với việc sử dụng Conversations.</string>
<string name="magic_create_text_on_x">Bạn đã được mời vào %1$s. Chúng tôi sẽ hướng dẫn bạn trong quá trình tạo tài khoản.\nKhi chọn %1$s là nhà cung cấp, bạn sẽ có thể giao tiếp với những người dùng của các nhà cung cấp khác bằng cách đưa cho họ địa chỉ XMPP đầy đủ của bạn.</string>
<string name="magic_create_text_fixed">Bạn đã được mời vào %1$s. Một tên người dùng đã được chọn sẵn cho bạn. Chúng tôi sẽ hướng dẫn bạn trong quá trình tạo tài khoản.\nBạn sẽ có thể giao tiếp với những người dùng của các nhà cung cấp khác bằng cách đưa cho họ địa chỉ XMPP đầy đủ của bạn.</string>
<string name="your_server_invitation">Lời mời vào máy chủ của bạn</string>
<string name="improperly_formatted_provisioning">Mã cung cấp không được định dạng đúng</string>
<string name="tap_share_button_send_invite">Nhấn nút chia sẻ để gửi lời mời vào %1$s đến liên hệ của bạn.</string>
<string name="if_contact_is_nearby_use_qr">Nếu liên hệ của bạn ở gần đây, họ cũng có thể quét mã ở dưới để chấp nhận lời mời của bạn.</string>
<string name="easy_invite_share_text">Hãy tham gia vào %1$s và trò chuyện với tôi: %2$s</string>
<string name="share_invite_with">Chia sẻ lời mời với...</string>
</resources>

View file

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pick_a_server">选择您的 XMPP 提供者</string>
<string name="use_conversations.im">使用 conversations.im</string>
<string name="create_new_account">创建新账户</string>
<string name="do_you_have_an_account">您有 XMPP 账户吗?如果您之前使用过其他的 XMPP 客户端,那么您已经拥有这种账户了。如果没有的话,您现在可以创建一个。\n提示有些电子邮件服务也提供XMPP账户。</string>
<string name="server_select_text">XMPP 是独立于提供者的即时消息网络。您可以将此客户端与任意 XMPP 服务器一同使用。\n不过您可以很容易地在 conversations.im 上创建账户它是特别适合与“Conversations”一起使用的提供者。</string>
<string name="magic_create_text_on_x">您已受邀加入 %1$s。我们将指导您完成创建帐户的过程。\n使用 %1$s 作为提供者时,您可以通过您的完整 XMPP 地址与其他提供者的用户进行交流。</string>
<string name="magic_create_text_fixed">您已受邀加入 %1$s。已为您选择了一个用户名。我们将指导您完成创建帐户的过程。\n您可以使用完整的 XMPP 地址来与其他提供者的用户进行交流。</string>
<string name="your_server_invitation">你的服务器邀请</string>
<string name="improperly_formatted_provisioning">格式不正确的配置代码</string>
<string name="tap_share_button_send_invite">点击分享按钮向您的联系人发送加入 %1$s 的邀请。</string>
<string name="if_contact_is_nearby_use_qr">如果你的联系人在附近,他们也可以扫描下面的代码来接受你的邀请。</string>
<string name="easy_invite_share_text">加入 %1$s 和我聊天:%2$s</string>
<string name="share_invite_with">分享邀请…</string>
</resources>

View file

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pick_a_server">挑選您的 XMPP 提供者</string>
<string name="use_conversations.im">使用 conversations.im</string>
<string name="create_new_account">建立新帳戶</string>
<string name="do_you_have_an_account">您已經擁有一個 XMPP 賬戶嗎?如果您之前使用過其他 XMPP 客戶端或 Conversations 的話,那麼您已經擁有 XMPP 賬戶了。如果沒有賬戶的話,您可以現在建立一個。\n提示有些電子郵件服務供應商也會提供 XMPP 賬戶。</string>
<string name="server_select_text">XMPP 是提供者無關的即時訊息網絡。 任何你選擇的 XMPP 伺服器都可在此客戶端上使用。\n不過我們令它在 Coversations.im 中建立帳戶變得更方便; Conversations.im 是特別適合 Conversations 的提供者</string>
<string name="magic_create_text_on_x">你已受邀參加 %1$s 。 我們將指導你完成建立帳戶的過程。選擇 %1$s 作爲提供者後,你可以將你完整的 XMPP 地址交給使用其他提供者的用戶,你便能與他們交流。</string>
<string name="magic_create_text_fixed">您已被邀請參加 %1$s 。 我們已經爲你選擇了一個用戶名。 我們將指導你完成建立帳戶的過程。\n將你完整的 XMPP 地址交給使用其他提供者的用戶後,你便能與他們交流。</string>
<string name="your_server_invitation">您的伺服器邀請</string>
<string name="improperly_formatted_provisioning">配置代碼格式不正確</string>
<string name="tap_share_button_send_invite">輕觸分享按鍵向您的聯絡人發送加入 %1$s 的邀請。</string>
<string name="if_contact_is_nearby_use_qr">如果你的聯絡人就在附近,他們也可以掃描下面的代碼來接受你的邀請。</string>
<string name="easy_invite_share_text">加入 %1$s 和我聊天:%2$s</string>
<string name="share_invite_with">分享邀請到...</string>
</resources>

View file

@ -1,120 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_PROFILE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-feature
android:name="android.hardware.location"
android:required="false" />
<uses-feature
android:name="android.hardware.location.gps"
android:required="false" />
<uses-feature
android:name="android.hardware.location.network"
android:required="false" />
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<uses-feature
android:name="android.hardware.camera.autofocus"
android:required="false" />
<uses-feature
android:name="android.hardware.microphone"
android:required="false" />
<queries>
<package android:name="org.torproject.android" />
<intent>
<action android:name="eu.siacs.conversations.location.request" />
</intent>
<intent>
<action android:name="eu.siacs.conversations.location.show" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:mimeType="resource/folder" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
</intent>
<intent>
<action android:name="org.unifiedpush.android.connector.MESSAGE" />
</intent>
</queries>
<application
android:name="im.conversations.android.Conversations"
android:allowBackup="true"
android:appCategory="social"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/new_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/new_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Conversations3"
tools:targetApi="31">
<service
android:name=".service.ForegroundService"
android:exported="false"/>
<service
android:name=".service.RtpSessionService"
android:exported="false"
android:foregroundServiceType="phoneCall" />
<receiver
android:name=".receiver.EventReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.ACTION_SHUTDOWN" />
<action android:name="android.media.RINGER_MODE_CHANGED" />
</intent-filter>
</receiver>
<activity
android:name="im.conversations.android.ui.activity.MainActivity"
android:exported="true"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="im.conversations.android.ui.activity.SettingsActivity" />
<activity
android:name="im.conversations.android.ui.activity.SetupActivity"
android:windowSoftInputMode="adjustResize" />
<activity
android:name=".ui.activity.RtpSessionActivity"
android:autoRemoveFromRecents="true"
android:launchMode="singleInstance"
android:supportsPictureInPicture="true" />
</application>
</manifest>

View file

@ -1,16 +0,0 @@
<configuration xmlns="https://tony19.github.io/logback-android/xml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://tony19.github.io/logback-android/xml https://cdn.jsdelivr.net/gh/tony19/logback-android/logback.xsd">
<appender name="logcat" class="ch.qos.logback.classic.android.LogcatAppender">
<tagEncoder>
<pattern>conversations</pattern>
</tagEncoder>
<encoder>
<pattern>%logger{12}: %msg</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="logcat" />
</root>
</configuration>

View file

@ -1,10 +0,0 @@
package eu.siacs.conversations;
import android.net.Uri;
public class Config {
public static final String LOGTAG = "conversations";
public static final Uri HELP = Uri.parse("https://help.conversations.im");
public static final boolean REQUIRE_RTP_VERIFICATION =
false; // require a/v calls to be verified with OMEMO
}

View file

@ -1,54 +0,0 @@
package eu.siacs.conversations.generator;
import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection;
import eu.siacs.conversations.xmpp.jingle.Media;
import im.conversations.android.xml.Element;
import im.conversations.android.xml.Namespace;
import im.conversations.android.xmpp.manager.JingleConnectionManager;
import im.conversations.android.xmpp.model.stanza.Message;
import org.jxmpp.jid.Jid;
public final class MessageGenerator {
private MessageGenerator() {
throw new IllegalStateException("Do not instantiate me");
}
public static Message sessionProposal(
final JingleConnectionManager.RtpSessionProposal proposal) {
final Message packet = new Message(Message.Type.CHAT); // we want to carbon copy those
packet.setTo(proposal.with);
packet.setId(JingleRtpConnection.JINGLE_MESSAGE_PROPOSE_ID_PREFIX + proposal.sessionId);
final Element propose = packet.addChild("propose", Namespace.JINGLE_MESSAGE);
propose.setAttribute("id", proposal.sessionId);
for (final Media media : proposal.media) {
propose.addChild("description", Namespace.JINGLE_APPS_RTP)
.setAttribute("media", media.toString());
}
packet.addChild("request", "urn:xmpp:receipts");
packet.addChild("store", "urn:xmpp:hints");
return packet;
}
public static Message sessionRetract(
final JingleConnectionManager.RtpSessionProposal proposal) {
final Message packet = new Message(Message.Type.CHAT); // we want to carbon copy those
packet.setTo(proposal.with);
final Element propose = packet.addChild("retract", Namespace.JINGLE_MESSAGE);
propose.setAttribute("id", proposal.sessionId);
propose.addChild("description", Namespace.JINGLE_APPS_RTP);
packet.addChild("store", "urn:xmpp:hints");
return packet;
}
public static Message sessionReject(final Jid with, final String sessionId) {
final Message packet = new Message(Message.Type.CHAT); // we want to carbon copy those
packet.setTo(with);
final Element propose = packet.addChild("reject", Namespace.JINGLE_MESSAGE);
propose.setAttribute("id", sessionId);
propose.addChild("description", Namespace.JINGLE_APPS_RTP);
packet.addChild("store", "urn:xmpp:hints");
return packet;
}
}

View file

@ -1,660 +0,0 @@
/*
* Copyright 2014 The WebRTC Project Authors. All rights reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
package eu.siacs.conversations.services;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.media.AudioDeviceInfo;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Build;
import android.util.Log;
import androidx.annotation.Nullable;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.utils.AppRTCUtils;
import eu.siacs.conversations.xmpp.jingle.Media;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import org.webrtc.ThreadUtils;
/** AppRTCAudioManager manages all audio related parts of the AppRTC demo. */
public class AppRTCAudioManager {
private static CountDownLatch microphoneLatch;
private final Context apprtcContext;
// Contains speakerphone setting: auto, true or false
@Nullable private SpeakerPhonePreference speakerPhonePreference;
// Handles all tasks related to Bluetooth headset devices.
private final AppRTCBluetoothManager bluetoothManager;
@Nullable private final AudioManager audioManager;
@Nullable private AudioManagerEvents audioManagerEvents;
private AudioManagerState amState;
private boolean savedIsSpeakerPhoneOn;
private boolean savedIsMicrophoneMute;
private boolean hasWiredHeadset;
// Default audio device; speaker phone for video calls or earpiece for audio
// only calls.
private AudioDevice defaultAudioDevice;
// Contains the currently selected audio device.
// This device is changed automatically using a certain scheme where e.g.
// a wired headset "wins" over speaker phone. It is also possible for a
// user to explicitly select a device (and overrid any predefined scheme).
// See |userSelectedAudioDevice| for details.
private AudioDevice selectedAudioDevice;
// Contains the user-selected audio device which overrides the predefined
// selection scheme.
// TODO(henrika): always set to AudioDevice.NONE today. Add support for
// explicit selection based on choice by userSelectedAudioDevice.
private AudioDevice userSelectedAudioDevice;
// Proximity sensor object. It measures the proximity of an object in cm
// relative to the view screen of a device and can therefore be used to
// assist device switching (close to ear <=> use headset earpiece if
// available, far from ear <=> use speaker phone).
@Nullable private AppRTCProximitySensor proximitySensor;
// Contains a list of available audio devices. A Set collection is used to
// avoid duplicate elements.
private Set<AudioDevice> audioDevices = new HashSet<>();
// Broadcast receiver for wired headset intent broadcasts.
private final BroadcastReceiver wiredHeadsetReceiver;
// Callback method for changes in audio focus.
@Nullable private AudioManager.OnAudioFocusChangeListener audioFocusChangeListener;
private AppRTCAudioManager(
Context context, final SpeakerPhonePreference speakerPhonePreference) {
Log.d(Config.LOGTAG, "ctor");
ThreadUtils.checkIsOnMainThread();
apprtcContext = context;
audioManager = ((AudioManager) context.getSystemService(Context.AUDIO_SERVICE));
bluetoothManager = AppRTCBluetoothManager.create(context, this);
wiredHeadsetReceiver = new WiredHeadsetReceiver();
amState = AudioManagerState.UNINITIALIZED;
this.speakerPhonePreference = speakerPhonePreference;
if (speakerPhonePreference == SpeakerPhonePreference.EARPIECE && hasEarpiece()) {
defaultAudioDevice = AudioDevice.EARPIECE;
} else {
defaultAudioDevice = AudioDevice.SPEAKER_PHONE;
}
// Create and initialize the proximity sensor.
// Tablet devices (e.g. Nexus 7) does not support proximity sensors.
// Note that, the sensor will not be active until start() has been called.
proximitySensor =
AppRTCProximitySensor.create(
context,
// This method will be called each time a state change is detected.
// Example: user holds his hand over the device (closer than ~5 cm),
// or removes his hand from the device.
this::onProximitySensorChangedState);
Log.d(Config.LOGTAG, "defaultAudioDevice: " + defaultAudioDevice);
AppRTCUtils.logDeviceInfo(Config.LOGTAG);
}
public void switchSpeakerPhonePreference(final SpeakerPhonePreference speakerPhonePreference) {
this.speakerPhonePreference = speakerPhonePreference;
if (speakerPhonePreference == SpeakerPhonePreference.EARPIECE && hasEarpiece()) {
defaultAudioDevice = AudioDevice.EARPIECE;
} else {
defaultAudioDevice = AudioDevice.SPEAKER_PHONE;
}
updateAudioDeviceState();
}
/** Construction. */
public static AppRTCAudioManager create(
Context context, SpeakerPhonePreference speakerPhonePreference) {
return new AppRTCAudioManager(context, speakerPhonePreference);
}
public static boolean isMicrophoneAvailable() {
microphoneLatch = new CountDownLatch(1);
AudioRecord audioRecord = null;
boolean available = true;
try {
final int sampleRate = 44100;
final int channel = AudioFormat.CHANNEL_IN_MONO;
final int format = AudioFormat.ENCODING_PCM_16BIT;
final int bufferSize = AudioRecord.getMinBufferSize(sampleRate, channel, format);
audioRecord =
new AudioRecord(
MediaRecorder.AudioSource.MIC, sampleRate, channel, format, bufferSize);
audioRecord.startRecording();
final short[] buffer = new short[bufferSize];
final int audioStatus = audioRecord.read(buffer, 0, bufferSize);
if (audioStatus == AudioRecord.ERROR_INVALID_OPERATION
|| audioStatus == AudioRecord.STATE_UNINITIALIZED) available = false;
} catch (Exception e) {
available = false;
} finally {
release(audioRecord);
}
microphoneLatch.countDown();
return available;
}
private static void release(final AudioRecord audioRecord) {
if (audioRecord == null) {
return;
}
try {
audioRecord.release();
} catch (Exception e) {
// ignore
}
}
/**
* This method is called when the proximity sensor reports a state change, e.g. from "NEAR to
* FAR" or from "FAR to NEAR".
*/
private void onProximitySensorChangedState() {
if (speakerPhonePreference != SpeakerPhonePreference.AUTO) {
return;
}
// The proximity sensor should only be activated when there are exactly two
// available audio devices.
if (audioDevices.size() == 2
&& audioDevices.contains(AppRTCAudioManager.AudioDevice.EARPIECE)
&& audioDevices.contains(AppRTCAudioManager.AudioDevice.SPEAKER_PHONE)) {
if (proximitySensor.sensorReportsNearState()) {
// Sensor reports that a "handset is being held up to a person's ear",
// or "something is covering the light sensor".
setAudioDeviceInternal(AppRTCAudioManager.AudioDevice.EARPIECE);
} else {
// Sensor reports that a "handset is removed from a person's ear", or
// "the light sensor is no longer covered".
setAudioDeviceInternal(AppRTCAudioManager.AudioDevice.SPEAKER_PHONE);
}
}
}
@SuppressWarnings("deprecation")
public void start(AudioManagerEvents audioManagerEvents) {
Log.d(Config.LOGTAG, AppRTCAudioManager.class.getName() + ".start()");
ThreadUtils.checkIsOnMainThread();
if (amState == AudioManagerState.RUNNING) {
Log.e(Config.LOGTAG, "AudioManager is already active");
return;
}
awaitMicrophoneLatch();
this.audioManagerEvents = audioManagerEvents;
amState = AudioManagerState.RUNNING;
// Store current audio state so we can restore it when stop() is called.
savedIsSpeakerPhoneOn = audioManager.isSpeakerphoneOn();
savedIsMicrophoneMute = audioManager.isMicrophoneMute();
hasWiredHeadset = hasWiredHeadset();
// Create an AudioManager.OnAudioFocusChangeListener instance.
audioFocusChangeListener =
new AudioManager.OnAudioFocusChangeListener() {
// Called on the listener to notify if the audio focus for this listener has
// been changed.
// The |focusChange| value indicates whether the focus was gained, whether the
// focus was lost,
// and whether that loss is transient, or whether the new focus holder will hold
// it for an
// unknown amount of time.
// TODO(henrika): possibly extend support of handling audio-focus changes. Only
// contains
// logging for now.
@Override
public void onAudioFocusChange(int focusChange) {
final String typeOfChange;
switch (focusChange) {
case AudioManager.AUDIOFOCUS_GAIN:
typeOfChange = "AUDIOFOCUS_GAIN";
break;
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT:
typeOfChange = "AUDIOFOCUS_GAIN_TRANSIENT";
break;
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE:
typeOfChange = "AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE";
break;
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
typeOfChange = "AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK";
break;
case AudioManager.AUDIOFOCUS_LOSS:
typeOfChange = "AUDIOFOCUS_LOSS";
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
typeOfChange = "AUDIOFOCUS_LOSS_TRANSIENT";
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
typeOfChange = "AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK";
break;
default:
typeOfChange = "AUDIOFOCUS_INVALID";
break;
}
Log.d(Config.LOGTAG, "onAudioFocusChange: " + typeOfChange);
}
};
// Request audio playout focus (without ducking) and install listener for changes in focus.
int result =
audioManager.requestAudioFocus(
audioFocusChangeListener,
AudioManager.STREAM_VOICE_CALL,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
Log.d(Config.LOGTAG, "Audio focus request granted for VOICE_CALL streams");
} else {
Log.e(Config.LOGTAG, "Audio focus request failed");
}
// Start by setting MODE_IN_COMMUNICATION as default audio mode. It is
// required to be in this mode when playout and/or recording starts for
// best possible VoIP performance.
audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
// Always disable microphone mute during a WebRTC call.
setMicrophoneMute(false);
// Set initial device states.
userSelectedAudioDevice = AudioDevice.NONE;
selectedAudioDevice = AudioDevice.NONE;
audioDevices.clear();
// Initialize and start Bluetooth if a BT device is available or initiate
// detection of new (enabled) BT devices.
bluetoothManager.start();
// Do initial selection of audio device. This setting can later be changed
// either by adding/removing a BT or wired headset or by covering/uncovering
// the proximity sensor.
updateAudioDeviceState();
// Register receiver for broadcast intents related to adding/removing a
// wired headset.
registerReceiver(wiredHeadsetReceiver, new IntentFilter(Intent.ACTION_HEADSET_PLUG));
Log.d(Config.LOGTAG, "AudioManager started");
}
private void awaitMicrophoneLatch() {
final CountDownLatch latch = microphoneLatch;
if (latch == null) {
return;
}
try {
latch.await();
} catch (InterruptedException e) {
// ignore
}
}
@SuppressWarnings("deprecation")
public void stop() {
Log.d(Config.LOGTAG, AppRTCAudioManager.class.getName() + ".stop()");
ThreadUtils.checkIsOnMainThread();
if (amState != AudioManagerState.RUNNING) {
Log.e(Config.LOGTAG, "Trying to stop AudioManager in incorrect state: " + amState);
return;
}
amState = AudioManagerState.UNINITIALIZED;
unregisterReceiver(wiredHeadsetReceiver);
bluetoothManager.stop();
// Restore previously stored audio states.
setSpeakerphoneOn(savedIsSpeakerPhoneOn);
setMicrophoneMute(savedIsMicrophoneMute);
audioManager.setMode(AudioManager.MODE_NORMAL);
// Abandon audio focus. Gives the previous focus owner, if any, focus.
audioManager.abandonAudioFocus(audioFocusChangeListener);
audioFocusChangeListener = null;
Log.d(Config.LOGTAG, "Abandoned audio focus for VOICE_CALL streams");
if (proximitySensor != null) {
proximitySensor.stop();
proximitySensor = null;
}
audioManagerEvents = null;
}
/** Changes selection of the currently active audio device. */
private void setAudioDeviceInternal(AudioDevice device) {
Log.d(Config.LOGTAG, "setAudioDeviceInternal(device=" + device + ")");
AppRTCUtils.assertIsTrue(audioDevices.contains(device));
switch (device) {
case SPEAKER_PHONE:
setSpeakerphoneOn(true);
break;
case EARPIECE:
case WIRED_HEADSET:
case BLUETOOTH:
setSpeakerphoneOn(false);
break;
default:
Log.e(Config.LOGTAG, "Invalid audio device selection");
break;
}
selectedAudioDevice = device;
}
/**
* Changes default audio device. TODO(henrika): add usage of this method in the AppRTCMobile
* client.
*/
public void setDefaultAudioDevice(AudioDevice defaultDevice) {
ThreadUtils.checkIsOnMainThread();
switch (defaultDevice) {
case SPEAKER_PHONE:
defaultAudioDevice = defaultDevice;
break;
case EARPIECE:
if (hasEarpiece()) {
defaultAudioDevice = defaultDevice;
} else {
defaultAudioDevice = AudioDevice.SPEAKER_PHONE;
}
break;
default:
Log.e(Config.LOGTAG, "Invalid default audio device selection");
break;
}
Log.d(Config.LOGTAG, "setDefaultAudioDevice(device=" + defaultAudioDevice + ")");
updateAudioDeviceState();
}
/** Changes selection of the currently active audio device. */
public void selectAudioDevice(AudioDevice device) {
ThreadUtils.checkIsOnMainThread();
if (!audioDevices.contains(device)) {
Log.e(Config.LOGTAG, "Can not select " + device + " from available " + audioDevices);
}
userSelectedAudioDevice = device;
updateAudioDeviceState();
}
/** Returns current set of available/selectable audio devices. */
public Set<AudioDevice> getAudioDevices() {
ThreadUtils.checkIsOnMainThread();
return Collections.unmodifiableSet(new HashSet<>(audioDevices));
}
/** Returns the currently selected audio device. */
public AudioDevice getSelectedAudioDevice() {
ThreadUtils.checkIsOnMainThread();
return selectedAudioDevice;
}
/** Helper method for receiver registration. */
private void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
apprtcContext.registerReceiver(receiver, filter);
}
/** Helper method for unregistration of an existing receiver. */
private void unregisterReceiver(BroadcastReceiver receiver) {
apprtcContext.unregisterReceiver(receiver);
}
/** Sets the speaker phone mode. */
private void setSpeakerphoneOn(boolean on) {
boolean wasOn = audioManager.isSpeakerphoneOn();
if (wasOn == on) {
return;
}
audioManager.setSpeakerphoneOn(on);
}
/** Sets the microphone mute state. */
private void setMicrophoneMute(boolean on) {
boolean wasMuted = audioManager.isMicrophoneMute();
if (wasMuted == on) {
return;
}
audioManager.setMicrophoneMute(on);
}
/** Gets the current earpiece state. */
private boolean hasEarpiece() {
return apprtcContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
}
/**
* Checks whether a wired headset is connected or not. This is not a valid indication that audio
* playback is actually over the wired headset as audio routing depends on other conditions. We
* only use it as an early indicator (during initialization) of an attached wired headset.
*/
@Deprecated
private boolean hasWiredHeadset() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
return audioManager.isWiredHeadsetOn();
} else {
final AudioDeviceInfo[] devices = audioManager.getDevices(AudioManager.GET_DEVICES_ALL);
for (AudioDeviceInfo device : devices) {
final int type = device.getType();
if (type == AudioDeviceInfo.TYPE_WIRED_HEADSET) {
Log.d(Config.LOGTAG, "hasWiredHeadset: found wired headset");
return true;
} else if (type == AudioDeviceInfo.TYPE_USB_DEVICE) {
Log.d(Config.LOGTAG, "hasWiredHeadset: found USB audio device");
return true;
}
}
return false;
}
}
/**
* Updates list of possible audio devices and make new device selection. TODO(henrika): add unit
* test to verify all state transitions.
*/
public void updateAudioDeviceState() {
ThreadUtils.checkIsOnMainThread();
Log.d(
Config.LOGTAG,
"--- updateAudioDeviceState: "
+ "wired headset="
+ hasWiredHeadset
+ ", "
+ "BT state="
+ bluetoothManager.getState());
Log.d(
Config.LOGTAG,
"Device status: "
+ "available="
+ audioDevices
+ ", "
+ "selected="
+ selectedAudioDevice
+ ", "
+ "user selected="
+ userSelectedAudioDevice);
// Check if any Bluetooth headset is connected. The internal BT state will
// change accordingly.
// TODO(henrika): perhaps wrap required state into BT manager.
if (bluetoothManager.getState() == AppRTCBluetoothManager.State.HEADSET_AVAILABLE
|| bluetoothManager.getState() == AppRTCBluetoothManager.State.HEADSET_UNAVAILABLE
|| bluetoothManager.getState() == AppRTCBluetoothManager.State.SCO_DISCONNECTING) {
bluetoothManager.updateDevice();
}
// Update the set of available audio devices.
Set<AudioDevice> newAudioDevices = new HashSet<>();
if (bluetoothManager.getState() == AppRTCBluetoothManager.State.SCO_CONNECTED
|| bluetoothManager.getState() == AppRTCBluetoothManager.State.SCO_CONNECTING
|| bluetoothManager.getState() == AppRTCBluetoothManager.State.HEADSET_AVAILABLE) {
newAudioDevices.add(AudioDevice.BLUETOOTH);
}
if (hasWiredHeadset) {
// If a wired headset is connected, then it is the only possible option.
newAudioDevices.add(AudioDevice.WIRED_HEADSET);
} else {
// No wired headset, hence the audio-device list can contain speaker
// phone (on a tablet), or speaker phone and earpiece (on mobile phone).
newAudioDevices.add(AudioDevice.SPEAKER_PHONE);
if (hasEarpiece()) {
newAudioDevices.add(AudioDevice.EARPIECE);
}
}
// Store state which is set to true if the device list has changed.
boolean audioDeviceSetUpdated = !audioDevices.equals(newAudioDevices);
// Update the existing audio device set.
audioDevices = newAudioDevices;
// Correct user selected audio devices if needed.
if (bluetoothManager.getState() == AppRTCBluetoothManager.State.HEADSET_UNAVAILABLE
&& userSelectedAudioDevice == AudioDevice.BLUETOOTH) {
// If BT is not available, it can't be the user selection.
userSelectedAudioDevice = AudioDevice.NONE;
}
if (hasWiredHeadset && userSelectedAudioDevice == AudioDevice.SPEAKER_PHONE) {
// If user selected speaker phone, but then plugged wired headset then make
// wired headset as user selected device.
userSelectedAudioDevice = AudioDevice.WIRED_HEADSET;
}
if (!hasWiredHeadset && userSelectedAudioDevice == AudioDevice.WIRED_HEADSET) {
// If user selected wired headset, but then unplugged wired headset then make
// speaker phone as user selected device.
userSelectedAudioDevice = AudioDevice.SPEAKER_PHONE;
}
// Need to start Bluetooth if it is available and user either selected it explicitly or
// user did not select any output device.
boolean needBluetoothAudioStart =
bluetoothManager.getState() == AppRTCBluetoothManager.State.HEADSET_AVAILABLE
&& (userSelectedAudioDevice == AudioDevice.NONE
|| userSelectedAudioDevice == AudioDevice.BLUETOOTH);
// Need to stop Bluetooth audio if user selected different device and
// Bluetooth SCO connection is established or in the process.
boolean needBluetoothAudioStop =
(bluetoothManager.getState() == AppRTCBluetoothManager.State.SCO_CONNECTED
|| bluetoothManager.getState()
== AppRTCBluetoothManager.State.SCO_CONNECTING)
&& (userSelectedAudioDevice != AudioDevice.NONE
&& userSelectedAudioDevice != AudioDevice.BLUETOOTH);
if (bluetoothManager.getState() == AppRTCBluetoothManager.State.HEADSET_AVAILABLE
|| bluetoothManager.getState() == AppRTCBluetoothManager.State.SCO_CONNECTING
|| bluetoothManager.getState() == AppRTCBluetoothManager.State.SCO_CONNECTED) {
Log.d(
Config.LOGTAG,
"Need BT audio: start="
+ needBluetoothAudioStart
+ ", "
+ "stop="
+ needBluetoothAudioStop
+ ", "
+ "BT state="
+ bluetoothManager.getState());
}
// Start or stop Bluetooth SCO connection given states set earlier.
if (needBluetoothAudioStop) {
bluetoothManager.stopScoAudio();
bluetoothManager.updateDevice();
}
if (needBluetoothAudioStart && !needBluetoothAudioStop) {
// Attempt to start Bluetooth SCO audio (takes a few second to start).
if (!bluetoothManager.startScoAudio()) {
// Remove BLUETOOTH from list of available devices since SCO failed.
audioDevices.remove(AudioDevice.BLUETOOTH);
audioDeviceSetUpdated = true;
}
}
// Update selected audio device.
final AudioDevice newAudioDevice;
if (bluetoothManager.getState() == AppRTCBluetoothManager.State.SCO_CONNECTED) {
// If a Bluetooth is connected, then it should be used as output audio
// device. Note that it is not sufficient that a headset is available;
// an active SCO channel must also be up and running.
newAudioDevice = AudioDevice.BLUETOOTH;
} else if (hasWiredHeadset) {
// If a wired headset is connected, but Bluetooth is not, then wired headset is used as
// audio device.
newAudioDevice = AudioDevice.WIRED_HEADSET;
} else {
// No wired headset and no Bluetooth, hence the audio-device list can contain speaker
// phone (on a tablet), or speaker phone and earpiece (on mobile phone).
// |defaultAudioDevice| contains either AudioDevice.SPEAKER_PHONE or
// AudioDevice.EARPIECE
// depending on the user's selection.
newAudioDevice = defaultAudioDevice;
}
// Switch to new device but only if there has been any changes.
if (newAudioDevice != selectedAudioDevice || audioDeviceSetUpdated) {
// Do the required device switch.
setAudioDeviceInternal(newAudioDevice);
Log.d(
Config.LOGTAG,
"New device status: "
+ "available="
+ audioDevices
+ ", "
+ "selected="
+ newAudioDevice);
if (audioManagerEvents != null) {
// Notify a listening client that audio device has been changed.
audioManagerEvents.onAudioDeviceChanged(selectedAudioDevice, audioDevices);
}
}
Log.d(Config.LOGTAG, "--- updateAudioDeviceState done");
}
/** AudioDevice is the names of possible audio devices that we currently support. */
public enum AudioDevice {
SPEAKER_PHONE,
WIRED_HEADSET,
EARPIECE,
BLUETOOTH,
NONE
}
/** AudioManager state. */
public enum AudioManagerState {
UNINITIALIZED,
PREINITIALIZED,
RUNNING,
}
public enum SpeakerPhonePreference {
AUTO,
EARPIECE,
SPEAKER;
public static SpeakerPhonePreference of(final Set<Media> media) {
if (media.contains(Media.VIDEO)) {
return SPEAKER;
} else {
return EARPIECE;
}
}
}
/** Selected audio device change event. */
public interface AudioManagerEvents {
// Callback fired once audio device is changed or list of available audio devices changed.
void onAudioDeviceChanged(
AudioDevice selectedAudioDevice, Set<AudioDevice> availableAudioDevices);
}
/* Receiver which handles changes in wired headset availability. */
private class WiredHeadsetReceiver extends BroadcastReceiver {
private static final int STATE_UNPLUGGED = 0;
private static final int STATE_PLUGGED = 1;
private static final int HAS_NO_MIC = 0;
private static final int HAS_MIC = 1;
@Override
public void onReceive(Context context, Intent intent) {
int state = intent.getIntExtra("state", STATE_UNPLUGGED);
int microphone = intent.getIntExtra("microphone", HAS_NO_MIC);
String name = intent.getStringExtra("name");
Log.d(
Config.LOGTAG,
"WiredHeadsetReceiver.onReceive"
+ AppRTCUtils.getThreadInfo()
+ ": "
+ "a="
+ intent.getAction()
+ ", s="
+ (state == STATE_UNPLUGGED ? "unplugged" : "plugged")
+ ", m="
+ (microphone == HAS_MIC ? "mic" : "no mic")
+ ", n="
+ name
+ ", sb="
+ isInitialStickyBroadcast());
hasWiredHeadset = (state == STATE_PLUGGED);
updateAudioDeviceState();
}
}
}

View file

@ -1,66 +0,0 @@
/*
* Copyright 2014 The WebRTC Project Authors. All rights reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
package eu.siacs.conversations.utils;
import android.os.Build;
import android.util.Log;
/** AppRTCUtils provides helper functions for managing thread safety. */
public final class AppRTCUtils {
private AppRTCUtils() {}
/** Helper method which throws an exception when an assertion has failed. */
public static void assertIsTrue(boolean condition) {
if (!condition) {
throw new AssertionError("Expected condition to be true");
}
}
/** Helper method for building a string of thread information. */
public static String getThreadInfo() {
return "@[name="
+ Thread.currentThread().getName()
+ ", id="
+ Thread.currentThread().getId()
+ "]";
}
/** Information about the current build, taken from system properties. */
public static void logDeviceInfo(String tag) {
Log.d(
tag,
"Android SDK: "
+ Build.VERSION.SDK_INT
+ ", "
+ "Release: "
+ Build.VERSION.RELEASE
+ ", "
+ "Brand: "
+ Build.BRAND
+ ", "
+ "Device: "
+ Build.DEVICE
+ ", "
+ "Id: "
+ Build.ID
+ ", "
+ "Hardware: "
+ Build.HARDWARE
+ ", "
+ "Manufacturer: "
+ Build.MANUFACTURER
+ ", "
+ "Model: "
+ Build.MODEL
+ ", "
+ "Product: "
+ Build.PRODUCT);
}
}

View file

@ -1,119 +0,0 @@
package eu.siacs.conversations.xmpp.jingle;
import android.content.Context;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket;
import im.conversations.android.IDs;
import im.conversations.android.xmpp.XmppConnection;
import im.conversations.android.xmpp.model.stanza.Iq;
import org.jxmpp.jid.Jid;
public abstract class AbstractJingleConnection extends XmppConnection.Delegate {
public static final String JINGLE_MESSAGE_PROPOSE_ID_PREFIX = "jm-propose-";
public static final String JINGLE_MESSAGE_PROCEED_ID_PREFIX = "jm-proceed-";
protected final Id id;
private final Jid initiator;
AbstractJingleConnection(
final Context context,
final XmppConnection connection,
final Id id,
final Jid initiator) {
super(context, connection);
this.id = id;
this.initiator = initiator;
}
boolean isInitiator() {
return initiator.equals(connection.getBoundAddress());
}
public abstract void deliverPacket(Iq jinglePacket);
public Id getId() {
return id;
}
public abstract void notifyRebound();
public static class Id implements OngoingRtpSession {
public final Jid with;
public final String sessionId;
private Id(final Jid with, final String sessionId) {
Preconditions.checkNotNull(with);
Preconditions.checkNotNull(sessionId);
this.with = with;
this.sessionId = sessionId;
}
public static Id of(final JinglePacket jinglePacket) {
return new Id(jinglePacket.getFrom(), jinglePacket.getSessionId());
}
public static Id of(Jid with, final String sessionId) {
return new Id(with, sessionId);
}
public static Id of(Jid with) {
return new Id(with, IDs.medium());
}
@Override
public Jid getWith() {
return with;
}
@Override
public String getSessionId() {
return sessionId;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Id id = (Id) o;
return Objects.equal(with, id.with) && Objects.equal(sessionId, id.sessionId);
}
@Override
public int hashCode() {
return Objects.hashCode(with, sessionId);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("with", with)
.add("sessionId", sessionId)
.toString();
}
}
public enum State {
NULL, // default value; nothing has been sent or received yet
PROPOSED,
ACCEPTED,
PROCEED,
REJECTED,
REJECTED_RACED, // used when we want to reject but havent received session init yet
RETRACTED,
RETRACTED_RACED, // used when receiving a retract after we already asked to proceed
SESSION_INITIALIZED, // equal to 'PENDING'
SESSION_INITIALIZED_PRE_APPROVED,
SESSION_ACCEPTED, // equal to 'ACTIVE'
TERMINATED_SUCCESS, // equal to 'ENDED' (after successful call) ui will just close
TERMINATED_DECLINED_OR_BUSY, // equal to 'ENDED' (after other party declined the call)
TERMINATED_CONNECTIVITY_ERROR, // equal to 'ENDED' (but after network failures; ui will
// display retry button)
TERMINATED_CANCEL_OR_TIMEOUT, // more or less the same as retracted; caller pressed end call
// before session was accepted
TERMINATED_APPLICATION_FAILURE,
TERMINATED_SECURITY_ERROR
}
}

View file

@ -1,153 +0,0 @@
package eu.siacs.conversations.xmpp.jingle;
import im.conversations.android.xml.Element;
import java.util.ArrayList;
import java.util.List;
import org.jxmpp.jid.Jid;
public class JingleCandidate {
public static int TYPE_UNKNOWN;
public static int TYPE_DIRECT = 0;
public static int TYPE_PROXY = 1;
private final boolean ours;
private boolean usedByCounterpart = false;
private final String cid;
private String host;
private int port;
private int type;
private Jid jid;
private int priority;
public JingleCandidate(String cid, boolean ours) {
this.ours = ours;
this.cid = cid;
}
public String getCid() {
return cid;
}
public void setHost(String host) {
this.host = host;
}
public String getHost() {
return this.host;
}
public void setJid(final Jid jid) {
this.jid = jid;
}
public Jid getJid() {
return this.jid;
}
public void setPort(int port) {
this.port = port;
}
public int getPort() {
return this.port;
}
public void setType(int type) {
this.type = type;
}
public void setType(String type) {
if (type == null) {
this.type = TYPE_UNKNOWN;
return;
}
switch (type) {
case "proxy":
this.type = TYPE_PROXY;
break;
case "direct":
this.type = TYPE_DIRECT;
break;
default:
this.type = TYPE_UNKNOWN;
break;
}
}
public void setPriority(int i) {
this.priority = i;
}
public int getPriority() {
return this.priority;
}
public boolean equals(JingleCandidate other) {
return this.getCid().equals(other.getCid());
}
public boolean equalValues(JingleCandidate other) {
return other != null
&& other.getHost().equals(this.getHost())
&& (other.getPort() == this.getPort());
}
public boolean isOurs() {
return ours;
}
public int getType() {
return this.type;
}
public static List<JingleCandidate> parse(final List<Element> elements) {
final List<JingleCandidate> candidates = new ArrayList<>();
for (final Element element : elements) {
if ("candidate".equals(element.getName())) {
candidates.add(JingleCandidate.parse(element));
}
}
return candidates;
}
public static JingleCandidate parse(Element element) {
final JingleCandidate candidate = new JingleCandidate(element.getAttribute("cid"), false);
candidate.setHost(element.getAttribute("host"));
candidate.setJid(element.getAttributeAsJid("jid"));
candidate.setType(element.getAttribute("type"));
candidate.setPriority(Integer.parseInt(element.getAttribute("priority")));
candidate.setPort(Integer.parseInt(element.getAttribute("port")));
return candidate;
}
public Element toElement() {
Element element = new Element("candidate");
element.setAttribute("cid", this.getCid());
element.setAttribute("host", this.getHost());
element.setAttribute("port", Integer.toString(this.getPort()));
if (jid != null) {
element.setAttribute("jid", jid);
}
element.setAttribute("priority", Integer.toString(this.getPriority()));
if (this.getType() == TYPE_DIRECT) {
element.setAttribute("type", "direct");
} else if (this.getType() == TYPE_PROXY) {
element.setAttribute("type", "proxy");
}
return element;
}
public void flagAsUsedByCounterpart() {
this.usedByCounterpart = true;
}
public boolean isUsedByCounterpart() {
return this.usedByCounterpart;
}
public String toString() {
return String.format(
"%s:%s (priority=%s,ours=%s)", getHost(), getPort(), getPriority(), isOurs());
}
}

View file

@ -1,83 +0,0 @@
package eu.siacs.conversations.xmpp.jingle;
import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
import im.conversations.android.axolotl.AxolotlService;
import java.util.concurrent.atomic.AtomicBoolean;
import org.whispersystems.libsignal.IdentityKey;
public class OmemoVerification {
private final AtomicBoolean deviceIdWritten = new AtomicBoolean(false);
private final AtomicBoolean identityKeyWritten = new AtomicBoolean(false);
private Integer deviceId;
private IdentityKey identityKey;
public void setDeviceId(final Integer id) {
if (deviceIdWritten.compareAndSet(false, true)) {
this.deviceId = id;
return;
}
throw new IllegalStateException("Device Id has already been set");
}
public int getDeviceId() {
Preconditions.checkNotNull(this.deviceId, "Device ID is null");
return this.deviceId;
}
public boolean hasDeviceId() {
return this.deviceId != null;
}
public void setSessionFingerprint(final IdentityKey identityKey) {
Preconditions.checkNotNull(identityKey, "IdentityKey must not be null");
if (identityKeyWritten.compareAndSet(false, true)) {
this.identityKey = identityKey;
return;
}
throw new IllegalStateException("Identity Key has already been set");
}
public IdentityKey getFingerprint() {
return this.identityKey;
}
public void setOrEnsureEqual(AxolotlService.OmemoVerifiedPayload<?> omemoVerifiedPayload) {
setOrEnsureEqual(omemoVerifiedPayload.getDeviceId(), omemoVerifiedPayload.getFingerprint());
}
public void setOrEnsureEqual(final int deviceId, final IdentityKey identityKey) {
Preconditions.checkNotNull(identityKey, "IdentityKey must not be null");
if (this.deviceIdWritten.get() || this.identityKeyWritten.get()) {
if (this.identityKey == null) {
throw new IllegalStateException(
"No session fingerprint has been previously provided");
}
if (!identityKey.equals(this.identityKey)) {
throw new SecurityException("IdentityKeys did not match");
}
if (this.deviceId == null) {
throw new IllegalStateException("No Device Id has been previously provided");
}
if (this.deviceId != deviceId) {
throw new IllegalStateException("Device Ids did not match");
}
} else {
this.setSessionFingerprint(identityKey);
this.setDeviceId(deviceId);
}
}
public boolean hasFingerprint() {
return this.identityKey != null;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("deviceId", deviceId)
.add("fingerprint", identityKey)
.toString();
}
}

View file

@ -1,20 +0,0 @@
package eu.siacs.conversations.xmpp.jingle;
import eu.siacs.conversations.xmpp.jingle.stanzas.Group;
import eu.siacs.conversations.xmpp.jingle.stanzas.OmemoVerifiedIceUdpTransportInfo;
import java.util.Map;
public class OmemoVerifiedRtpContentMap extends RtpContentMap {
public OmemoVerifiedRtpContentMap(Group group, Map<String, DescriptionTransport> contents) {
super(group, contents);
for (final DescriptionTransport descriptionTransport : contents.values()) {
if (descriptionTransport.transport instanceof OmemoVerifiedIceUdpTransportInfo) {
((OmemoVerifiedIceUdpTransportInfo) descriptionTransport.transport)
.ensureNoPlaintextFingerprint();
continue;
}
throw new IllegalStateException(
"OmemoVerifiedRtpContentMap contains non-verified transport info");
}
}
}

View file

@ -1,5 +0,0 @@
package eu.siacs.conversations.xmpp.jingle;
public interface OnPrimaryCandidateFound {
void onPrimaryCandidateFound(boolean success, JingleCandidate canditate);
}

View file

@ -1,7 +0,0 @@
package eu.siacs.conversations.xmpp.jingle;
public interface OnTransportConnected {
void failed();
void established();
}

View file

@ -1,9 +0,0 @@
package eu.siacs.conversations.xmpp.jingle;
import org.jxmpp.jid.Jid;
public interface OngoingRtpSession {
Jid getWith();
String getSessionId();
}

View file

@ -1,21 +0,0 @@
package eu.siacs.conversations.xmpp.jingle;
public enum RtpEndUserState {
INCOMING_CALL, // received a 'propose' message
CONNECTING, // session-initiate or session-accepted but no webrtc peer connection yet
CONNECTED, // session-accepted and webrtc peer connection is connected
RECONNECTING, // session-accepted and webrtc peer connection was connected once but is currently
// disconnected or failed
INCOMING_CONTENT_ADD, // session-accepted with a pending, incoming content-add
FINDING_DEVICE, // 'propose' has been sent out; no 184 ack yet
RINGING, // 'propose' has been sent out and it has been 184 acked
ACCEPTING_CALL, // 'proceed' message has been sent; but no session-initiate has been received
ENDING_CALL, // libwebrt says 'closed' but session-terminate hasnt gone through
ENDED, // close UI
DECLINED_OR_BUSY, // other party declined; no retry button
CONNECTIVITY_ERROR, // network error; retry button
CONNECTIVITY_LOST_ERROR, // network error but for call duration > 0
RETRACTED, // user pressed home or power button during 'ringing' - shows retry button
APPLICATION_ERROR, // something rather bad happened; libwebrtc failed or we got in IQ-error
SECURITY_ERROR // problem with DTLS (missing) or verification
}

View file

@ -1,274 +0,0 @@
package eu.siacs.conversations.xmpp.jingle;
import static java.util.Arrays.asList;
import android.content.Context;
import android.media.AudioManager;
import android.media.ToneGenerator;
import android.util.Log;
import eu.siacs.conversations.Config;
import im.conversations.android.xmpp.manager.JingleConnectionManager;
import java.util.Arrays;
import java.util.Set;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
public class ToneManager {
private final ToneGenerator toneGenerator;
private final Context context;
private ToneState state = null;
private RtpEndUserState endUserState = null;
private ScheduledFuture<?> currentTone;
private ScheduledFuture<?> currentResetFuture;
private boolean appRtcAudioManagerHasControl = false;
private static volatile ToneManager INSTANCE;
private ToneManager(final Context context) {
ToneGenerator toneGenerator;
try {
toneGenerator = new ToneGenerator(AudioManager.STREAM_VOICE_CALL, 60);
} catch (final RuntimeException e) {
Log.e(Config.LOGTAG, "unable to instantiate ToneGenerator", e);
toneGenerator = null;
}
this.toneGenerator = toneGenerator;
this.context = context.getApplicationContext();
}
private static ToneState of(
final boolean isInitiator, final RtpEndUserState state, final Set<Media> media) {
if (isInitiator) {
if (asList(
RtpEndUserState.FINDING_DEVICE,
RtpEndUserState.RINGING,
RtpEndUserState.CONNECTING)
.contains(state)) {
return ToneState.RINGING;
}
if (state == RtpEndUserState.DECLINED_OR_BUSY) {
return ToneState.BUSY;
}
}
if (state == RtpEndUserState.ENDING_CALL) {
if (media.contains(Media.VIDEO)) {
return ToneState.NULL;
} else {
return ToneState.ENDING_CALL;
}
}
if (Arrays.asList(
RtpEndUserState.CONNECTED,
RtpEndUserState.RECONNECTING,
RtpEndUserState.INCOMING_CONTENT_ADD)
.contains(state)) {
if (media.contains(Media.VIDEO)) {
return ToneState.NULL;
} else {
return ToneState.CONNECTED;
}
}
return ToneState.NULL;
}
public void transition(final RtpEndUserState state, final Set<Media> media) {
transition(state, of(true, state, media), media);
}
void transition(
final boolean isInitiator, final RtpEndUserState state, final Set<Media> media) {
transition(state, of(isInitiator, state, media), media);
}
private synchronized void transition(
final RtpEndUserState endUserState, final ToneState state, final Set<Media> media) {
final RtpEndUserState normalizeEndUserState = normalize(endUserState);
if (this.endUserState == normalizeEndUserState) {
return;
}
this.endUserState = normalizeEndUserState;
if (this.state == state) {
return;
}
if (state == ToneState.NULL && this.state == ToneState.ENDING_CALL) {
return;
}
cancelCurrentTone();
Log.d(Config.LOGTAG, getClass().getName() + ".transition(" + state + ")");
if (state != ToneState.NULL) {
configureAudioManagerForCall(media);
}
switch (state) {
case RINGING:
scheduleWaitingTone();
break;
case CONNECTED:
scheduleConnected();
break;
case BUSY:
scheduleBusy();
break;
case ENDING_CALL:
scheduleEnding();
break;
case NULL:
if (noResetScheduled()) {
resetAudioManager();
}
break;
default:
throw new IllegalStateException("Unable to handle transition to " + state);
}
this.state = state;
}
private static RtpEndUserState normalize(final RtpEndUserState endUserState) {
if (Arrays.asList(
RtpEndUserState.CONNECTED,
RtpEndUserState.RECONNECTING,
RtpEndUserState.INCOMING_CONTENT_ADD)
.contains(endUserState)) {
return RtpEndUserState.CONNECTED;
} else {
return endUserState;
}
}
void setAppRtcAudioManagerHasControl(final boolean appRtcAudioManagerHasControl) {
this.appRtcAudioManagerHasControl = appRtcAudioManagerHasControl;
}
private void scheduleConnected() {
this.currentTone =
JingleConnectionManager.SCHEDULED_EXECUTOR_SERVICE.schedule(
() -> {
startTone(ToneGenerator.TONE_PROP_PROMPT, 200);
},
0,
TimeUnit.SECONDS);
}
private void scheduleEnding() {
this.currentTone =
JingleConnectionManager.SCHEDULED_EXECUTOR_SERVICE.schedule(
() -> {
startTone(ToneGenerator.TONE_CDMA_CALLDROP_LITE, 375);
},
0,
TimeUnit.SECONDS);
this.currentResetFuture =
JingleConnectionManager.SCHEDULED_EXECUTOR_SERVICE.schedule(
this::resetAudioManager, 375, TimeUnit.MILLISECONDS);
}
private void scheduleBusy() {
this.currentTone =
JingleConnectionManager.SCHEDULED_EXECUTOR_SERVICE.schedule(
() -> {
startTone(ToneGenerator.TONE_CDMA_NETWORK_BUSY, 2500);
},
0,
TimeUnit.SECONDS);
this.currentResetFuture =
JingleConnectionManager.SCHEDULED_EXECUTOR_SERVICE.schedule(
this::resetAudioManager, 2500, TimeUnit.MILLISECONDS);
}
private void scheduleWaitingTone() {
this.currentTone =
JingleConnectionManager.SCHEDULED_EXECUTOR_SERVICE.scheduleAtFixedRate(
() -> {
startTone(ToneGenerator.TONE_CDMA_DIAL_TONE_LITE, 750);
},
0,
3,
TimeUnit.SECONDS);
}
private boolean noResetScheduled() {
return this.currentResetFuture == null || this.currentResetFuture.isDone();
}
private void cancelCurrentTone() {
if (currentTone != null) {
currentTone.cancel(true);
}
if (toneGenerator != null) {
toneGenerator.stopTone();
}
}
private void startTone(final int toneType, final int durationMs) {
if (toneGenerator != null) {
this.toneGenerator.startTone(toneType, durationMs);
} else {
Log.e(Config.LOGTAG, "failed to start tone. ToneGenerator doesn't exist");
}
}
private void configureAudioManagerForCall(final Set<Media> media) {
if (appRtcAudioManagerHasControl) {
Log.d(
Config.LOGTAG,
ToneManager.class.getName()
+ ": do not configure audio manager because RTC has control");
return;
}
final AudioManager audioManager =
(AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
if (audioManager == null) {
return;
}
final boolean isSpeakerPhone = media.contains(Media.VIDEO);
Log.d(
Config.LOGTAG,
ToneManager.class.getName()
+ ": putting AudioManager into communication mode. speaker="
+ isSpeakerPhone);
audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
audioManager.setSpeakerphoneOn(isSpeakerPhone);
}
private void resetAudioManager() {
if (appRtcAudioManagerHasControl) {
Log.d(
Config.LOGTAG,
ToneManager.class.getName()
+ ": do not reset audio manager because RTC has control");
return;
}
final AudioManager audioManager =
(AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
if (audioManager == null) {
return;
}
Log.d(
Config.LOGTAG,
ToneManager.class.getName() + ": putting AudioManager back into normal mode");
audioManager.setMode(AudioManager.MODE_NORMAL);
audioManager.setSpeakerphoneOn(false);
}
public static ToneManager getInstance(final Context context) {
if (INSTANCE != null) {
return INSTANCE;
}
synchronized (ToneManager.class) {
if (INSTANCE != null) {
return INSTANCE;
}
INSTANCE = new ToneManager(context);
return INSTANCE;
}
}
private enum ToneState {
NULL,
RINGING,
CONNECTED,
BUSY,
ENDING_CALL
}
}

View file

@ -1,69 +0,0 @@
package eu.siacs.conversations.xmpp.jingle.stanzas;
import com.google.common.base.Preconditions;
import im.conversations.android.xml.Element;
import java.util.Arrays;
import java.util.List;
public class FileTransferDescription extends GenericDescription {
public static List<String> NAMESPACES =
Arrays.asList(Version.FT_3.namespace, Version.FT_4.namespace, Version.FT_5.namespace);
private FileTransferDescription(String name, String namespace) {
super(name, namespace);
}
public Version getVersion() {
final String namespace = getNamespace();
if (namespace.equals(Version.FT_3.namespace)) {
return Version.FT_3;
} else if (namespace.equals(Version.FT_4.namespace)) {
return Version.FT_4;
} else if (namespace.equals(Version.FT_5.namespace)) {
return Version.FT_5;
} else {
throw new IllegalStateException("Unknown namespace");
}
}
public Element getFileOffer() {
final Version version = getVersion();
if (version == Version.FT_3) {
final Element offer = this.findChild("offer");
return offer == null ? null : offer.findChild("file");
} else {
return this.findChild("file");
}
}
public static FileTransferDescription upgrade(final Element element) {
Preconditions.checkArgument(
"description".equals(element.getName()),
"Name of provided element is not description");
Preconditions.checkArgument(
NAMESPACES.contains(element.getNamespace()),
"Element does not match a file transfer namespace");
final FileTransferDescription description =
new FileTransferDescription("description", element.getNamespace());
description.setAttributes(element.getAttributes());
description.setChildren(element.getChildren());
return description;
}
public enum Version {
FT_3("urn:xmpp:jingle:apps:file-transfer:3"),
FT_4("urn:xmpp:jingle:apps:file-transfer:4"),
FT_5("urn:xmpp:jingle:apps:file-transfer:5");
private final String namespace;
Version(String namespace) {
this.namespace = namespace;
}
public String getNamespace() {
return namespace;
}
}
}

View file

@ -1,62 +0,0 @@
package eu.siacs.conversations.xmpp.jingle.stanzas;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import im.conversations.android.xml.Element;
import im.conversations.android.xml.Namespace;
import java.util.Collection;
import java.util.List;
public class Group extends Element {
private Group() {
super("group", Namespace.JINGLE_APPS_GROUPING);
}
public Group(final String semantics, final Collection<String> identificationTags) {
super("group", Namespace.JINGLE_APPS_GROUPING);
this.setAttribute("semantics", semantics);
for (String tag : identificationTags) {
this.addChild(new Element("content").setAttribute("name", tag));
}
}
public String getSemantics() {
return this.getAttribute("semantics");
}
public List<String> getIdentificationTags() {
final ImmutableList.Builder<String> builder = new ImmutableList.Builder<>();
for (final Element child : this.children) {
if ("content".equals(child.getName())) {
final String name = child.getAttribute("name");
if (name != null) {
builder.add(name);
}
}
}
return builder.build();
}
public static Group ofSdpString(final String input) {
ImmutableList.Builder<String> tagBuilder = new ImmutableList.Builder<>();
final String[] parts = input.split(" ");
if (parts.length >= 2) {
final String semantics = parts[0];
for (int i = 1; i < parts.length; ++i) {
tagBuilder.add(parts[i]);
}
return new Group(semantics, tagBuilder.build());
}
return null;
}
public static Group upgrade(final Element element) {
Preconditions.checkArgument("group".equals(element.getName()));
Preconditions.checkArgument(Namespace.JINGLE_APPS_GROUPING.equals(element.getNamespace()));
final Group group = new Group();
group.setAttributes(element.getAttributes());
group.setChildren(element.getChildren());
return group;
}
}

View file

@ -1,49 +0,0 @@
package eu.siacs.conversations.xmpp.jingle.stanzas;
import com.google.common.base.Preconditions;
import im.conversations.android.xml.Element;
import im.conversations.android.xml.Namespace;
public class IbbTransportInfo extends GenericTransportInfo {
private IbbTransportInfo(final String name, final String xmlns) {
super(name, xmlns);
}
public IbbTransportInfo(final String transportId, final int blockSize) {
super("transport", Namespace.JINGLE_TRANSPORTS_IBB);
Preconditions.checkNotNull(transportId, "Transport ID can not be null");
Preconditions.checkArgument(blockSize > 0, "Block size must be larger than 0");
this.setAttribute("block-size", blockSize);
this.setAttribute("sid", transportId);
}
public String getTransportId() {
return this.getAttribute("sid");
}
public int getBlockSize() {
final String blockSize = this.getAttribute("block-size");
if (blockSize == null) {
return 0;
}
try {
return Integer.parseInt(blockSize);
} catch (NumberFormatException e) {
return 0;
}
}
public static IbbTransportInfo upgrade(final Element element) {
Preconditions.checkArgument(
"transport".equals(element.getName()), "Name of provided element is not transport");
Preconditions.checkArgument(
Namespace.JINGLE_TRANSPORTS_IBB.equals(element.getNamespace()),
"Element does not match ibb transport namespace");
final IbbTransportInfo transportInfo =
new IbbTransportInfo("transport", Namespace.JINGLE_TRANSPORTS_IBB);
transportInfo.setAttributes(element.getAttributes());
transportInfo.setChildren(element.getChildren());
return transportInfo;
}
}

View file

@ -1,53 +0,0 @@
package eu.siacs.conversations.xmpp.jingle.stanzas;
import com.google.common.base.Preconditions;
import eu.siacs.conversations.xmpp.jingle.JingleCandidate;
import im.conversations.android.xml.Element;
import im.conversations.android.xml.Namespace;
import java.util.Collection;
import java.util.List;
public class S5BTransportInfo extends GenericTransportInfo {
private S5BTransportInfo(final String name, final String xmlns) {
super(name, xmlns);
}
public String getTransportId() {
return this.getAttribute("sid");
}
public S5BTransportInfo(
final String transportId, final Collection<JingleCandidate> candidates) {
super("transport", Namespace.JINGLE_TRANSPORTS_S5B);
Preconditions.checkNotNull(transportId, "transport id must not be null");
for (JingleCandidate candidate : candidates) {
this.addChild(candidate.toElement());
}
this.setAttribute("sid", transportId);
}
public S5BTransportInfo(final String transportId, final Element child) {
super("transport", Namespace.JINGLE_TRANSPORTS_S5B);
Preconditions.checkNotNull(transportId, "transport id must not be null");
this.addChild(child);
this.setAttribute("sid", transportId);
}
public List<JingleCandidate> getCandidates() {
return JingleCandidate.parse(this.getChildren());
}
public static S5BTransportInfo upgrade(final Element element) {
Preconditions.checkArgument(
"transport".equals(element.getName()), "Name of provided element is not transport");
Preconditions.checkArgument(
Namespace.JINGLE_TRANSPORTS_S5B.equals(element.getNamespace()),
"Element does not match s5b transport namespace");
final S5BTransportInfo transportInfo =
new S5BTransportInfo("transport", Namespace.JINGLE_TRANSPORTS_S5B);
transportInfo.setAttributes(element.getAttributes());
transportInfo.setChildren(element.getChildren());
return transportInfo;
}
}

View file

@ -1,15 +0,0 @@
package im.conversations.android;
import im.conversations.android.database.ConversationsDatabase;
import im.conversations.android.database.model.Account;
public abstract class AbstractAccountService {
protected Account account;
protected ConversationsDatabase database;
protected AbstractAccountService(final Account account, final ConversationsDatabase database) {
this.account = account;
this.database = database;
}
}

View file

@ -1,55 +0,0 @@
package im.conversations.android;
import android.content.Context;
import android.content.SharedPreferences;
import android.net.Uri;
import androidx.preference.PreferenceManager;
import com.google.common.base.Strings;
public class AppSettings {
public static final String PREFERENCE_KEY_RINGTONE = "call_ringtone";
public static final String PREFERENCE_KEY_BTBV = "btbv";
public static final String PREFERENCE_KEY_TRUST_SYSTEM_CA_STORE = "trust_system_ca_store";
private final Context context;
public AppSettings(final Context context) {
this.context = context;
}
public Uri getRingtone() {
final SharedPreferences sharedPreferences =
PreferenceManager.getDefaultSharedPreferences(context);
final String incomingCallRingtone =
sharedPreferences.getString(
PREFERENCE_KEY_RINGTONE,
context.getString(R.string.incoming_call_ringtone));
return Strings.isNullOrEmpty(incomingCallRingtone) ? null : Uri.parse(incomingCallRingtone);
}
public void setRingtone(final Uri uri) {
final SharedPreferences sharedPreferences =
PreferenceManager.getDefaultSharedPreferences(context);
sharedPreferences
.edit()
.putString(PREFERENCE_KEY_RINGTONE, uri == null ? null : uri.toString())
.apply();
}
public boolean isBTBVEnabled() {
final SharedPreferences sharedPreferences =
PreferenceManager.getDefaultSharedPreferences(context);
return sharedPreferences.getBoolean(
PREFERENCE_KEY_BTBV, context.getResources().getBoolean(R.bool.btbv));
}
public boolean isTrustSystemCAStore() {
final SharedPreferences sharedPreferences =
PreferenceManager.getDefaultSharedPreferences(context);
return sharedPreferences.getBoolean(
PREFERENCE_KEY_TRUST_SYSTEM_CA_STORE,
context.getResources().getBoolean(R.bool.btbv));
}
}

View file

@ -1,86 +0,0 @@
package im.conversations.android;
import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.preference.PreferenceManager;
import com.google.android.material.color.DynamicColors;
import com.google.android.material.color.DynamicColorsOptions;
import im.conversations.android.dns.Resolver;
import im.conversations.android.notification.Channels;
import im.conversations.android.xmpp.ConnectionPool;
import java.security.SecureRandom;
import java.security.Security;
import org.conscrypt.Conscrypt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Conversations extends Application {
private static final Logger LOGGER = LoggerFactory.getLogger(Conversations.class);
public static final SecureRandom SECURE_RANDOM = new SecureRandom();
@Override
public void onCreate() {
super.onCreate();
try {
Security.insertProviderAt(Conscrypt.newProvider(), 1);
} catch (final Throwable throwable) {
LOGGER.warn("Could not initialize security provider", throwable);
}
final var channels = new Channels(this);
channels.initialize();
Resolver.init(this);
ConnectionPool.getInstance(this).reconfigure();
applyThemeSettings();
}
public void applyThemeSettings() {
final var sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
if (sharedPreferences == null) {
return;
}
applyThemeSettings(sharedPreferences);
}
private void applyThemeSettings(final SharedPreferences sharedPreferences) {
AppCompatDelegate.setDefaultNightMode(getDesiredNightMode(this, sharedPreferences));
var dynamicColorsOptions =
new DynamicColorsOptions.Builder()
.setPrecondition((activity, t) -> isDynamicColorsDesired(activity))
.build();
DynamicColors.applyToActivitiesIfAvailable(this, dynamicColorsOptions);
}
public static int getDesiredNightMode(final Context context) {
final var sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
if (sharedPreferences == null) {
return AppCompatDelegate.getDefaultNightMode();
}
return getDesiredNightMode(context, sharedPreferences);
}
public static boolean isDynamicColorsDesired(final Context context) {
final var preferences = PreferenceManager.getDefaultSharedPreferences(context);
return preferences.getBoolean("dynamic_colors", false);
}
private static int getDesiredNightMode(
final Context context, final SharedPreferences sharedPreferences) {
final String theme =
sharedPreferences.getString("theme", context.getString(R.string.default_theme));
return getDesiredNightMode(theme);
}
public static int getDesiredNightMode(final String theme) {
if ("automatic".equals(theme)) {
return AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM;
} else if ("light".equals(theme)) {
return AppCompatDelegate.MODE_NIGHT_NO;
} else {
return AppCompatDelegate.MODE_NIGHT_YES;
}
}
}

View file

@ -1,87 +0,0 @@
package im.conversations.android;
import com.google.common.base.Preconditions;
import com.google.common.io.BaseEncoding;
import com.google.common.io.ByteSource;
import java.io.IOException;
import java.util.Random;
import java.util.UUID;
public class IDs {
private static final Random RANDOM = new Random();
private static final long UUID_VERSION_MASK = 4 << 12;
public static int quickInt() {
return RANDOM.nextInt();
}
public static String huge() {
final var random = new byte[96];
Conversations.SECURE_RANDOM.nextBytes(random);
return BaseEncoding.base64Url().encode(random);
}
public static String medium() {
final var random = new byte[9];
Conversations.SECURE_RANDOM.nextBytes(random);
return BaseEncoding.base64Url().encode(random);
}
public static String tiny() {
final var random = new byte[3];
Conversations.SECURE_RANDOM.nextBytes(random);
return BaseEncoding.base64Url().encode(random);
}
public static String tiny(final byte[] seed) {
return BaseEncoding.base64Url().encode(slice(seed));
}
private static byte[] slice(final byte[] input) {
if (input == null || input.length < 3) {
return new byte[3];
}
try {
return ByteSource.wrap(input).slice(0, 3).read();
} catch (final IOException e) {
return new byte[3];
}
}
public static UUID uuid(final byte[] bytes) {
Preconditions.checkArgument(bytes != null && bytes.length == 32);
long msb = 0;
long lsb = 0;
msb |= (bytes[0x0] & 0xffL) << 56;
msb |= (bytes[0x1] & 0xffL) << 48;
msb |= (bytes[0x2] & 0xffL) << 40;
msb |= (bytes[0x3] & 0xffL) << 32;
msb |= (bytes[0x4] & 0xffL) << 24;
msb |= (bytes[0x5] & 0xffL) << 16;
msb |= (bytes[0x6] & 0xffL) << 8;
msb |= (bytes[0x7] & 0xffL);
lsb |= (bytes[0x8] & 0xffL) << 56;
lsb |= (bytes[0x9] & 0xffL) << 48;
lsb |= (bytes[0xa] & 0xffL) << 40;
lsb |= (bytes[0xb] & 0xffL) << 32;
lsb |= (bytes[0xc] & 0xffL) << 24;
lsb |= (bytes[0xd] & 0xffL) << 16;
lsb |= (bytes[0xe] & 0xffL) << 8;
lsb |= (bytes[0xf] & 0xffL);
msb = (msb & 0xffffffffffff0fffL) | UUID_VERSION_MASK; // set version
lsb = (lsb & 0x3fffffffffffffffL) | 0x8000000000000000L; // set variant
return new UUID(msb, lsb);
}
public static byte[] seed() {
final var random = new byte[32];
Conversations.SECURE_RANDOM.nextBytes(random);
return random;
}
}

View file

@ -1,44 +0,0 @@
package im.conversations.android.axolotl;
import com.google.common.base.Objects;
import org.jxmpp.jid.BareJid;
import org.whispersystems.libsignal.SignalProtocolAddress;
public class AxolotlAddress extends SignalProtocolAddress {
private final BareJid jid;
public AxolotlAddress(final BareJid jid, int deviceId) {
super(jid.toString(), deviceId);
this.jid = jid;
}
public BareJid getJid() {
return this.jid;
}
public static AxolotlAddress cast(final SignalProtocolAddress signalProtocolAddress) {
if (signalProtocolAddress instanceof AxolotlAddress) {
return (AxolotlAddress) signalProtocolAddress;
}
throw new IllegalArgumentException(
String.format(
"This %s is not a %s",
SignalProtocolAddress.class.getSimpleName(),
AxolotlAddress.class.getSimpleName()));
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
AxolotlAddress that = (AxolotlAddress) o;
return Objects.equal(jid, that.jid);
}
@Override
public int hashCode() {
return Objects.hashCode(super.hashCode(), jid);
}
}

View file

@ -1,12 +0,0 @@
package im.conversations.android.axolotl;
public class AxolotlDecryptionException extends Exception {
public AxolotlDecryptionException(final String message) {
super(message);
}
public AxolotlDecryptionException(final Throwable throwable) {
super(throwable);
}
}

View file

@ -1,16 +0,0 @@
package im.conversations.android.axolotl;
public class AxolotlEncryptionException extends Exception {
public AxolotlEncryptionException(String msg) {
super(msg);
}
public AxolotlEncryptionException(String msg, Exception e) {
super(msg, e);
}
public AxolotlEncryptionException(Exception e) {
super(e);
}
}

View file

@ -1,34 +0,0 @@
package im.conversations.android.axolotl;
import java.nio.charset.StandardCharsets;
import org.whispersystems.libsignal.IdentityKey;
public class AxolotlPayload {
public final AxolotlAddress axolotlAddress;
public final IdentityKey identityKey;
public final boolean preKeyMessage;
public final boolean inDeviceList;
public final byte[] payload;
public AxolotlPayload(
AxolotlAddress axolotlAddress,
final IdentityKey identityKey,
final boolean preKeyMessage,
final boolean inDeviceList,
byte[] payload) {
this.axolotlAddress = axolotlAddress;
this.identityKey = identityKey;
this.preKeyMessage = preKeyMessage;
this.inDeviceList = inDeviceList;
this.payload = payload;
}
public String payloadAsString() {
return new String(payload, StandardCharsets.UTF_8);
}
public boolean hasPayload() {
return payload != null;
}
}

View file

@ -1,304 +0,0 @@
package im.conversations.android.axolotl;
import android.content.Context;
import android.os.Build;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import eu.siacs.conversations.xmpp.jingle.OmemoVerification;
import im.conversations.android.AbstractAccountService;
import im.conversations.android.database.AxolotlDatabaseStore;
import im.conversations.android.database.ConversationsDatabase;
import im.conversations.android.database.model.Account;
import im.conversations.android.transformer.MessageContentWrapper;
import im.conversations.android.xmpp.model.axolotl.Encrypted;
import im.conversations.android.xmpp.model.axolotl.Header;
import im.conversations.android.xmpp.model.axolotl.Key;
import im.conversations.android.xmpp.model.axolotl.Payload;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.HashSet;
import java.util.Set;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.Jid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.libsignal.DuplicateMessageException;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.InvalidKeyException;
import org.whispersystems.libsignal.InvalidKeyIdException;
import org.whispersystems.libsignal.InvalidMessageException;
import org.whispersystems.libsignal.InvalidVersionException;
import org.whispersystems.libsignal.LegacyMessageException;
import org.whispersystems.libsignal.NoSessionException;
import org.whispersystems.libsignal.UntrustedIdentityException;
import org.whispersystems.libsignal.protocol.PreKeySignalMessage;
import org.whispersystems.libsignal.protocol.SignalMessage;
import org.whispersystems.libsignal.state.SessionRecord;
import org.whispersystems.libsignal.state.SignalProtocolStore;
public class AxolotlService extends AbstractAccountService {
private static final Logger LOGGER = LoggerFactory.getLogger(AxolotlService.class);
public static final String KEY_TYPE = "AES";
public static final String CIPHER_MODE = "AES/GCM/NoPadding";
public static final String BOUNCY_CASTLE_PROVIDER = "BC";
private final SignalProtocolStore signalProtocolStore;
private PostDecryptionHook postDecryptionHook;
private final Set<AxolotlAddress> freshSessions = new HashSet<>();
private final Multimap<BareJid, Integer> devicesNotInPep = ArrayListMultimap.create();
public AxolotlService(
final Account account,
final Context context,
final ConversationsDatabase conversationsDatabase) {
super(account, conversationsDatabase);
this.signalProtocolStore =
new AxolotlDatabaseStore(account, context, conversationsDatabase);
}
public void setPostDecryptionHook(final PostDecryptionHook postDecryptionHook) {
this.postDecryptionHook = postDecryptionHook;
}
private AxolotlSession buildReceivingSession(
final Jid from, final IdentityKey identityKey, final Header header) {
final Optional<Integer> sid = header.getSourceDevice();
if (sid.isPresent()) {
return AxolotlSession.of(
signalProtocolStore,
identityKey,
new AxolotlAddress(from.asBareJid(), sid.get()));
}
throw new IllegalArgumentException("Header did not contain a source device id");
}
public AxolotlSession getExistingSession(final AxolotlAddress axolotlAddress) {
final SessionRecord sessionState = signalProtocolStore.loadSession(axolotlAddress);
if (sessionState == null) {
return null;
}
final IdentityKey identityKey = sessionState.getSessionState().getRemoteIdentityKey();
return AxolotlSession.of(signalProtocolStore, identityKey, axolotlAddress);
}
private AxolotlSession getExistingSessionOrThrow(final AxolotlAddress axolotlAddress)
throws NoSessionException {
final var session = getExistingSession(axolotlAddress);
if (session == null) {
// TODO When receiving a message that is not an OMEMOKeyExchange from a device there is
// no session with, clients SHOULD create a session with that device and notify it about
// the new session by responding with an empty OMEMO message as per Sending a message.
throw new NoSessionException(
String.format("No session for %s", axolotlAddress.toString()));
}
return session;
}
public boolean decryptEmptyMessage(final BareJid from, final Encrypted encrypted) {
Preconditions.checkArgument(
!encrypted.hasPayload(), "Use decryptToMessageContent to decrypt payload messages");
try {
final var payload = decrypt(from, encrypted);
return !payload.hasPayload();
} catch (final AxolotlDecryptionException e) {
return false;
}
}
public MessageContentWrapper decryptToMessageContent(
final BareJid from, final Encrypted encrypted) {
Preconditions.checkArgument(encrypted.hasPayload());
try {
return MessageContentWrapper.ofAxolotl(decrypt(from, encrypted));
} catch (final AxolotlDecryptionException e) {
return MessageContentWrapper.ofAxolotlException(e);
}
}
public AxolotlPayload decrypt(final Jid from, final Encrypted encrypted)
throws AxolotlDecryptionException {
final AxolotlPayload axolotlPayload;
try {
axolotlPayload = decryptOrThrow(from, encrypted);
} catch (final IllegalArgumentException
| NotEncryptedForThisDeviceException
| InvalidMessageException
| InvalidVersionException
| UntrustedIdentityException
| DuplicateMessageException
| InvalidKeyIdException
| LegacyMessageException
| InvalidKeyException
| NoSessionException
| OutdatedSenderException
| NoSuchPaddingException
| NoSuchAlgorithmException
| NoSuchProviderException
| InvalidAlgorithmParameterException
| java.security.InvalidKeyException
| IllegalBlockSizeException
| BadPaddingException e) {
throw new AxolotlDecryptionException(e);
}
registerForHook(axolotlPayload);
return axolotlPayload;
}
private AxolotlPayload decryptOrThrow(final Jid from, final Encrypted encrypted)
throws NotEncryptedForThisDeviceException, InvalidMessageException,
InvalidVersionException, UntrustedIdentityException, DuplicateMessageException,
InvalidKeyIdException, LegacyMessageException, InvalidKeyException,
NoSessionException, OutdatedSenderException, NoSuchPaddingException,
NoSuchAlgorithmException, NoSuchProviderException,
InvalidAlgorithmParameterException, java.security.InvalidKeyException,
IllegalBlockSizeException, BadPaddingException {
final Payload payload = encrypted.getPayload();
final Header header = encrypted.getHeader();
final Key ourKey = header.getKey(signalProtocolStore.getLocalRegistrationId());
if (ourKey == null) {
throw new NotEncryptedForThisDeviceException();
}
final byte[] keyWithAuthTag;
final AxolotlSession session;
final boolean preKeyMessage;
if (ourKey.isPreKey()) {
final PreKeySignalMessage preKeySignalMessage =
new PreKeySignalMessage(ourKey.asBytes());
preKeyMessage = true;
session = buildReceivingSession(from, preKeySignalMessage.getIdentityKey(), header);
keyWithAuthTag = session.sessionCipher.decrypt(preKeySignalMessage);
} else {
final SignalMessage signalMessage = new SignalMessage(ourKey.asBytes());
preKeyMessage = false;
session =
getExistingSessionOrThrow(
new AxolotlAddress(from.asBareJid(), header.getSourceDevice().get()));
keyWithAuthTag = session.sessionCipher.decrypt(signalMessage);
}
final var inDeviceList = database.axolotlDao().hasDeviceId(account, session.axolotlAddress);
if (payload == null) {
return new AxolotlPayload(
session.axolotlAddress, session.identityKey, preKeyMessage, inDeviceList, null);
}
if (keyWithAuthTag.length < 32) {
throw new OutdatedSenderException(
"Key did not contain auth tag. Sender needs to update their OMEMO client");
}
final byte[] key = new byte[16];
final byte[] authTag = new byte[16];
final byte[] iv = header.getIv();
System.arraycopy(keyWithAuthTag, 0, key, 0, key.length);
System.arraycopy(keyWithAuthTag, key.length, authTag, 0, authTag.length);
final Cipher cipher;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
cipher = Cipher.getInstance(CIPHER_MODE);
} else {
cipher = Cipher.getInstance(CIPHER_MODE, BOUNCY_CASTLE_PROVIDER);
}
final SecretKey secretKey = new SecretKeySpec(key, KEY_TYPE);
final IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);
final byte[] payloadAsBytes = payload.asBytes();
final byte[] payloadWithAuthTag = new byte[payloadAsBytes.length + 16];
System.arraycopy(payloadAsBytes, 0, payloadWithAuthTag, 0, payloadAsBytes.length);
System.arraycopy(authTag, 0, payloadWithAuthTag, payloadAsBytes.length, authTag.length);
final byte[] decryptedPayload = cipher.doFinal(payloadWithAuthTag);
return new AxolotlPayload(
session.axolotlAddress,
session.identityKey,
preKeyMessage,
inDeviceList,
decryptedPayload);
}
private void registerForHook(final AxolotlPayload axolotlPayload) {
synchronized (this.freshSessions) {
if (axolotlPayload.preKeyMessage) {
this.freshSessions.add(axolotlPayload.axolotlAddress);
}
}
synchronized (this.devicesNotInPep) {
if (!axolotlPayload.inDeviceList) {
this.devicesNotInPep.put(
axolotlPayload.axolotlAddress.getJid(),
axolotlPayload.axolotlAddress.getDeviceId());
}
}
}
public void executePostDecryptionHook() {
final var hook = this.postDecryptionHook;
if (hook == null) {
return;
}
final Set<AxolotlAddress> freshSessions;
synchronized (this.freshSessions) {
freshSessions = ImmutableSet.copyOf(this.freshSessions);
this.freshSessions.clear();
}
final Multimap<BareJid, Integer> devicesNotInPep;
synchronized (this.devicesNotInPep) {
devicesNotInPep = ImmutableMultimap.copyOf(this.devicesNotInPep);
}
hook.executeHook(freshSessions);
hook.executeHook(devicesNotInPep);
}
public SignalProtocolStore getSignalProtocolStore() {
return this.signalProtocolStore;
}
public static class OmemoVerifiedPayload<T> {
private final int deviceId;
private final IdentityKey identityKey;
private final T payload;
public OmemoVerifiedPayload(OmemoVerification omemoVerification, T payload) {
this.deviceId = omemoVerification.getDeviceId();
this.identityKey = omemoVerification.getFingerprint();
this.payload = payload;
}
public int getDeviceId() {
return deviceId;
}
public IdentityKey getFingerprint() {
return identityKey;
}
public T getPayload() {
return payload;
}
}
public static class NotVerifiedException extends SecurityException {
public NotVerifiedException(String message) {
super(message);
}
}
public interface PostDecryptionHook {
void executeHook(final Set<AxolotlAddress> freshSessions);
void executeHook(final Multimap<BareJid, Integer> devicesNotInPep);
}
}

View file

@ -1,29 +0,0 @@
package im.conversations.android.axolotl;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.SessionCipher;
import org.whispersystems.libsignal.state.SignalProtocolStore;
public class AxolotlSession {
public final AxolotlAddress axolotlAddress;
public final IdentityKey identityKey;
public final SessionCipher sessionCipher;
private AxolotlSession(
AxolotlAddress axolotlAddress,
final IdentityKey identityKey,
SessionCipher sessionCipher) {
this.axolotlAddress = axolotlAddress;
this.identityKey = identityKey;
this.sessionCipher = sessionCipher;
}
public static AxolotlSession of(
final SignalProtocolStore signalProtocolStore,
final IdentityKey identityKey,
final AxolotlAddress axolotlAddress) {
final var sessionCipher = new SessionCipher(signalProtocolStore, axolotlAddress);
return new AxolotlSession(axolotlAddress, identityKey, sessionCipher);
}
}

View file

@ -1,231 +0,0 @@
package im.conversations.android.axolotl;
import android.annotation.SuppressLint;
import android.os.Build;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import im.conversations.android.Conversations;
import im.conversations.android.xmpp.model.axolotl.Encrypted;
import im.conversations.android.xmpp.model.axolotl.Header;
import im.conversations.android.xmpp.model.axolotl.Key;
import im.conversations.android.xmpp.model.axolotl.Payload;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.ArrayList;
import java.util.List;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.whispersystems.libsignal.UntrustedIdentityException;
import org.whispersystems.libsignal.protocol.CiphertextMessage;
public class EncryptionBuilder {
private Long sourceDeviceId;
private final ArrayList<AxolotlSession> sessions = new ArrayList<>();
private byte[] payload;
private KeyTransport keyTransport;
public Encrypted build() throws AxolotlEncryptionException {
try {
return buildOrThrow();
} catch (final InvalidAlgorithmParameterException
| NoSuchPaddingException
| IllegalBlockSizeException
| NoSuchAlgorithmException
| BadPaddingException
| NoSuchProviderException
| InvalidKeyException
| UntrustedIdentityException e) {
throw new AxolotlEncryptionException(e);
}
}
private Encrypted buildOrThrow()
throws InvalidAlgorithmParameterException, NoSuchPaddingException,
IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException,
NoSuchProviderException, InvalidKeyException, UntrustedIdentityException {
final long sourceDeviceId =
Preconditions.checkNotNull(this.sourceDeviceId, "Specify a source device id");
final var payloadCleartext = Preconditions.checkNotNull(this.payload, "Specify a payload");
Preconditions.checkState(
this.keyTransport == null, "A payload message should not have a keyTransport");
Preconditions.checkState(sessions.size() > 0, "Add at least on session");
final var sessions = ImmutableList.copyOf(this.sessions);
final var key = generateKey();
final var iv = generateIv();
final var encryptedPayload = encrypt(payloadCleartext, key, iv);
final var keyWithAuthTag = new byte[32];
System.arraycopy(key, 0, keyWithAuthTag, 0, key.length);
System.arraycopy(
encryptedPayload.authTag, 0, keyWithAuthTag, 16, encryptedPayload.authTag.length);
final var header = buildHeader(sessions, keyWithAuthTag);
header.addIv(iv);
header.setSourceDevice(sourceDeviceId);
final var encrypted = new Encrypted();
encrypted.addExtension(header);
final var payload = encrypted.addExtension(new Payload());
payload.setContent(encryptedPayload.encrypted);
return encrypted;
}
public Encrypted buildKeyTransport() throws AxolotlEncryptionException {
try {
return buildKeyTransportOrThrow();
} catch (final UntrustedIdentityException e) {
throw new AxolotlEncryptionException(e);
}
}
private Encrypted buildKeyTransportOrThrow() throws UntrustedIdentityException {
final long sourceDeviceId =
Preconditions.checkNotNull(this.sourceDeviceId, "Specify a source device id");
Preconditions.checkState(
this.payload == null, "A key transport message should not have a payload");
final var keyTransport =
Preconditions.checkNotNull(this.keyTransport, "Specify a keyTransport");
// TODO key transport messages in twomemo (omemo:1) use 32 bytes of zeros instead of a key
// TODO if we are not using this using this for actual key transport we can do this in siacs
// omemo too (and get rid of the IV)
final var sessions = ImmutableList.copyOf(this.sessions);
final var header = buildHeader(sessions, keyTransport.key);
header.addIv(keyTransport.iv);
header.setSourceDevice(sourceDeviceId);
final var encrypted = new Encrypted();
encrypted.addExtension(header);
return encrypted;
}
public Encrypted buildEmpty() throws AxolotlEncryptionException {
try {
return buildEmptyOrThrow();
} catch (final UntrustedIdentityException e) {
throw new AxolotlEncryptionException(e);
}
}
private Encrypted buildEmptyOrThrow() throws UntrustedIdentityException {
final long sourceDeviceId =
Preconditions.checkNotNull(this.sourceDeviceId, "Specify a source device id");
Preconditions.checkState(
this.payload == null, "An empty message should not have a payload");
Preconditions.checkState(
this.keyTransport == null, "An empty message should not have a keyTransport");
final var sessions = ImmutableList.copyOf(this.sessions);
final var header = buildHeader(sessions, new byte[32]);
header.setSourceDevice(sourceDeviceId);
final var encrypted = new Encrypted();
encrypted.addExtension(header);
return encrypted;
}
public EncryptionBuilder payload(final String payload) {
this.payload = payload.getBytes(StandardCharsets.UTF_8);
return this;
}
public EncryptionBuilder keyTransport(final KeyTransport keyTransport) {
this.keyTransport = keyTransport;
return this;
}
public EncryptionBuilder session(final AxolotlSession session) {
this.sessions.add(session);
return this;
}
public EncryptionBuilder sourceDeviceId(final long sourceDeviceId) {
this.sourceDeviceId = sourceDeviceId;
return this;
}
private Header buildHeader(List<AxolotlSession> sessions, final byte[] keyWithAuthTag)
throws UntrustedIdentityException {
final var header = new Header();
for (final AxolotlSession session : sessions) {
final var cipherMessage = session.sessionCipher.encrypt(keyWithAuthTag);
final var key = header.addExtension(new Key());
key.setRemoteDeviceId(session.axolotlAddress.getDeviceId());
key.setContent(cipherMessage.serialize());
key.setIsPreKey(cipherMessage.getType() == CiphertextMessage.PREKEY_TYPE);
}
return header;
}
@SuppressLint("DeprecatedProvider")
private static EncryptedPayload encrypt(
final byte[] payloadCleartext, final byte[] key, final byte[] iv)
throws NoSuchPaddingException, NoSuchAlgorithmException, NoSuchProviderException,
IllegalBlockSizeException, BadPaddingException,
InvalidAlgorithmParameterException, InvalidKeyException {
final Cipher cipher;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
cipher = Cipher.getInstance(AxolotlService.CIPHER_MODE);
} else {
cipher =
Cipher.getInstance(
AxolotlService.CIPHER_MODE, AxolotlService.BOUNCY_CASTLE_PROVIDER);
}
final SecretKey secretKey = new SecretKeySpec(key, AxolotlService.KEY_TYPE);
final IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
final var encryptedWithAuthTag = cipher.doFinal(payloadCleartext);
final var authTag = new byte[16];
final var encrypted = new byte[encryptedWithAuthTag.length - authTag.length];
System.arraycopy(encryptedWithAuthTag, 0, encrypted, 0, encrypted.length);
System.arraycopy(encryptedWithAuthTag, encrypted.length, authTag, 0, authTag.length);
return new EncryptedPayload(encrypted, authTag);
}
private static byte[] generateKey() {
try {
KeyGenerator generator = KeyGenerator.getInstance(AxolotlService.KEY_TYPE);
generator.init(128);
return generator.generateKey().getEncoded();
} catch (final NoSuchAlgorithmException e) {
throw new AssertionError(e);
}
}
private static final class EncryptedPayload {
public final byte[] encrypted;
public final byte[] authTag;
private EncryptedPayload(byte[] encrypted, byte[] authTag) {
this.encrypted = encrypted;
this.authTag = authTag;
}
}
private static byte[] generateIv() {
final byte[] iv = new byte[12];
Conversations.SECURE_RANDOM.nextBytes(iv);
return iv;
}
public static KeyTransport createKeyTransport() {
return new KeyTransport(generateKey(), generateIv());
}
public static class KeyTransport {
public final byte[] key;
public final byte[] iv;
private KeyTransport(byte[] key, byte[] iv) {
this.key = key;
this.iv = iv;
}
}
}

View file

@ -1,8 +0,0 @@
package im.conversations.android.axolotl;
public class OutdatedSenderException extends AxolotlDecryptionException {
public OutdatedSenderException(final String message) {
super(message);
}
}

View file

@ -1,160 +0,0 @@
package im.conversations.android.database;
import android.content.Context;
import im.conversations.android.AbstractAccountService;
import im.conversations.android.AppSettings;
import im.conversations.android.axolotl.AxolotlAddress;
import im.conversations.android.database.dao.AxolotlDao;
import im.conversations.android.database.model.Account;
import im.conversations.android.database.model.Trust;
import java.util.List;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.IdentityKeyPair;
import org.whispersystems.libsignal.InvalidKeyIdException;
import org.whispersystems.libsignal.SignalProtocolAddress;
import org.whispersystems.libsignal.state.PreKeyRecord;
import org.whispersystems.libsignal.state.SessionRecord;
import org.whispersystems.libsignal.state.SignalProtocolStore;
import org.whispersystems.libsignal.state.SignedPreKeyRecord;
public class AxolotlDatabaseStore extends AbstractAccountService implements SignalProtocolStore {
private AppSettings appSettings;
public AxolotlDatabaseStore(
final Account account,
final Context context,
final ConversationsDatabase conversationsDatabase) {
super(account, conversationsDatabase);
this.appSettings = new AppSettings(context);
}
private AxolotlDao axolotlDao() {
return database.axolotlDao();
}
@Override
public IdentityKeyPair getIdentityKeyPair() {
return axolotlDao().getOrCreateIdentityKeyPair(account);
}
@Override
public int getLocalRegistrationId() {
return account.getPublicDeviceIdInt();
}
@Override
public boolean saveIdentity(
final SignalProtocolAddress signalProtocolAddress, final IdentityKey identityKey) {
final var address = AxolotlAddress.cast(signalProtocolAddress);
final boolean isBTBVEnabled = appSettings.isBTBVEnabled();
return database.runInTransaction(
() -> {
final Trust trust;
if (!isBTBVEnabled
|| axolotlDao().isAnyIdentityVerified(account, address.getJid())) {
trust = Trust.UNDECIDED;
} else {
trust = Trust.TRUSTED;
}
return axolotlDao().setIdentity(account, address.getJid(), identityKey, trust);
});
}
@Override
public boolean isTrustedIdentity(
SignalProtocolAddress address, IdentityKey identityKey, Direction direction) {
// TODO return false for Direction==Sending and Trust == untrusted
return true;
}
@Override
public PreKeyRecord loadPreKey(int preKeyId) throws InvalidKeyIdException {
final var preKey = axolotlDao().getPreKey(account.id, preKeyId);
if (preKey == null) {
throw new InvalidKeyIdException(String.format("PreKey %d does not exist", preKeyId));
}
return preKey;
}
@Override
public void storePreKey(int preKeyId, PreKeyRecord preKeyRecord) {
axolotlDao().setPreKey(account, preKeyId, preKeyRecord);
}
@Override
public boolean containsPreKey(int preKeyId) {
return axolotlDao().hasPreKey(account.id, preKeyId);
}
@Override
public void removePreKey(int preKeyId) {
axolotlDao().markPreKeyAsRemoved(account.id, preKeyId);
}
@Override
public SessionRecord loadSession(final SignalProtocolAddress signalProtocolAddress) {
final var address = AxolotlAddress.cast(signalProtocolAddress);
final var sessionRecord =
axolotlDao().getSessionRecord(account.id, address.getJid(), address.getDeviceId());
return sessionRecord == null ? new SessionRecord() : sessionRecord;
}
@Override
public List<Integer> getSubDeviceSessions(String name) {
return axolotlDao().getSessionDeviceIds(account.id, name);
}
@Override
public void storeSession(SignalProtocolAddress signalProtocolAddress, SessionRecord record) {
final var address = AxolotlAddress.cast(signalProtocolAddress);
axolotlDao().setSessionRecord(account, address.getJid(), address.getDeviceId(), record);
}
@Override
public boolean containsSession(SignalProtocolAddress signalProtocolAddress) {
final var address = AxolotlAddress.cast(signalProtocolAddress);
return axolotlDao().hasSession(account.id, address.getJid(), address.getDeviceId());
}
@Override
public void deleteSession(SignalProtocolAddress signalProtocolAddress) {
final var address = AxolotlAddress.cast(signalProtocolAddress);
axolotlDao().deleteSession(account.id, address.getJid(), address.getDeviceId());
}
@Override
public void deleteAllSessions(String name) {
axolotlDao().deleteSessions(account.id, name);
}
@Override
public SignedPreKeyRecord loadSignedPreKey(int signedPreKeyId) throws InvalidKeyIdException {
final var signedPreKeyRecord = axolotlDao().getSignedPreKey(account.id, signedPreKeyId);
if (signedPreKeyRecord == null) {
throw new InvalidKeyIdException(
String.format("signedPreKey %d not found", signedPreKeyId));
}
return signedPreKeyRecord;
}
@Override
public List<SignedPreKeyRecord> loadSignedPreKeys() {
return axolotlDao().getSignedPreKeys(account.id);
}
@Override
public void storeSignedPreKey(int signedPreKeyId, SignedPreKeyRecord record) {
axolotlDao().setSignedPreKey(account, signedPreKeyId, record);
}
@Override
public boolean containsSignedPreKey(int signedPreKeyId) {
return axolotlDao().hasSignedPreKey(account.id, signedPreKeyId);
}
@Override
public void removeSignedPreKey(int signedPreKeyId) {
axolotlDao().markSignedPreKeyAsRemoved(account.id, signedPreKeyId);
}
}

View file

@ -1,145 +0,0 @@
package im.conversations.android.database;
import android.content.Context;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import androidx.room.TypeConverters;
import im.conversations.android.database.dao.AccountDao;
import im.conversations.android.database.dao.ArchiveDao;
import im.conversations.android.database.dao.AvatarDao;
import im.conversations.android.database.dao.AxolotlDao;
import im.conversations.android.database.dao.BlockingDao;
import im.conversations.android.database.dao.BookmarkDao;
import im.conversations.android.database.dao.CertificateTrustDao;
import im.conversations.android.database.dao.ChatDao;
import im.conversations.android.database.dao.DiscoDao;
import im.conversations.android.database.dao.MessageDao;
import im.conversations.android.database.dao.NickDao;
import im.conversations.android.database.dao.PresenceDao;
import im.conversations.android.database.dao.RosterDao;
import im.conversations.android.database.dao.ServiceRecordDao;
import im.conversations.android.database.entity.AccountEntity;
import im.conversations.android.database.entity.ArchivePageEntity;
import im.conversations.android.database.entity.AvatarAdditionalEntity;
import im.conversations.android.database.entity.AvatarEntity;
import im.conversations.android.database.entity.AxolotlDeviceListEntity;
import im.conversations.android.database.entity.AxolotlDeviceListItemEntity;
import im.conversations.android.database.entity.AxolotlIdentityEntity;
import im.conversations.android.database.entity.AxolotlIdentityKeyPairEntity;
import im.conversations.android.database.entity.AxolotlPreKeyEntity;
import im.conversations.android.database.entity.AxolotlSessionEntity;
import im.conversations.android.database.entity.AxolotlSignedPreKeyEntity;
import im.conversations.android.database.entity.BlockedItemEntity;
import im.conversations.android.database.entity.BookmarkEntity;
import im.conversations.android.database.entity.BookmarkGroupEntity;
import im.conversations.android.database.entity.CertificateTrustEntity;
import im.conversations.android.database.entity.ChatEntity;
import im.conversations.android.database.entity.DiscoEntity;
import im.conversations.android.database.entity.DiscoExtensionEntity;
import im.conversations.android.database.entity.DiscoExtensionFieldEntity;
import im.conversations.android.database.entity.DiscoExtensionFieldValueEntity;
import im.conversations.android.database.entity.DiscoFeatureEntity;
import im.conversations.android.database.entity.DiscoIdentityEntity;
import im.conversations.android.database.entity.DiscoItemEntity;
import im.conversations.android.database.entity.GroupEntity;
import im.conversations.android.database.entity.MessageContentEntity;
import im.conversations.android.database.entity.MessageEntity;
import im.conversations.android.database.entity.MessageReactionEntity;
import im.conversations.android.database.entity.MessageStateEntity;
import im.conversations.android.database.entity.MessageVersionEntity;
import im.conversations.android.database.entity.MucStatusCodeEntity;
import im.conversations.android.database.entity.NickEntity;
import im.conversations.android.database.entity.PresenceEntity;
import im.conversations.android.database.entity.RosterItemEntity;
import im.conversations.android.database.entity.RosterItemGroupEntity;
import im.conversations.android.database.entity.ServiceRecordCacheEntity;
@Database(
entities = {
AccountEntity.class,
ArchivePageEntity.class,
AvatarAdditionalEntity.class,
AvatarEntity.class,
AxolotlDeviceListEntity.class,
AxolotlDeviceListItemEntity.class,
AxolotlIdentityEntity.class,
AxolotlIdentityKeyPairEntity.class,
AxolotlPreKeyEntity.class,
AxolotlSessionEntity.class,
AxolotlSignedPreKeyEntity.class,
BlockedItemEntity.class,
BookmarkEntity.class,
BookmarkGroupEntity.class,
CertificateTrustEntity.class,
ChatEntity.class,
DiscoEntity.class,
DiscoExtensionEntity.class,
DiscoExtensionFieldEntity.class,
DiscoExtensionFieldValueEntity.class,
DiscoFeatureEntity.class,
DiscoIdentityEntity.class,
DiscoItemEntity.class,
GroupEntity.class,
MessageEntity.class,
MessageStateEntity.class,
MessageContentEntity.class,
MessageVersionEntity.class,
MucStatusCodeEntity.class,
NickEntity.class,
PresenceEntity.class,
MessageReactionEntity.class,
RosterItemEntity.class,
RosterItemGroupEntity.class,
ServiceRecordCacheEntity.class
},
version = 1)
@TypeConverters(Converters.class)
public abstract class ConversationsDatabase extends RoomDatabase {
private static volatile ConversationsDatabase INSTANCE = null;
public static ConversationsDatabase getInstance(final Context context) {
if (INSTANCE != null) {
return INSTANCE;
}
synchronized (ConversationsDatabase.class) {
if (INSTANCE != null) {
return INSTANCE;
}
final Context application = context.getApplicationContext();
INSTANCE =
Room.databaseBuilder(application, ConversationsDatabase.class, "conversations")
.build();
return INSTANCE;
}
}
public abstract AccountDao accountDao();
public abstract ArchiveDao archiveDao();
public abstract AvatarDao avatarDao();
public abstract AxolotlDao axolotlDao();
public abstract BlockingDao blockingDao();
public abstract BookmarkDao bookmarkDao();
public abstract CertificateTrustDao certificateTrustDao();
public abstract ChatDao chatDao();
public abstract DiscoDao discoDao();
public abstract MessageDao messageDao();
public abstract NickDao nickDao();
public abstract PresenceDao presenceDao();
public abstract RosterDao rosterDao();
public abstract ServiceRecordDao serviceRecordDao();
}

View file

@ -1,190 +0,0 @@
package im.conversations.android.database;
import androidx.room.TypeConverter;
import com.google.common.base.Strings;
import com.google.common.net.MediaType;
import de.measite.minidns.DNSName;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.time.Instant;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.Jid;
import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.jid.parts.Resourcepart;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.IdentityKeyPair;
import org.whispersystems.libsignal.InvalidKeyException;
import org.whispersystems.libsignal.state.PreKeyRecord;
import org.whispersystems.libsignal.state.SessionRecord;
import org.whispersystems.libsignal.state.SignedPreKeyRecord;
public final class Converters {
private Converters() {}
@TypeConverter
public static Instant toInstant(final Long timestamp) {
return timestamp == null ? null : Instant.ofEpochMilli(timestamp);
}
@TypeConverter
public static Long fromInstant(final Instant instant) {
return instant == null ? null : instant.getEpochSecond() * 1000;
}
@TypeConverter
public static BareJid toBareJid(final String input) {
return input == null ? null : JidCreate.bareFromOrThrowUnchecked(input);
}
@TypeConverter
public static String fromBareJid(final BareJid jid) {
return jid == null ? null : jid.toString();
}
@TypeConverter
public static String fromJid(final Jid jid) {
return jid == null ? null : jid.toString();
}
@TypeConverter
public static Jid toJid(final String input) {
return input == null ? null : JidCreate.fromOrThrowUnchecked(input);
}
// TODO do we want to return null on null input?
@TypeConverter
public static Resourcepart toResourcePart(final String input) {
return Strings.isNullOrEmpty(input)
? Resourcepart.EMPTY
: Resourcepart.fromOrThrowUnchecked(input);
}
@TypeConverter
public static String fromResourcePart(final Resourcepart resourcepart) {
return resourcepart == null ? null : resourcepart.toString();
}
@TypeConverter
public static byte[] fromIdentityKey(final IdentityKey identityKey) {
return identityKey == null ? null : identityKey.serialize();
}
@TypeConverter
public static IdentityKey toIdentityKey(final byte[] serialized) {
if (serialized == null || serialized.length == 0) {
return null;
}
try {
return new IdentityKey(serialized, 0);
} catch (final InvalidKeyException e) {
throw new RuntimeException(e);
}
}
@TypeConverter
public static byte[] fromSessionRecord(final SessionRecord sessionRecord) {
return sessionRecord == null ? null : sessionRecord.serialize();
}
@TypeConverter
public static SessionRecord toSessionRecord(final byte[] serialized) {
if (serialized == null || serialized.length == 0) {
return null;
}
try {
return new SessionRecord(serialized);
} catch (final IOException e) {
throw new RuntimeException(e);
}
}
@TypeConverter
public static byte[] fromSignedPreKey(final SignedPreKeyRecord signedPreKeyRecord) {
return signedPreKeyRecord == null ? null : signedPreKeyRecord.serialize();
}
@TypeConverter
public static SignedPreKeyRecord toSignedPreKey(final byte[] serialized) {
if (serialized == null || serialized.length == 0) {
return null;
}
try {
return new SignedPreKeyRecord(serialized);
} catch (final IOException e) {
throw new RuntimeException(e);
}
}
@TypeConverter
public static byte[] fromPreKey(final PreKeyRecord preKeyRecord) {
return preKeyRecord == null ? null : preKeyRecord.serialize();
}
@TypeConverter
public static PreKeyRecord toPreKey(final byte[] serialized) {
if (serialized == null || serialized.length == 0) {
return null;
}
try {
return new PreKeyRecord(serialized);
} catch (final IOException e) {
throw new RuntimeException(e);
}
}
@TypeConverter
public static byte[] fromIdentityKeyPair(final IdentityKeyPair identityKeyPair) {
return identityKeyPair == null ? null : identityKeyPair.serialize();
}
@TypeConverter
public static IdentityKeyPair toIdentityKeyPair(final byte[] serialized) {
if (serialized == null || serialized.length == 0) {
return null;
}
try {
return new IdentityKeyPair(serialized);
} catch (final InvalidKeyException e) {
throw new RuntimeException(e);
}
}
@TypeConverter
public static String fromDNSName(final DNSName dnsName) {
return dnsName == null ? null : dnsName.toString();
}
@TypeConverter
public static DNSName toDNSName(final String dnsName) {
return dnsName == null ? null : DNSName.from(dnsName);
}
@TypeConverter
public static byte[] fromInetAddress(final InetAddress inetAddress) {
return inetAddress == null ? null : inetAddress.getAddress();
}
@TypeConverter
public static InetAddress toInetAddress(final byte[] address) {
if (address == null || address.length == 0) {
return null;
}
try {
return InetAddress.getByAddress(address);
} catch (final UnknownHostException e) {
return null;
}
}
@TypeConverter
public static String fromMediaType(final MediaType mediaType) {
return mediaType == null ? null : mediaType.toString();
}
@TypeConverter
public static MediaType toMediaType(final String mediaType) {
return mediaType == null ? null : MediaType.parse(mediaType);
}
}

View file

@ -1,224 +0,0 @@
package im.conversations.android.database;
import android.content.Context;
import android.security.keystore.KeyGenParameterSpec;
import androidx.annotation.NonNull;
import androidx.security.crypto.EncryptedFile;
import androidx.security.crypto.MasterKeys;
import com.google.common.collect.ImmutableMap;
import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import im.conversations.android.database.model.Account;
import im.conversations.android.database.model.Credential;
import im.conversations.android.xmpp.sasl.ChannelBindingMechanism;
import im.conversations.android.xmpp.sasl.HashedToken;
import im.conversations.android.xmpp.sasl.SaslMechanism;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.lang.reflect.Type;
import java.security.GeneralSecurityException;
import java.util.HashMap;
import java.util.Map;
// TODO cache credentials?!
public class CredentialStore {
private static final String FILENAME = "credential.store";
private static final Gson GSON = new GsonBuilder().create();
private static volatile CredentialStore INSTANCE;
private final Context context;
private CredentialStore(final Context context) {
this.context = context.getApplicationContext();
}
public static CredentialStore getInstance(final Context context) {
if (INSTANCE != null) {
return INSTANCE;
}
synchronized (CredentialStore.class) {
if (INSTANCE != null) {
return INSTANCE;
}
INSTANCE = new CredentialStore(context);
return INSTANCE;
}
}
public synchronized Credential get(final Account account) {
return getOrEmpty(account);
}
public void setPassword(final Account account, final String password)
throws GeneralSecurityException, IOException {
setPassword(account, password, false);
}
public synchronized void setPassword(
final Account account, final String password, final boolean autogeneratedPassword)
throws GeneralSecurityException, IOException {
final Credential credential = getOrEmpty(account);
final Credential modifiedCredential =
new Credential(
password,
autogeneratedPassword,
credential.pinnedMechanism,
credential.pinnedChannelBinding,
credential.fastMechanism,
credential.fastToken,
credential.preAuthRegistrationToken,
credential.privateKeyAlias);
// TODO ignore if unchanged
this.set(account, modifiedCredential);
}
public synchronized void setFastToken(
final Account account, final HashedToken.Mechanism mechanism, final String token)
throws GeneralSecurityException, IOException {
final Credential credential = getOrEmpty(account);
final Credential modifiedCredential =
new Credential(
credential.password,
credential.autogeneratedPassword,
credential.pinnedMechanism,
credential.pinnedChannelBinding,
mechanism.name(),
token,
credential.preAuthRegistrationToken,
credential.privateKeyAlias);
// TODO ignore if unchanged
this.set(account, modifiedCredential);
}
public synchronized void resetFastToken(final Account account)
throws GeneralSecurityException, IOException {
final Credential credential = getOrEmpty(account);
final Credential modifiedCredential =
new Credential(
credential.password,
credential.autogeneratedPassword,
credential.pinnedMechanism,
credential.pinnedChannelBinding,
null,
null,
credential.preAuthRegistrationToken,
credential.privateKeyAlias);
// TODO ignore if unchanged
this.set(account, modifiedCredential);
}
public synchronized void setPinnedMechanism(
final Account account, final SaslMechanism mechanism)
throws GeneralSecurityException, IOException {
final String pinnedMechanism = mechanism.getMechanism();
final String pinnedChannelBinding;
if (mechanism instanceof ChannelBindingMechanism) {
pinnedChannelBinding =
((ChannelBindingMechanism) mechanism).getChannelBinding().toString();
} else {
pinnedChannelBinding = null;
}
final Credential credential = getOrEmpty(account);
final Credential modifiedCredential =
new Credential(
credential.password,
credential.autogeneratedPassword,
pinnedMechanism,
pinnedChannelBinding,
credential.fastMechanism,
credential.fastToken,
credential.preAuthRegistrationToken,
credential.privateKeyAlias);
// TODO ignore if unchanged
this.set(account, modifiedCredential);
}
public synchronized void resetPinnedMechanism(final Account account)
throws GeneralSecurityException, IOException {
final Credential credential = getOrEmpty(account);
final Credential modifiedCredential =
new Credential(
credential.password,
credential.autogeneratedPassword,
null,
null,
credential.fastMechanism,
credential.fastToken,
credential.preAuthRegistrationToken,
credential.privateKeyAlias);
// TODO ignore if unchanged
this.set(account, modifiedCredential);
}
private Credential getOrEmpty(final Account account) {
final Map<String, Credential> store = loadOrEmpty();
final Credential credential = store.get(account.address.toString());
return credential == null ? Credential.empty() : credential;
}
private void set(@NonNull final Account account, @NonNull final Credential credential)
throws GeneralSecurityException, IOException {
final HashMap<String, Credential> credentialStore = new HashMap<>(loadOrEmpty());
credentialStore.put(account.address.toString(), credential);
store(credentialStore);
}
private Map<String, Credential> loadOrEmpty() {
final Map<String, Credential> store;
try {
store = load();
} catch (final Exception e) {
return ImmutableMap.of();
}
return store == null ? ImmutableMap.of() : store;
}
private Map<String, Credential> load() throws GeneralSecurityException, IOException {
final EncryptedFile encryptedFile = getEncryptedFile();
try (final FileInputStream inputStream = encryptedFile.openFileInput()) {
final Type type = new TypeToken<Map<String, Credential>>() {}.getType();
return GSON.fromJson(new InputStreamReader(inputStream), type);
}
}
private void store(final Map<String, Credential> store)
throws GeneralSecurityException, IOException {
final File file = getCredentialStoreFile();
if (file.delete()) {
// Log.d(Config.LOGTAG,"delete old credential file: "+file.getAbsolutePath());
}
final EncryptedFile encryptedFile = getEncryptedFile(file);
try (final OutputStreamWriter writer =
new OutputStreamWriter(encryptedFile.openFileOutput())) {
GSON.toJson(store, writer);
writer.flush();
}
}
private EncryptedFile getEncryptedFile() throws GeneralSecurityException, IOException {
return getEncryptedFile(getCredentialStoreFile());
}
private EncryptedFile getEncryptedFile(final File file)
throws GeneralSecurityException, IOException {
final KeyGenParameterSpec keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC;
final String mainKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec);
return new EncryptedFile.Builder(
file,
context,
mainKeyAlias,
EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB)
.build();
}
private File getCredentialStoreFile() {
return new File(context.getFilesDir(), FILENAME);
}
}

View file

@ -1,24 +0,0 @@
package im.conversations.android.database;
import im.conversations.android.database.model.ChatType;
import java.util.Arrays;
import org.jxmpp.jid.BareJid;
public interface KnownSender {
default boolean isKnownSender() {
final var chatType = getChatType();
final var membersOnlyNonAnonymous = isMembersOnlyNonAnonymous();
final var sender = getSender();
return chatType == ChatType.INDIVIDUAL
|| (Arrays.asList(ChatType.MUC, ChatType.MUC_PM).contains(chatType)
&& membersOnlyNonAnonymous
&& sender != null);
}
ChatType getChatType();
boolean isMembersOnlyNonAnonymous();
BareJid getSender();
}

View file

@ -1,93 +0,0 @@
package im.conversations.android.database.dao;
import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Transaction;
import com.google.common.util.concurrent.ListenableFuture;
import im.conversations.android.database.entity.AccountEntity;
import im.conversations.android.database.model.Account;
import im.conversations.android.database.model.AccountIdentifier;
import im.conversations.android.database.model.Connection;
import java.util.List;
import org.jxmpp.jid.BareJid;
@Dao
public abstract class AccountDao {
@Query("SELECT EXISTS (SELECT id FROM account WHERE address=:address)")
public abstract boolean hasAccount(BareJid address);
@Query("SELECT NOT EXISTS (SELECT id FROM account)")
public abstract LiveData<Boolean> hasNoAccounts();
@Insert
public abstract long insert(final AccountEntity account);
@Query("SELECT id,address,randomSeed FROM account WHERE enabled = 1")
public abstract ListenableFuture<List<Account>> getEnabledAccounts();
@Query("SELECT id,address,randomSeed FROM account WHERE address=:address AND enabled=1")
public abstract ListenableFuture<Account> getEnabledAccount(BareJid address);
@Query("SELECT id,address,randomSeed FROM account WHERE id=:id AND enabled=1")
public abstract ListenableFuture<Account> getEnabledAccount(long id);
@Query("SELECT id,address FROM account")
public abstract LiveData<List<AccountIdentifier>> getAccounts();
@Query("SELECT hostname,port,directTls FROM account WHERE id=:id AND hostname IS NOT null")
public abstract Connection getConnectionSettings(long id);
@Query("SELECT resource FROM account WHERE id=:id")
public abstract String getResource(long id);
@Query("SELECT rosterVersion FROM account WHERE id=:id")
public abstract String getRosterVersion(long id);
@Query("SELECT quickStartAvailable FROM account where id=:id")
public abstract boolean quickStartAvailable(long id);
@Query("SELECT loginAndBind FROM account where id=:id")
public abstract boolean loginAndBind(long id);
@Query(
"UPDATE account set quickStartAvailable=:available WHERE id=:id AND"
+ " quickStartAvailable != :available")
public abstract void setQuickStartAvailable(long id, boolean available);
@Query(
"UPDATE account set loginAndBind=:loginAndBind WHERE id=:id AND"
+ " loginAndBind != :loginAndBind")
public abstract void setLoginAndBind(long id, boolean loginAndBind);
@Query(
"UPDATE account set showErrorNotification=:showErrorNotification WHERE id=:id AND"
+ " showErrorNotification != :showErrorNotification")
public abstract int setShowErrorNotification(long id, boolean showErrorNotification);
@Query("UPDATE account set resource=:resource WHERE id=:id")
public abstract void setResource(long id, String resource);
@Query("DELETE FROM account WHERE id=:id")
public abstract int delete(long id);
@Query(
"UPDATE account SET hostname=:hostname, port=:port, directTls=:directTls WHERE"
+ " id=:account")
protected abstract int setConnection(
long account, String hostname, int port, boolean directTls);
@Transaction
public void setConnection(final Account account, final Connection connection) {
final var count =
setConnection(
account.id, connection.hostname, connection.port, connection.directTls);
if (count != 1) {
throw new IllegalStateException("Could not update account");
}
}
// TODO on disable set resource to null
}

View file

@ -1,183 +0,0 @@
package im.conversations.android.database.dao;
import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Query;
import androidx.room.Transaction;
import androidx.room.Upsert;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import im.conversations.android.database.entity.ArchivePageEntity;
import im.conversations.android.database.model.Account;
import im.conversations.android.database.model.StanzaId;
import im.conversations.android.xml.Namespace;
import im.conversations.android.xmpp.Range;
import im.conversations.android.xmpp.manager.ArchiveManager;
import java.util.List;
import java.util.Objects;
import org.jxmpp.jid.Jid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Dao
public abstract class ArchiveDao extends BaseDao {
private static final Logger LOGGER = LoggerFactory.getLogger(ArchiveDao.class);
@Transaction
public List<Range> resetLivePage(final Account account, final Jid archive) {
// if the server support extended MAM or catch-up query from (live)page.end to now can use
// after-id reverse towards that (this means newer messages will be fetched earlier); if and
// when we support this we also need a new page type called END that will eventually be
// deleted once we need page.end
final boolean emitRangeAfter =
hasDiscoItemFeature(
account.id, archive, Namespace.MESSAGE_ARCHIVE_MANAGEMENT_EXTENDED);
final var page =
getPage(
account.id,
archive,
ArchivePageEntity.Type.START,
ArchivePageEntity.Type.MIDDLE);
final var livePage = getPage(account.id, archive, ArchivePageEntity.Type.LIVE);
if (page == null && livePage == null) {
LOGGER.info("Emitting initial query for {}", archive);
return ImmutableList.of(new Range(Range.Order.REVERSE, null));
}
final ImmutableList.Builder<Range> queryRangeBuilder = new ImmutableList.Builder<>();
final boolean gapLess = page != null && livePage != null && page.end.equals(livePage.start);
if (gapLess) {
LOGGER.info("Page and live page for {} were gap-less", archive);
page.end = livePage.end;
insert(page);
if (page.type != ArchivePageEntity.Type.START && !page.reachedMaxPages) {
queryRangeBuilder.add(new Range(Range.Order.REVERSE, page.start));
}
queryRangeBuilder.add(new Range(Range.Order.NORMAL, livePage.end));
} else if (page != null) {
LOGGER.info("Ignoring live page for {}", archive);
// this will simply ignore the last live page and overwrite it
if (page.type != ArchivePageEntity.Type.START && !page.reachedMaxPages) {
queryRangeBuilder.add(new Range(Range.Order.REVERSE, page.start));
}
queryRangeBuilder.add(new Range(Range.Order.NORMAL, page.end));
} else {
LOGGER.info("Converting live page into regular page for {}", archive);
insert(
ArchivePageEntity.of(
account,
archive,
ArchivePageEntity.Type.MIDDLE,
livePage.start,
livePage.end,
false));
queryRangeBuilder.add(new Range(Range.Order.REVERSE, livePage.start));
queryRangeBuilder.add(new Range(Range.Order.NORMAL, livePage.end));
}
if (livePage != null) {
delete(livePage);
}
return queryRangeBuilder.build();
}
public void submitPage(
final Account account,
final Jid archive,
final Range range,
final ArchiveManager.QueryResult queryResult,
final boolean reachedMaxPagesReversing) {
if (reachedMaxPagesReversing) {
Preconditions.checkState(
range.order == Range.Order.REVERSE,
"reachedMaxPagesReversing can only be true when reversing");
}
final var isComplete = queryResult.isComplete;
final var page = queryResult.page;
final var existingPage =
getPage(
account.id,
archive,
ArchivePageEntity.Type.START,
ArchivePageEntity.Type.MIDDLE);
final boolean isStart = range.order == Range.Order.REVERSE && isComplete;
if (existingPage == null) {
insert(
ArchivePageEntity.of(
account,
archive,
isStart ? ArchivePageEntity.Type.START : ArchivePageEntity.Type.MIDDLE,
page.first,
page.last,
reachedMaxPagesReversing));
} else {
if (range.order == Range.Order.REVERSE) {
Preconditions.checkState(
Objects.equals(range.id, existingPage.start),
"Reversing range did not match start of existing page");
existingPage.start = page.first;
existingPage.type =
isStart ? ArchivePageEntity.Type.START : ArchivePageEntity.Type.MIDDLE;
} else if (range.order == Range.Order.NORMAL) {
Preconditions.checkState(
Objects.equals(range.id, existingPage.end),
"Normal range did not match end of existing page");
existingPage.end = page.last;
} else {
throw new IllegalStateException(String.format("Unknown order %s", range.order));
}
existingPage.reachedMaxPages = existingPage.reachedMaxPages || reachedMaxPagesReversing;
insert(existingPage);
}
final boolean lastIsLive =
(range.order == Range.Order.REVERSE && range.id == null)
|| (range.order == Range.Order.NORMAL && queryResult.isComplete);
if (lastIsLive) {
final var existingLivePage = getPage(account.id, archive, ArchivePageEntity.Type.LIVE);
if (existingLivePage != null) {
existingLivePage.start = page.last;
insert(existingLivePage);
} else {
insert(
ArchivePageEntity.of(
account,
archive,
ArchivePageEntity.Type.LIVE,
page.last,
page.last,
false));
}
}
}
public void setLivePageStanzaId(final Account account, final StanzaId stanzaId) {
LOGGER.info("set live page stanza id {}", stanzaId);
final var currentLivePage = getPage(account.id, stanzaId.by, ArchivePageEntity.Type.LIVE);
if (currentLivePage != null) {
currentLivePage.end = stanzaId.id;
insert(currentLivePage);
} else {
insert(
ArchivePageEntity.of(
account,
stanzaId.by,
ArchivePageEntity.Type.LIVE,
stanzaId.id,
stanzaId.id,
false));
}
}
@Delete
protected abstract void delete(final ArchivePageEntity entity);
@Upsert
protected abstract void insert(final ArchivePageEntity entity);
@Query(
"SELECT * FROM archive_page WHERE accountId=:account AND archive=:archive AND type"
+ " IN(:type)")
protected abstract ArchivePageEntity getPage(
long account, Jid archive, ArchivePageEntity.Type... type);
}

View file

@ -1,31 +0,0 @@
package im.conversations.android.database.dao;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import com.google.common.collect.Collections2;
import im.conversations.android.database.entity.AvatarAdditionalEntity;
import im.conversations.android.database.entity.AvatarEntity;
import im.conversations.android.database.model.Account;
import im.conversations.android.xmpp.model.avatar.Info;
import java.util.Collection;
import org.jxmpp.jid.BareJid;
@Dao
public abstract class AvatarDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
protected abstract long insert(AvatarEntity avatar);
@Insert
protected abstract void insert(Collection<AvatarAdditionalEntity> entities);
public void set(
final Account account,
final BareJid address,
final Info thumbnail,
final Collection<Info> additional) {
final long id = insert(AvatarEntity.of(account, address, thumbnail));
insert(Collections2.transform(additional, a -> AvatarAdditionalEntity.of(id, a)));
}
}

View file

@ -1,249 +0,0 @@
package im.conversations.android.database.dao;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
import androidx.room.Transaction;
import com.google.common.collect.Collections2;
import im.conversations.android.axolotl.AxolotlAddress;
import im.conversations.android.database.entity.AxolotlDeviceListEntity;
import im.conversations.android.database.entity.AxolotlDeviceListItemEntity;
import im.conversations.android.database.entity.AxolotlIdentityEntity;
import im.conversations.android.database.entity.AxolotlIdentityKeyPairEntity;
import im.conversations.android.database.entity.AxolotlPreKeyEntity;
import im.conversations.android.database.entity.AxolotlSessionEntity;
import im.conversations.android.database.entity.AxolotlSignedPreKeyEntity;
import im.conversations.android.database.model.Account;
import im.conversations.android.database.model.Trust;
import im.conversations.android.xmpp.model.error.Condition;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import org.jxmpp.jid.BareJid;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.IdentityKeyPair;
import org.whispersystems.libsignal.ecc.Curve;
import org.whispersystems.libsignal.state.PreKeyRecord;
import org.whispersystems.libsignal.state.SessionRecord;
import org.whispersystems.libsignal.state.SignedPreKeyRecord;
@Dao
public abstract class AxolotlDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
protected abstract long insert(AxolotlDeviceListEntity entity);
@Insert
protected abstract void insert(Collection<AxolotlDeviceListItemEntity> entities);
@Insert(onConflict = OnConflictStrategy.IGNORE)
protected abstract void insertUnconfirmed(Collection<AxolotlDeviceListItemEntity> entities);
@Transaction
public void setDeviceList(Account account, BareJid from, Set<Integer> deviceIds) {
final var listId = insert(AxolotlDeviceListEntity.of(account.id, from));
insert(
Collections2.transform(
deviceIds,
deviceId -> AxolotlDeviceListItemEntity.of(listId, deviceId, true)));
}
@Transaction
public void setUnconfirmedDevices(
final Account account, final BareJid address, Set<Integer> unconfirmedDeviceIds) {
final Long listId = getDeviceListId(account.id, address);
if (listId == null) {
return;
}
insertUnconfirmed(
Collections2.transform(
unconfirmedDeviceIds,
deviceId -> AxolotlDeviceListItemEntity.of(listId, deviceId, false)));
}
@Query("SELECT id FROM axolotl_device_list WHERE accountId=:account AND address=:address")
abstract Long getDeviceListId(long account, final BareJid address);
@Query(
"SELECT EXISTS(SELECT deviceId FROM axolotl_device_list JOIN axolotl_device_list_item"
+ " ON axolotl_device_list.id=axolotl_device_list_item.deviceListId WHERE"
+ " accountId=:account AND address=:address AND deviceId=:deviceId)")
public abstract boolean hasDeviceId(
final long account, final BareJid address, final int deviceId);
public boolean hasDeviceId(final Account account, final AxolotlAddress axolotlAddress) {
return hasDeviceId(account.id, axolotlAddress.getJid(), axolotlAddress.getDeviceId());
}
@Transaction
public void setDeviceListError(
final Account account, final BareJid address, Condition condition) {
insert(AxolotlDeviceListEntity.of(account.id, address, condition.getName()));
}
@Transaction
public void setDeviceListParsingError(final Account account, final BareJid address) {
insert(AxolotlDeviceListEntity.ofParsingIssue(account.id, address));
}
@Transaction
public IdentityKeyPair getOrCreateIdentityKeyPair(final Account account) {
final var existing = getIdentityKeyPair(account.id);
if (existing != null) {
return existing;
}
final var ecKeyPair = Curve.generateKeyPair();
final var identityKeyPair =
new IdentityKeyPair(
new IdentityKey(ecKeyPair.getPublicKey()), ecKeyPair.getPrivateKey());
insert(AxolotlIdentityKeyPairEntity.of(account, identityKeyPair));
return identityKeyPair;
}
@Insert
protected abstract void insert(AxolotlIdentityKeyPairEntity entity);
@Query("SELECT identityKeyPair FROM axolotl_identity_key_pair WHERE accountId=:account")
protected abstract IdentityKeyPair getIdentityKeyPair(long account);
@Query(
"SELECT signedPreKeyRecord FROM axolotl_signed_pre_key WHERE accountId=:account AND"
+ " signedPreKeyId=:signedPreKeyId")
public abstract SignedPreKeyRecord getSignedPreKey(long account, int signedPreKeyId);
@Query(
"SELECT NOT EXISTS(SELECT signedPreKeyRecord FROM axolotl_signed_pre_key WHERE"
+ " accountId=:account AND signedPreKeyId=:signedPreKeyId)")
public abstract boolean hasNotSignedPreKey(long account, int signedPreKeyId);
@Query(
"SELECT signedPreKeyRecord FROM axolotl_signed_pre_key WHERE accountId=:account ORDER"
+ " BY signedPreKeyId DESC LIMIT 1")
public abstract SignedPreKeyRecord getLatestSignedPreKey(long account);
@Transaction
public boolean setIdentity(
final Account account,
final BareJid address,
final IdentityKey identityKey,
final Trust trust) {
final var existing = getIdentityKey(account.id, address, identityKey);
if (existing == null || !existing.equals(identityKey)) {
insert(AxolotlIdentityEntity.of(account, address, identityKey, trust));
return true;
} else {
return false;
}
}
public boolean isAnyIdentityVerified(final Account account, final BareJid address) {
return isAnyIdentityTrustStatus(
account.id, address, Arrays.asList(Trust.VERIFIED, Trust.VERIFIED_X509));
}
@Query(
"SELECT EXISTS (SELECT id FROM axolotl_identity WHERE accountId=:account AND"
+ " address=:address AND trust IN(:trusts))")
abstract boolean isAnyIdentityTrustStatus(
long account, BareJid address, Collection<Trust> trusts);
@Insert(onConflict = OnConflictStrategy.REPLACE)
protected abstract void insert(AxolotlIdentityEntity axolotlIdentityEntity);
@Query(
"SELECT identityKey FROM AXOLOTL_IDENTITY WHERE accountId=:account AND"
+ " address=:address AND identityKey=:identityKey")
protected abstract IdentityKey getIdentityKey(
long account, BareJid address, IdentityKey identityKey);
@Query(
"SELECT preKeyRecord FROM axolotl_pre_key WHERE accountId=:account AND"
+ " preKeyid=:preKeyId")
public abstract PreKeyRecord getPreKey(long account, int preKeyId);
@Query("SELECT MAX(preKeyId) FROM axolotl_pre_key WHERE accountId=:account")
public abstract Integer getMaxPreKeyId(final long account);
@Query("SELECT COUNT(id) FROM axolotl_pre_key WHERE accountId=:account AND removed=0")
public abstract int getExistingPreKeyCount(final long account);
public void setPreKey(final Account account, int preKeyId, PreKeyRecord preKeyRecord) {
insert(AxolotlPreKeyEntity.of(account, preKeyId, preKeyRecord));
}
@Insert(onConflict = OnConflictStrategy.REPLACE)
protected abstract void insert(AxolotlPreKeyEntity axolotlPreKeyEntity);
public void setPreKeys(final Account account, final Collection<PreKeyRecord> preKeyRecords) {
insertPreKeys(
Collections2.transform(
preKeyRecords, r -> AxolotlPreKeyEntity.of(account, r.getId(), r)));
}
@Insert(onConflict = OnConflictStrategy.REPLACE)
protected abstract void insertPreKeys(Collection<AxolotlPreKeyEntity> entities);
@Insert(onConflict = OnConflictStrategy.REPLACE)
protected abstract void insert(AxolotlSessionEntity axolotlSessionEntity);
@Query(
"SELECT EXISTS(SELECT id FROM axolotl_pre_key WHERE accountId=:account AND"
+ " preKeyId=:preKeyId)")
public abstract boolean hasPreKey(long account, int preKeyId);
@Query(
"SELECT EXISTS(SELECT id FROM axolotl_signed_pre_key WHERE accountId=:account AND"
+ " signedPreKeyId=:signedPreKeyId)")
public abstract boolean hasSignedPreKey(long account, int signedPreKeyId);
@Query("UPDATE axolotl_pre_key SET removed=1 WHERE accountId=:account AND preKeyId=:preKeyId")
public abstract void markPreKeyAsRemoved(long account, int preKeyId);
@Query(
"UPDATE axolotl_signed_pre_key SET removed=1 WHERE accountId=:account AND"
+ " signedPreKeyId=:signedPreKeyId")
public abstract void markSignedPreKeyAsRemoved(long account, int signedPreKeyId);
@Query(
"SELECT sessionRecord FROM axolotl_session WHERE accountId=:account AND"
+ " address=:address AND deviceId=:deviceId")
public abstract SessionRecord getSessionRecord(long account, BareJid address, int deviceId);
@Query("SELECT deviceId FROM axolotl_session WHERE accountId=:account AND address=:address")
public abstract List<Integer> getSessionDeviceIds(long account, String address);
public void setSessionRecord(
Account account, BareJid address, int deviceId, SessionRecord record) {
insert(AxolotlSessionEntity.of(account, address, deviceId, record));
}
@Query(
"SELECT EXISTS(SELECT id FROM axolotl_session WHERE accountId=:account AND"
+ " address=:address AND deviceId=:deviceId)")
public abstract boolean hasSession(long account, BareJid address, int deviceId);
@Query(
"DELETE FROM axolotl_session WHERE accountId=:account AND address=:address AND"
+ " deviceId=:deviceId")
public abstract void deleteSession(long account, BareJid address, int deviceId);
@Query("DELETE FROM axolotl_session WHERE accountId=:account AND address=:address")
public abstract void deleteSessions(long account, String address);
@Query(
"SELECT signedPreKeyRecord FROM axolotl_signed_pre_key WHERE accountId=:account AND"
+ " removed=0")
public abstract List<SignedPreKeyRecord> getSignedPreKeys(long account);
@Query("SELECT preKeyRecord FROM axolotl_pre_key WHERE accountId=:account AND removed=0")
public abstract List<PreKeyRecord> getPreKeys(long account);
public void setSignedPreKey(Account account, int signedPreKeyId, SignedPreKeyRecord record) {
insert(AxolotlSignedPreKeyEntity.of(account, signedPreKeyId, record));
}
@Insert(onConflict = OnConflictStrategy.REPLACE)
protected abstract void insert(AxolotlSignedPreKeyEntity signedPreKeyEntity);
}

View file

@ -1,14 +0,0 @@
package im.conversations.android.database.dao;
import androidx.room.Query;
import org.jxmpp.jid.Jid;
public abstract class BaseDao {
@Query(
"SELECT EXISTS (SELECT disco_item.id FROM disco_item JOIN disco_feature on"
+ " disco_item.discoId=disco_feature.discoId WHERE accountId=:account AND"
+ " address=:entity AND feature=:feature)")
protected abstract boolean hasDiscoItemFeature(
final long account, final Jid entity, final String feature);
}

View file

@ -1,39 +0,0 @@
package im.conversations.android.database.dao;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
import androidx.room.Transaction;
import com.google.common.collect.Collections2;
import im.conversations.android.database.entity.BlockedItemEntity;
import im.conversations.android.database.model.Account;
import im.conversations.android.xmpp.model.blocking.Item;
import java.util.Collection;
import org.jxmpp.jid.Jid;
@Dao
public abstract class BlockingDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
abstract void insert(Collection<BlockedItemEntity> entities);
@Query("DELETE FROM blocked WHERE accountId=:account")
public abstract void clear(final long account);
@Transaction
public void set(final Account account, final Collection<Item> blockedItems) {
final var entities =
Collections2.transform(
blockedItems, i -> BlockedItemEntity.of(account.id, i.getJid()));
clear(account.id);
insert(entities);
}
public void add(Account account, Collection<Jid> addresses) {
insert(Collections2.transform(addresses, a -> BlockedItemEntity.of(account.id, a)));
}
@Query("DELETE from blocked WHERE accountId=:account AND address IN(:addresses)")
public abstract void remove(final long account, Collection<Jid> addresses);
}

View file

@ -1,50 +0,0 @@
package im.conversations.android.database.dao;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Transaction;
import com.google.common.collect.Collections2;
import im.conversations.android.database.entity.BookmarkEntity;
import im.conversations.android.database.model.Account;
import im.conversations.android.xmpp.model.bookmark.Conference;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import org.jxmpp.jid.Jid;
import org.jxmpp.jid.impl.JidCreate;
@Dao
public abstract class BookmarkDao {
@Query("DELETE FROM bookmark WHERE accountId=:account")
public abstract void deleteAll(final long account);
@Query("DELETE FROM bookmark WHERE accountId=:account and address IN(:addresses)")
public abstract void delete(final long account, Collection<Jid> addresses);
@Insert
protected abstract void insert(Collection<BookmarkEntity> bookmarks);
@Transaction
public void updateItems(final Account account, Map<String, Conference> items) {
final Collection<Jid> addresses =
Collections2.transform(items.keySet(), JidCreate::fromOrNull);
delete(account.id, addresses);
final var entities =
Collections2.transform(
items.entrySet(), entry -> BookmarkEntity.of(account.id, entry));
// non null filtering is required because BookmarkEntity.of() can return null values if the
insert(Collections2.filter(entities, Objects::nonNull));
}
@Transaction
public void setItems(Account account, Map<String, Conference> items) {
deleteAll(account.id);
final var entities =
Collections2.transform(
items.entrySet(), entry -> BookmarkEntity.of(account.id, entry));
// non null filtering is required because BookmarkEntity.of() can return null values if the
insert(Collections2.filter(entities, Objects::nonNull));
}
}

View file

@ -1,26 +0,0 @@
package im.conversations.android.database.dao;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
import im.conversations.android.database.entity.CertificateTrustEntity;
import im.conversations.android.database.model.Account;
import im.conversations.android.tls.ScopeFingerprint;
@Dao
public abstract class CertificateTrustDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
public abstract void insert(final CertificateTrustEntity certificateTrustEntity);
@Query(
"SELECT EXISTS (SELECT id FROM certificate_trust WHERE accountId=:account AND"
+ " scope=:scope AND fingerprint=:fingerprint)")
protected abstract boolean isTrusted(
final long account, final String scope, final byte[] fingerprint);
public boolean isTrusted(final Account account, final ScopeFingerprint scopeFingerprint) {
return isTrusted(account.id, scopeFingerprint.scope, scopeFingerprint.fingerprint.array());
}
}

View file

@ -1,263 +0,0 @@
package im.conversations.android.database.dao;
import androidx.lifecycle.LiveData;
import androidx.paging.PagingSource;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Transaction;
import com.google.common.util.concurrent.ListenableFuture;
import im.conversations.android.database.entity.ChatEntity;
import im.conversations.android.database.entity.MucStatusCodeEntity;
import im.conversations.android.database.model.Account;
import im.conversations.android.database.model.AccountIdentifier;
import im.conversations.android.database.model.ChatFilter;
import im.conversations.android.database.model.ChatIdentifier;
import im.conversations.android.database.model.ChatInfo;
import im.conversations.android.database.model.ChatOverviewItem;
import im.conversations.android.database.model.ChatType;
import im.conversations.android.database.model.GroupIdentifier;
import im.conversations.android.database.model.MucState;
import im.conversations.android.database.model.MucWithNick;
import im.conversations.android.xmpp.model.stanza.Message;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.jxmpp.jid.Jid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Dao
public abstract class ChatDao {
private static final Logger LOGGER = LoggerFactory.getLogger(ChatDao.class);
@Transaction
public ChatIdentifier getOrCreateChat(
final Account account,
final Jid remote,
final Message.Type messageType,
final boolean multiUserChat) {
final ChatType chatType;
if (multiUserChat
&& Arrays.asList(Message.Type.CHAT, Message.Type.NORMAL).contains(messageType)) {
chatType = ChatType.MUC_PM;
} else if (messageType == Message.Type.GROUPCHAT) {
chatType = ChatType.MUC;
} else {
chatType = ChatType.INDIVIDUAL;
}
final Jid address = chatType == ChatType.MUC_PM ? remote : remote.asBareJid();
final ChatIdentifier existing = get(account.id, address);
if (existing != null) {
return existing;
}
// TODO do not create entity for 'error'
final var entity = new ChatEntity();
entity.accountId = account.id;
entity.address = address.toString();
entity.type = chatType;
entity.archived = true;
final long id = insert(entity);
return new ChatIdentifier(id, address, chatType, true);
}
@Query(
"SELECT id,address,type,archived FROM chat WHERE accountId=:accountId AND"
+ " address=:address")
protected abstract ChatIdentifier get(final long accountId, final Jid address);
@Query(
"SELECT id,address,type,archived FROM chat WHERE accountId=:accountId AND"
+ " address=:address AND type=:chatType")
public abstract ChatIdentifier get(
final long accountId, final Jid address, final ChatType chatType);
@Insert
protected abstract long insert(ChatEntity chatEntity);
@Query("UPDATE chat SET archived=:archived WHERE chat.id=:chatId")
public abstract void setArchived(final long chatId, final boolean archived);
@Query(
"UPDATE chat SET archived=:archived WHERE chat.accountId=:account AND"
+ " chat.address=:address")
protected abstract void setArchived(
final long account, final String address, final boolean archived);
@Query("SELECT id,name FROM `group` ORDER BY name")
public abstract LiveData<List<GroupIdentifier>> getGroups();
@Transaction
public void syncWithBookmarks(final Account account) {
final var chatsNotInBookmarks = getChatsNotInBookmarks(account.id, ChatType.MUC);
final var bookmarksNotInChat = getBookmarksNotInChats(account.id, ChatType.MUC);
LOGGER.info("chatsNotInBookmark {}", chatsNotInBookmarks);
LOGGER.info("bookmarkNotInChat {}", bookmarksNotInChat);
archive(account.id, chatsNotInBookmarks);
createOrUnarchiveMuc(account.id, bookmarksNotInChat);
}
private void archive(final long account, final List<String> addresses) {
for (final String address : addresses) {
setArchived(account, address, true);
}
}
private void createOrUnarchiveMuc(final long account, final List<Jid> addresses) {
for (final Jid address : addresses) {
createOrUnarchiveMuc(account, address);
}
}
private void createOrUnarchiveMuc(final long account, final Jid address) {
final var bareJid = address.asBareJid();
final var existing = get(account, bareJid);
if (existing != null) {
if (existing.archived) {
setArchived(existing.id, false);
}
return;
}
final var entity = new ChatEntity();
entity.accountId = account;
entity.address = bareJid.toString();
entity.type = ChatType.MUC;
entity.archived = false;
insert(entity);
}
@Query(
"SELECT chat.address FROM chat WHERE chat.accountId=:account AND chat.type=:chatType"
+ " AND archived=0 EXCEPT SELECT bookmark.address FROM bookmark WHERE"
+ " bookmark.accountId=:account AND bookmark.autoJoin=1")
protected abstract List<String> getChatsNotInBookmarks(long account, ChatType chatType);
@Query(
"SELECT bookmark.address FROM bookmark WHERE bookmark.accountId=:account AND"
+ " bookmark.autoJoin=1 EXCEPT SELECT chat.address FROM chat WHERE"
+ " chat.accountId=:account AND chat.type=:chatType AND archived=0")
protected abstract List<Jid> getBookmarksNotInChats(long account, ChatType chatType);
@Query(
"SELECT chat.id as chatId,chat.address,bookmark.nick as nickBookmark,nick.nick as"
+ " nickAccount FROM chat LEFT JOIN bookmark ON chat.accountId=bookmark.accountId"
+ " AND chat.address=bookmark.address JOIN account ON account.id=chat.accountId"
+ " LEFT JOIN nick ON nick.accountId=chat.accountId AND"
+ " nick.address=account.address WHERE chat.accountId=:account AND"
+ " chat.type=:chatType AND chat.archived=0 AND chat.mucState IS NULL")
public abstract ListenableFuture<List<MucWithNick>> getMultiUserChats(
final long account, final ChatType chatType);
@Query("UPDATE chat SET mucState=:mucState, errorCondition=:errorCondition WHERE id=:chatId")
protected abstract void setMucStateInternal(
final long chatId, final MucState mucState, final String errorCondition);
@Transaction
public void setMucState(
final long chatId, final MucState mucState, final String errorCondition) {
setMucStateInternal(chatId, mucState, errorCondition);
deleteStatusCodes(chatId);
}
@Transaction
public void setMucState(
final long chatId, final MucState mucState, final Collection<Integer> statusCodes) {
setMucStateInternal(chatId, mucState, null);
deleteStatusCodes(chatId);
insertStatusCode(MucStatusCodeEntity.of(chatId, statusCodes));
}
@Transaction
public void setMucState(final long chatId, final MucState mucState) {
setMucStateInternal(chatId, mucState, null);
deleteStatusCodes(chatId);
}
@Transaction
public void resetMucStates() {
this.nullMucStates();
this.deleteStatusCodes();
}
@Insert
protected abstract void insertStatusCode(final Collection<MucStatusCodeEntity> entities);
@Query("UPDATE chat SET mucState=null,errorCondition=null")
protected abstract void nullMucStates();
@Query("DELETE FROM muc_status_code")
protected abstract void deleteStatusCodes();
@Query("DELETE FROM muc_status_code WHERE chatId=:chatId")
protected abstract void deleteStatusCodes(final long chatId);
// TODO select vCardPhoto for c.type='MUC_PM'
@Transaction
@Query(
"SELECT c.id,c.accountId,c.address,c.type,m.sentAt,m.senderIdentity as"
+ " sender,m.outgoing,m.latestVersion as"
+ " version,m.toBare,m.toResource,m.fromBare,m.fromResource,(SELECT count(id) FROM"
+ " message WHERE chatId=c.id) as unread,(CASE WHEN c.type = 'INDIVIDUAL' THEN"
+ " (SELECT name FROM roster WHERE roster.accountId=c.accountId AND"
+ " roster.address=c.address) ELSE NULL END) as rosterName,(CASE WHEN c.type ="
+ " 'INDIVIDUAL' THEN (SELECT nick FROM nick WHERE nick.accountId=c.accountId AND"
+ " nick.address=c.address) ELSE NULL END) as nick,(CASE WHEN c.type IN('MUC') THEN"
+ " (SELECT name FROM roster WHERE roster.accountId=c.accountId AND"
+ " roster.address=m.senderIdentity) ELSE NULL END) as senderRosterName,(CASE WHEN"
+ " c.type IN ('MUC') THEN (SELECT nick FROM nick WHERE nick.accountId=c.accountId"
+ " AND nick.address=m.senderIdentity) ELSE NULL END) as senderNick,(SELECT"
+ " identity.name FROM disco_item JOIN disco_identity identity ON"
+ " disco_item.discoId=identity.discoId WHERE disco_item.accountId=c.accountId AND"
+ " disco_item.address=c.address LIMIT 1) as discoIdentityName,(SELECT name FROM"
+ " bookmark WHERE bookmark.accountId=c.accountId AND bookmark.address=c.address)"
+ " as bookmarkName,(CASE WHEN c.type='MUC' THEN (SELECT vCardPhoto FROM presence"
+ " WHERE presence.accountId=c.accountId AND address=c.address AND resource='')"
+ " WHEN c.type='INDIVIDUAL' THEN (SELECT vCardPhoto FROM presence WHERE"
+ " accountId=c.accountId AND address=c.address AND vCardPhoto NOT NULL LIMIT 1)"
+ " ELSE NULL END) as vCardPhoto,(SELECT thumb_id FROM avatar WHERE"
+ " avatar.accountId=c.accountId AND avatar.address=c.address) as avatar,(CASE WHEN"
+ " c.type IN ('MUC','MUC_PM') THEN (SELECT count(distinct(df.feature)) == 2 FROM"
+ " disco_item di JOIN disco_feature df ON di.discoId = df.discoId WHERE"
+ " di.address=c.address AND df.feature IN('muc_membersonly','muc_nonanonymous'))"
+ " ELSE 0 END) as membersOnlyNonAnonymous,(CASE WHEN m.occupantId IS NOT NULL AND"
+ " c.type IN ('MUC','MUC_PM') THEN (SELECT resource FROM presence WHERE"
+ " accountId=c.accountId AND address=c.address AND occupantId=m.occupantId LIMIT"
+ " 1) ELSE NULL END) as occupantResource FROM CHAT c LEFT JOIN message m ON (m.id"
+ " = (SELECT id FROM message WHERE chatId=c.id ORDER by receivedAt DESC LIMIT 1))"
+ " WHERE (:accountId IS NULL OR c.accountId=:accountId) AND (:groupId IS NULL OR"
+ " (c.address IN(SELECT roster.address FROM roster JOIN roster_group ON"
+ " roster.id=roster_group.rosterItemId WHERE roster.accountId=c.accountId AND"
+ " roster_group.groupId=:groupId) OR c.address IN(SELECT address FROM bookmark"
+ " JOIN bookmark_group ON bookmark.id=bookmark_group.bookmarkId WHERE"
+ " bookmark.accountId=c.accountId AND bookmark_group.groupId=:groupId))) AND"
+ " c.archived=0 ORDER by m.receivedAt DESC")
public abstract PagingSource<Integer, ChatOverviewItem> getChatOverview(
final Long accountId, final Long groupId);
@Query(
"SELECT c.accountId,c.address,c.type,(SELECT name FROM roster WHERE"
+ " roster.accountId=c.accountId AND roster.address=c.address) as"
+ " rosterName,(SELECT nick FROM nick WHERE nick.accountId=c.accountId AND"
+ " nick.address=c.address) as nick,(SELECT identity.name FROM disco_item JOIN"
+ " disco_identity identity ON disco_item.discoId=identity.discoId WHERE"
+ " disco_item.accountId=c.accountId AND disco_item.address=c.address LIMIT 1) as"
+ " discoIdentityName,(SELECT name FROM bookmark WHERE"
+ " bookmark.accountId=c.accountId AND bookmark.address=c.address) as"
+ " bookmarkName,(CASE WHEN c.type IN ('MUC','MUC_PM') THEN (SELECT"
+ " count(distinct(df.feature)) == 2 FROM disco_item di JOIN disco_feature df ON"
+ " di.discoId = df.discoId WHERE di.address=c.address AND df.feature"
+ " IN('muc_membersonly','muc_nonanonymous')) ELSE 0 END) as"
+ " membersOnlyNonAnonymous FROM chat c WHERE c.id=:chatId")
public abstract LiveData<ChatInfo> getChatInfo(final long chatId);
public PagingSource<Integer, ChatOverviewItem> getChatOverview(final ChatFilter chatFilter) {
if (chatFilter instanceof AccountIdentifier account) {
return getChatOverview(account.id, null);
} else if (chatFilter instanceof GroupIdentifier group) {
return getChatOverview(null, group.id);
} else {
return getChatOverview(null, null);
}
}
}

View file

@ -1,238 +0,0 @@
package im.conversations.android.database.dao;
import androidx.annotation.Nullable;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
import androidx.room.Transaction;
import com.google.common.base.Strings;
import com.google.common.collect.Collections2;
import com.google.common.util.concurrent.ListenableFuture;
import im.conversations.android.database.entity.DiscoEntity;
import im.conversations.android.database.entity.DiscoExtensionEntity;
import im.conversations.android.database.entity.DiscoExtensionFieldEntity;
import im.conversations.android.database.entity.DiscoExtensionFieldValueEntity;
import im.conversations.android.database.entity.DiscoFeatureEntity;
import im.conversations.android.database.entity.DiscoIdentityEntity;
import im.conversations.android.database.entity.DiscoItemEntity;
import im.conversations.android.database.model.Account;
import im.conversations.android.database.model.DiscoItemWithExtension;
import im.conversations.android.xmpp.Entity;
import im.conversations.android.xmpp.EntityCapabilities;
import im.conversations.android.xmpp.EntityCapabilities2;
import im.conversations.android.xmpp.model.data.Data;
import im.conversations.android.xmpp.model.data.Value;
import im.conversations.android.xmpp.model.disco.info.InfoQuery;
import im.conversations.android.xmpp.model.disco.items.Item;
import java.util.Collection;
import java.util.List;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.DomainBareJid;
import org.jxmpp.jid.Jid;
import org.jxmpp.jid.parts.Resourcepart;
@Dao
public abstract class DiscoDao extends BaseDao {
@Insert(onConflict = OnConflictStrategy.IGNORE)
protected abstract void insertDiscoItems(Collection<DiscoItemEntity> items);
@Insert
protected abstract void insert(DiscoItemEntity item);
@Insert
protected abstract void insertDiscoIdentities(Collection<DiscoIdentityEntity> identities);
@Insert
protected abstract void insertDiscoFeatures(Collection<DiscoFeatureEntity> features);
@Query(
"DELETE FROM disco_item WHERE accountId=:account AND parentAddress=:parent AND"
+ " parentNode=:parentNode AND address NOT IN(:existent)")
protected abstract void deleteNonExistentDiscoItems(
final long account,
final Jid parent,
final String parentNode,
final Collection<Jid> existent);
@Query(
"UPDATE presence SET discoId=:discoId WHERE accountId=:account AND address=:address"
+ " AND resource=:resource")
protected abstract void updateDiscoIdInPresence(
long account, Jid address, Resourcepart resource, long discoId);
@Query(
"UPDATE disco_item SET discoId=:discoId WHERE accountId=:account AND address=:address"
+ " AND node=:node")
protected abstract int updateDiscoIdInDiscoItem(
long account, Jid address, String node, long discoId);
@Insert
protected abstract void insertDiscoFieldValues(
Collection<DiscoExtensionFieldValueEntity> value);
@Insert
protected abstract long insert(DiscoEntity entity);
@Insert
protected abstract long insert(DiscoExtensionEntity entity);
@Insert
protected abstract long insert(DiscoExtensionFieldEntity entity);
@Transaction
public void set(
final Account account,
final Entity.DiscoItem parent,
final String parentNode,
final Collection<Item> items) {
final var entities =
Collections2.transform(
items, i -> DiscoItemEntity.of(account.id, parent.address, parentNode, i));
insertDiscoItems(entities);
deleteNonExistentDiscoItems(
account.id,
parent.address,
Strings.nullToEmpty(parentNode),
Collections2.transform(items, Item::getJid));
}
@Transaction
public boolean set(
final Account account,
final Entity entity,
@Nullable final String node,
final EntityCapabilities.Hash capsHash) {
final Long existingDiscoId;
if (capsHash instanceof EntityCapabilities2.EntityCaps2Hash) {
existingDiscoId = getDiscoId(account.id, capsHash.hash);
} else if (capsHash instanceof EntityCapabilities.EntityCapsHash) {
existingDiscoId = getDiscoIdByCapsHash(account.id, capsHash.hash);
} else {
existingDiscoId = null;
}
if (existingDiscoId == null) {
return false;
}
updateDiscoId(account.id, entity, node, existingDiscoId);
return true;
}
protected void updateDiscoId(
final long account,
final Entity entity,
@Nullable final String node,
final long discoId) {
if (entity instanceof Entity.DiscoItem) {
if (updateDiscoIdInDiscoItem(
account, entity.address, Strings.nullToEmpty(node), discoId)
> 0) {
return;
}
insert(DiscoItemEntity.of(account, entity.address, node, discoId));
} else if (entity instanceof Entity.Presence) {
updateDiscoIdInPresence(
account,
entity.address.asBareJid(),
entity.address.getResourceOrEmpty(),
discoId);
}
}
@Transaction
public void set(
final Account account,
final Entity entity,
final String node,
final byte[] capsHash,
final byte[] caps2HashSha256,
final InfoQuery infoQuery,
final boolean cache) {
final Long existingDiscoId = getDiscoId(account.id, caps2HashSha256);
if (existingDiscoId != null) {
updateDiscoId(account.id, entity, node, existingDiscoId);
return;
}
final long discoId = insert(DiscoEntity.of(account.id, capsHash, caps2HashSha256, cache));
insertDiscoIdentities(
Collections2.transform(
infoQuery.getIdentities(), i -> DiscoIdentityEntity.of(discoId, i)));
insertDiscoFeatures(
Collections2.transform(
infoQuery.getFeatures(), f -> DiscoFeatureEntity.of(discoId, f.getVar())));
for (final Data data : infoQuery.getExtensions(Data.class)) {
final var extensionId = insert(DiscoExtensionEntity.of(discoId, data.getFormType()));
for (final var field : data.getFields()) {
final var fieldId =
insert(DiscoExtensionFieldEntity.of(extensionId, field.getFieldName()));
insertDiscoFieldValues(
Collections2.transform(
field.getExtensions(Value.class),
v -> DiscoExtensionFieldValueEntity.of(fieldId, v.getContent())));
}
}
updateDiscoId(account.id, entity, node, discoId);
}
@Query(
"DELETE FROM disco WHERE accountId=:account AND cache=0 AND id NOT IN(SELECT discoId"
+ " FROM presence WHERE discoId IS NOT NULL) AND id NOT IN(SELECT discoId FROM"
+ " disco_item WHERE discoId IS NOT NULL)")
public abstract void deleteUnused(long account);
@Query("SELECT id FROM disco WHERE accountId=:accountId AND caps2HashSha256=:caps2HashSha256")
protected abstract Long getDiscoId(final long accountId, final byte[] caps2HashSha256);
@Query("SELECT id FROM disco WHERE accountId=:accountId AND capsHash=:capsHash")
protected abstract Long getDiscoIdByCapsHash(final long accountId, final byte[] capsHash);
@Query(
"SELECT EXISTS (SELECT presence.id FROM presence JOIN disco_feature on"
+ " presence.discoId=disco_feature.discoId WHERE accountId=:account AND"
+ " address=:address AND resource=:resource AND feature=:feature)")
protected abstract boolean hasPresenceFeature(
final long account,
final BareJid address,
final Resourcepart resource,
final String feature);
@Query(
"SELECT count(presence.id) FROM presence JOIN disco_feature on"
+ " presence.discoId=disco_feature.discoId WHERE accountId=:account AND"
+ " address=:address AND feature=:feature")
public abstract int countPresencesWithFeature(
final long account, final BareJid address, final String feature);
public int countPresencesWithFeature(final Account account, final String feature) {
return countPresencesWithFeature(account.id, account.address, feature);
}
public boolean hasFeature(final long account, final Entity entity, final String feature) {
if (entity instanceof Entity.DiscoItem) {
return hasDiscoItemFeature(account, entity.address, feature);
}
if (entity instanceof Entity.Presence) {
return hasPresenceFeature(
account,
entity.address.asBareJid(),
entity.address.getResourceOrEmpty(),
feature);
}
throw new IllegalStateException(
String.format(
"Discovering features for %s is not implemented",
entity.getClass().getName()));
}
@Transaction
@Query(
"SELECT disco_item.discoId,address FROM disco_item JOIN disco_feature ON"
+ " disco_item.discoId=disco_feature.discoId WHERE feature=:feature AND"
+ " (address=:address OR parentAddress=:address)")
public abstract ListenableFuture<List<DiscoItemWithExtension>> getItemByFeature(
DomainBareJid address, final String feature);
}

View file

@ -1,27 +0,0 @@
package im.conversations.android.database.dao;
import androidx.room.Insert;
import androidx.room.Query;
import im.conversations.android.database.entity.GroupEntity;
public abstract class GroupDao {
public long getOrCreateId(final String name) {
final Long existing = getGroupId(name);
if (existing != null) {
return existing;
}
return insert(GroupEntity.of(name));
}
@Query("SELECT id FROM `group` WHERE name=:name")
abstract Long getGroupId(final String name);
@Insert
abstract Long insert(GroupEntity groupEntity);
@Query(
"DELETE from `group` WHERE id NOT IN(SELECT groupId FROM roster_group) AND id NOT"
+ " IN(SELECT groupId FROM bookmark_group)")
abstract void deleteEmptyGroups();
}

View file

@ -1,546 +0,0 @@
package im.conversations.android.database.dao;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.paging.PagingSource;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Transaction;
import androidx.room.Update;
import com.google.common.base.Preconditions;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.ListenableFuture;
import im.conversations.android.database.entity.MessageContentEntity;
import im.conversations.android.database.entity.MessageEntity;
import im.conversations.android.database.entity.MessageReactionEntity;
import im.conversations.android.database.entity.MessageStateEntity;
import im.conversations.android.database.entity.MessageVersionEntity;
import im.conversations.android.database.model.Account;
import im.conversations.android.database.model.ChatIdentifier;
import im.conversations.android.database.model.Encryption;
import im.conversations.android.database.model.MessageIdentifier;
import im.conversations.android.database.model.MessageState;
import im.conversations.android.database.model.MessageWithContentReactions;
import im.conversations.android.database.model.Modification;
import im.conversations.android.transformer.MessageContentWrapper;
import im.conversations.android.transformer.MessageTransformation;
import im.conversations.android.xmpp.model.fallback.Body;
import im.conversations.android.xmpp.model.reactions.Reactions;
import im.conversations.android.xmpp.model.stanza.Message;
import java.util.Collection;
import java.util.List;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.Jid;
import org.jxmpp.jid.parts.Resourcepart;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.libsignal.IdentityKey;
@Dao
public abstract class MessageDao {
private static final Logger LOGGER = LoggerFactory.getLogger(MessageDao.class);
@Query(
"UPDATE message SET acknowledged=1 WHERE messageId=:messageId AND toBare=:toBare AND"
+ " toResource=NULL AND chatId IN (SELECT id FROM chat WHERE accountId=:account)")
abstract int acknowledge(long account, String messageId, final BareJid toBare);
@Query(
"UPDATE message SET acknowledged=1 WHERE messageId=:messageId AND toBare=:toBare AND"
+ " toResource=:toResource AND chatId IN (SELECT id FROM chat WHERE"
+ " accountId=:account)")
abstract int acknowledge(
long account,
final String messageId,
final BareJid toBare,
final Resourcepart toResource);
public boolean acknowledge(
final Account account, @NonNull final String messageId, @NonNull final Jid to) {
return acknowledge(account.id, messageId, to);
}
public boolean acknowledge(
final long account, @NonNull final String messageId, @NonNull final Jid to) {
if (to.hasResource()) {
return acknowledge(account, messageId, to.asBareJid(), to.getResourceOrThrow()) > 0;
} else {
return acknowledge(account, messageId, to.asBareJid()) > 0;
}
}
// this method returns a MessageIdentifier (message + version) used to create ORIGINAL messages
// it might return something that was previously a stub (message that only has reactions or
// corrections but no original content). but in the process of invoking this method the stub
// will be upgraded to an original message (missing information filled in)
public MessageIdentifier getOrCreateMessage(
ChatIdentifier chatIdentifier, final MessageTransformation transformation) {
final MessageIdentifier messageIdentifier =
get(
chatIdentifier.id,
transformation.fromBare(),
transformation.occupantId,
transformation.stanzaId,
transformation.messageId);
if (messageIdentifier != null) {
if (messageIdentifier.isStub()) {
if (transformation.type == Message.Type.GROUPCHAT) {
mergeMessageStubs(chatIdentifier, messageIdentifier, transformation);
}
LOGGER.info(
"Found stub for stanzaId '{}' and messageId '{}'",
transformation.stanzaId,
transformation.messageId);
final long originalVersionId =
insert(
MessageVersionEntity.of(
messageIdentifier.id,
Modification.ORIGINAL,
transformation));
final MessageEntity updatedEntity =
MessageEntity.of(chatIdentifier.id, transformation);
updatedEntity.id = messageIdentifier.id;
updatedEntity.latestVersion = getLatestVersion(messageIdentifier.id);
LOGGER.info(
"Created original version {} and updated latest version to {} for"
+ " messageEntityId {}",
originalVersionId,
updatedEntity.latestVersion,
messageIdentifier.id);
update(updatedEntity);
return new MessageIdentifier(
updatedEntity.id,
transformation.stanzaId,
transformation.messageId,
transformation.fromBare(),
originalVersionId);
} else {
throw new IllegalStateException(
String.format(
"A message with stanzaId '%s' and messageId '%s' from %s already"
+ " exists",
transformation.stanzaId,
transformation.messageId,
transformation.from));
}
}
final MessageEntity entity = MessageEntity.of(chatIdentifier.id, transformation);
final long messageEntityId = insert(entity);
final long messageVersionId =
insert(
MessageVersionEntity.of(
messageEntityId, Modification.ORIGINAL, transformation));
setLatestMessageId(messageEntityId, messageVersionId);
return new MessageIdentifier(
messageEntityId,
transformation.stanzaId,
transformation.messageId,
transformation.fromBare(),
messageVersionId);
}
private void mergeMessageStubs(
ChatIdentifier chatIdentifier,
MessageIdentifier messageIdentifier,
final MessageTransformation transformation) {
final Long stub;
if (messageIdentifier.messageId == null && transformation.messageId != null) {
stub = getMessageStubByMessageId(chatIdentifier.id, transformation.messageId);
} else if (messageIdentifier.stanzaId == null && transformation.stanzaId != null) {
stub = getMessageStubByStanzaId(chatIdentifier.id, transformation.stanzaId);
} else {
return;
}
if (stub == null) {
return;
}
LOGGER.info("Updating message.id in dangling stub {} => {}", stub, messageIdentifier.id);
updateMessageEntityIdInReactions(stub, messageIdentifier.id);
updateMessageEntityIdInVersions(stub, messageIdentifier.id);
deleteMessageEntity(stub);
}
@Query(
"UPDATE message_reaction SET messageEntityId=:newMessageEntityId WHERE"
+ " messageEntityId=:oldMessageEntityId")
protected abstract void updateMessageEntityIdInReactions(
long oldMessageEntityId, Long newMessageEntityId);
@Query(
"UPDATE message_version SET messageEntityId=:newMessageEntityId WHERE"
+ " messageEntityId=:oldMessageEntityId")
protected abstract void updateMessageEntityIdInVersions(
long oldMessageEntityId, Long newMessageEntityId);
@Query("DELETE FROM message WHERE id=:messageEntityId")
protected abstract void deleteMessageEntity(final long messageEntityId);
@Query(
"SELECT id FROM message WHERE chatId=:chatId AND messageId=:messageId AND stanzaId IS"
+ " NULL AND latestVersion IS NULL")
protected abstract Long getMessageStubByMessageId(long chatId, String messageId);
@Query(
"SELECT id FROM message WHERE chatId=:chatId AND stanzaId=:stanzaId AND messageId IS"
+ " NULL AND latestVersion IS NULL")
protected abstract Long getMessageStubByStanzaId(long chatId, String stanzaId);
// this gets either a message or a stub.
// stubs are recognized by latestVersion=NULL
// when found by stanzaId the stanzaId must either by verified or belonging to a stub
// when found by messageId the from must either match (for corrections) or not be set (null) and
// we only look up stubs
// TODO `senderIdentity` should probably match too
@Query(
"SELECT id,stanzaId,messageId,fromBare,latestVersion as version FROM message WHERE"
+ " chatId=:chatId AND (fromBare=:fromBare OR fromBare IS NULL) AND"
+ " (occupantId=:occupantId OR occupantId IS NULL) AND ((stanzaId IS NOT NULL AND"
+ " stanzaId=:stanzaId AND (stanzaIdVerified=1 OR latestVersion IS NULL)) OR"
+ " (stanzaId IS NULL AND messageId=:messageId AND latestVersion IS NULL))")
abstract MessageIdentifier get(
long chatId, BareJid fromBare, String occupantId, String stanzaId, String messageId);
public MessageIdentifier getOrCreateVersion(
ChatIdentifier chat,
MessageTransformation transformation,
final String messageId,
final Modification modification) {
Preconditions.checkArgument(
messageId != null, "A modification must reference a message id");
final MessageIdentifier messageIdentifier;
if (transformation.type == Message.Type.GROUPCHAT) {
// TODO if modification == moderation do not take occupant Id into account
Preconditions.checkNotNull(
transformation.occupantId,
"To create a version of a group chat message occupant id must be set");
messageIdentifier =
getByOccupantIdAndMessageId(
chat.id,
transformation.fromBare(),
transformation.occupantId,
messageId);
} else {
messageIdentifier = getByMessageId(chat.id, transformation.fromBare(), messageId);
}
if (messageIdentifier == null) {
LOGGER.info(
"Create stub for {} because we could not find anything with id {} from {}",
modification,
messageId,
transformation.fromBare());
// TODO when creating a stub for 'moderation' we should not include occupant id and
// senderId in there since we dont know who those are
final var messageEntity = MessageEntity.stub(chat.id, messageId, transformation);
final long messageEntityId = insert(messageEntity);
final long messageVersionId =
insert(MessageVersionEntity.of(messageEntityId, modification, transformation));
// we do not point latestVersion to this newly created versions. We've only created a
// stub and are waiting for the original message to arrive
return new MessageIdentifier(
messageEntityId, null, null, transformation.fromBare(), messageVersionId);
}
if (hasVersionWithMessageId(messageIdentifier.id, transformation.messageId)) {
throw new IllegalStateException(
String.format(
"A modification with messageId %s has already been applied",
messageId));
}
final long messageVersionId =
insert(MessageVersionEntity.of(messageIdentifier.id, modification, transformation));
if (messageIdentifier.version != null) {
// if the existing message was not a stub we retarget the version
final long latestVersion = getLatestVersion(messageIdentifier.id);
setLatestMessageId(messageIdentifier.id, latestVersion);
}
return new MessageIdentifier(
messageIdentifier.id,
messageIdentifier.stanzaId,
messageIdentifier.messageId,
messageIdentifier.fromBare,
messageVersionId);
}
@Query(
"SELECT id,stanzaId,messageId,fromBare,latestVersion as version FROM message WHERE"
+ " chatId=:chatId AND (fromBare=:fromBare OR fromBare IS NULL) AND"
+ " (occupantId=:occupantId OR occupantId IS NULL) AND messageId=:messageId")
abstract MessageIdentifier getByOccupantIdAndMessageId(
long chatId, BareJid fromBare, String occupantId, String messageId);
@Query(
"SELECT id,stanzaId,messageId,fromBare,latestVersion as version FROM message WHERE"
+ " chatId=:chatId AND (fromBare=:fromBare OR fromBare IS NULL) AND"
+ " messageId=:messageId")
abstract MessageIdentifier getByMessageId(long chatId, BareJid fromBare, String messageId);
@Query(
"SELECT id FROM message_version WHERE messageEntityId=:messageEntityId ORDER BY (CASE"
+ " modification WHEN 'ORIGINAL' THEN 1 ELSE 0 END),receivedAt DESC LIMIT 1")
abstract Long getLatestVersion(long messageEntityId);
@Query(
"SELECT EXISTS (SELECT id FROM message_version WHERE messageEntityId=:messageEntityId"
+ " AND messageId=:messageId)")
abstract boolean hasVersionWithMessageId(long messageEntityId, String messageId);
@Insert
protected abstract long insert(MessageEntity messageEntity);
@Update
protected abstract void update(final MessageEntity messageEntity);
@Insert
protected abstract long insert(MessageVersionEntity messageVersionEntity);
@Query("UPDATE message SET latestVersion=:messageVersionId WHERE id=:messageEntityId")
protected abstract void setLatestMessageId(
final long messageEntityId, final long messageVersionId);
public MessageIdentifier getOrCreateStub(
final ChatIdentifier chat, final Message.Type messageType, final String parentId) {
final MessageIdentifier existing;
if (messageType == Message.Type.GROUPCHAT) {
existing = getByStanzaId(chat.id, parentId);
} else {
existing = getByMessageId(chat.id, parentId);
}
if (existing != null) {
return existing;
}
final MessageEntity messageEntity;
if (messageType == Message.Type.GROUPCHAT) {
LOGGER.info("Create stub for stanza id {}", parentId);
messageEntity = MessageEntity.stubOfStanzaId(chat.id, parentId);
} else {
LOGGER.info("Create stub for message id {}", parentId);
messageEntity = MessageEntity.stubOfMessageId(chat.id, parentId);
}
final long messageEntityId = insert(messageEntity);
return new MessageIdentifier(messageEntityId, null, null, null, null);
}
@Query(
"SELECT id,stanzaId,messageId,fromBare,latestVersion as version FROM message WHERE"
+ " chatId=:chatId AND messageId=:messageId")
protected abstract MessageIdentifier getByMessageId(final long chatId, final String messageId);
@Query(
"SELECT id,stanzaId,messageId,fromBare,latestVersion as version FROM message WHERE"
+ " chatId=:chatId AND stanzaId=:stanzaId")
protected abstract MessageIdentifier getByStanzaId(final long chatId, final String stanzaId);
public void insertMessageContent(
final Long latestVersion, final MessageContentWrapper messageContentWrapper) {
Preconditions.checkNotNull(
latestVersion, "Contents can only be inserted for a specific version");
Preconditions.checkArgument(
messageContentWrapper.contents.size() > 0,
"If you are trying to insert empty contents something went wrong");
insertMessageContent(
Lists.transform(
messageContentWrapper.contents,
c -> MessageContentEntity.of(latestVersion, c)));
final int rows =
updateMessageVersionEncryption(
latestVersion,
messageContentWrapper.encryption,
messageContentWrapper.identityKey);
if (rows != 1) {
throw new IllegalStateException(
"We expected to update encryption information on exactly 1 row");
}
}
@Insert
protected abstract void insertMessageContent(Collection<MessageContentEntity> contentEntities);
@Query(
"UPDATE message_version SET encryption=:encryption,identityKey=:identityKey WHERE"
+ " id=:messageVersionId")
protected abstract int updateMessageVersionEncryption(
long messageVersionId, Encryption encryption, IdentityKey identityKey);
public void insertMessageState(
ChatIdentifier chatIdentifier,
final String messageId,
final MessageState messageState) {
final Long versionId = getVersionIdForOutgoingMessage(chatIdentifier.id, messageId);
if (versionId == null) {
LOGGER.warn(
"Can not find message {} in chat {} ({})",
messageId,
chatIdentifier.id,
chatIdentifier.address);
return;
}
insert(MessageStateEntity.of(versionId, messageState));
}
@Query(
"SELECT message_version.id FROM message_version JOIN message ON"
+ " message.id=message_version.messageEntityId WHERE message.chatId=:chatId AND"
+ " message_version.messageId=:messageId AND message.outgoing=1")
protected abstract Long getVersionIdForOutgoingMessage(long chatId, final String messageId);
@Insert
protected abstract void insert(MessageStateEntity messageStateEntity);
@Insert
protected abstract void insertReactions(Collection<MessageReactionEntity> reactionEntities);
public void insertReactions(
ChatIdentifier chat, Reactions reactions, MessageTransformation transformation) {
final Message.Type messageType = transformation.type;
final MessageIdentifier messageIdentifier =
getOrCreateStub(chat, messageType, reactions.getId());
if (messageType == Message.Type.GROUPCHAT) {
Preconditions.checkNotNull(
transformation.occupantId,
"OccupantId must not be null when processing reactions in group chats");
deleteReactionsByOccupantId(messageIdentifier.id, transformation.occupantId);
} else {
deleteReactionsByFromBare(messageIdentifier.id, transformation.fromBare());
}
LOGGER.info(
"Inserting reaction from {} to messageEntityId {}",
transformation.from,
messageIdentifier.id);
insertReactions(
Collections2.transform(
reactions.getReactions(),
r -> MessageReactionEntity.of(messageIdentifier.id, r, transformation)));
}
@Query(
"DELETE FROM message_reaction WHERE messageEntityId=:messageEntityId AND"
+ " occupantId=:occupantId")
protected abstract void deleteReactionsByOccupantId(long messageEntityId, String occupantId);
@Query(
"DELETE FROM message_reaction WHERE messageEntityId=:messageEntityId AND"
+ " reactionBy=:fromBare")
protected abstract void deleteReactionsByFromBare(long messageEntityId, BareJid fromBare);
@VisibleForTesting
@Transaction
@Query(
"SELECT c.accountId,m.id as id,type as"
+ " chatType,sentAt,outgoing,toBare,toResource,fromBare,fromResource,acknowledged,senderIdentity"
+ " as sender,(SELECT name FROM roster WHERE roster.accountId=c.accountId AND"
+ " roster.address=m.senderIdentity) as senderRosterName,(SELECT nick FROM nick"
+ " WHERE nick.accountId=c.accountId AND nick.address=m.senderIdentity) as"
+ " senderNick,(SELECT vCardPhoto FROM presence WHERE accountId=c.accountId AND"
+ " address=m.senderIdentity AND vCardPhoto NOT NULL LIMIT 1) as"
+ " senderVcardPhoto,(SELECT thumb_id FROM avatar WHERE"
+ " avatar.accountId=c.accountId AND avatar.address=m.senderIdentity) as"
+ " senderAvatar,(CASE WHEN m.occupantId IS NOT NULL AND c.type IN ('MUC','MUC_PM')"
+ " THEN (SELECT vCardPhoto FROM presence WHERE accountId=c.accountId AND"
+ " address=c.address AND occupantId=m.occupantId AND vCardPhoto NOT NULL LIMIT 1)"
+ " ELSE NULL END) as occupantVcardPhoto,(CASE WHEN m.occupantId IS NOT NULL AND"
+ " c.type IN ('MUC','MUC_PM') THEN (SELECT resource FROM presence WHERE"
+ " accountId=c.accountId AND address=c.address AND occupantId=m.occupantId LIMIT"
+ " 1) ELSE NULL END) as occupantResource,modification,latestVersion as"
+ " version,inReplyToMessageEntityId,inReplyToFallbackStart,inReplyToFallbackEnd,encryption,message_version.identityKey,trust,(CASE"
+ " WHEN c.type IN ('MUC','MUC_PM') THEN (SELECT count(distinct(df.feature)) == 2"
+ " FROM disco_item di JOIN disco_feature df ON di.discoId = df.discoId WHERE"
+ " di.address=c.address AND df.feature IN('muc_membersonly','muc_nonanonymous'))"
+ " ELSE 0 END) as membersOnlyNonAnonymous FROM chat c JOIN message m on"
+ " m.chatId=c.id JOIN message_version ON m.latestVersion=message_version.id LEFT"
+ " JOIN axolotl_identity ON c.accountId=axolotl_identity.accountId AND"
+ " m.senderIdentity=axolotl_identity.address AND"
+ " message_version.identityKey=axolotl_identity.identityKey WHERE c.id=:chatId AND"
+ " latestVersion IS NOT NULL ORDER BY m.receivedAt")
public abstract List<MessageWithContentReactions> getMessagesForTesting(long chatId);
@Transaction
@Query(
"SELECT c.accountId,m.id as id,type as"
+ " chatType,sentAt,outgoing,toBare,toResource,fromBare,fromResource,acknowledged,senderIdentity"
+ " as sender,(SELECT name FROM roster WHERE roster.accountId=c.accountId AND"
+ " roster.address=m.senderIdentity) as senderRosterName,(SELECT nick FROM nick"
+ " WHERE nick.accountId=c.accountId AND nick.address=m.senderIdentity) as"
+ " senderNick,(SELECT vCardPhoto FROM presence WHERE accountId=c.accountId AND"
+ " address=m.senderIdentity AND vCardPhoto NOT NULL LIMIT 1) as"
+ " senderVcardPhoto,(SELECT thumb_id FROM avatar WHERE"
+ " avatar.accountId=c.accountId AND avatar.address=m.senderIdentity) as"
+ " senderAvatar,(CASE WHEN m.occupantId IS NOT NULL AND c.type IN ('MUC','MUC_PM')"
+ " THEN (SELECT vCardPhoto FROM presence WHERE accountId=c.accountId AND"
+ " address=c.address AND occupantId=m.occupantId AND vCardPhoto NOT NULL LIMIT 1)"
+ " ELSE NULL END) as occupantVcardPhoto,(CASE WHEN m.occupantId IS NOT NULL AND"
+ " c.type IN ('MUC','MUC_PM') THEN (SELECT resource FROM presence WHERE"
+ " accountId=c.accountId AND address=c.address AND occupantId=m.occupantId LIMIT"
+ " 1) ELSE NULL END) as occupantResource,modification,latestVersion as"
+ " version,inReplyToMessageEntityId,inReplyToFallbackStart,inReplyToFallbackEnd,encryption,message_version.identityKey,trust,(CASE"
+ " WHEN c.type IN ('MUC','MUC_PM') THEN (SELECT count(distinct(df.feature)) == 2"
+ " FROM disco_item di JOIN disco_feature df ON di.discoId = df.discoId WHERE"
+ " di.address=c.address AND df.feature IN('muc_membersonly','muc_nonanonymous'))"
+ " ELSE 0 END) as membersOnlyNonAnonymous FROM chat c JOIN message m on"
+ " m.chatId=c.id JOIN message_version ON m.latestVersion=message_version.id LEFT"
+ " JOIN axolotl_identity ON c.accountId=axolotl_identity.accountId AND"
+ " m.senderIdentity=axolotl_identity.address AND"
+ " message_version.identityKey=axolotl_identity.identityKey WHERE c.id=:chatId AND"
+ " latestVersion IS NOT NULL ORDER BY m.receivedAt DESC,m.id DESC")
public abstract PagingSource<Integer, MessageWithContentReactions> getMessages(long chatId);
@Query(
"SELECT CASE WHEN (SELECT EXISTS(SELECT id FROM message WHERE chatId=:chatId AND"
+ " id=:messageId)) THEN (SELECT count(id) FROM message WHERE chatId=:chatId AND"
+ " (receivedAt > (SELECT receivedAt FROM message WHERE id=:messageId) OR"
+ " (receivedAt=(SELECT receivedAt FROM message WHERE id=:messageId) AND id >"
+ " :messageId))) ELSE NULL END")
public abstract ListenableFuture<Integer> getPosition(final long chatId, final long messageId);
public void setInReplyTo(
final ChatIdentifier chat,
final MessageIdentifier messageIdentifier,
final Message.Type messageType,
final Jid to,
final String inReplyTo,
final Body fallbackBody) {
if (messageType == Message.Type.GROUPCHAT) {
final Long messageEntityId = getMessageByStanzaId(chat.id, inReplyTo);
setInReplyToStanzaId(messageIdentifier.id, inReplyTo, messageEntityId);
} else {
final Long messageEntityId = getMessageByMessageId(chat.id, to.asBareJid(), inReplyTo);
setInReplyToMessageId(messageIdentifier.id, inReplyTo, messageEntityId);
}
final int inReplyToFallbackStart = fallbackBody == null ? 0 : fallbackBody.getStart();
final int inReplyToFallbackEnd = fallbackBody == null ? 0 : fallbackBody.getEnd();
if (inReplyToFallbackStart != 0 || inReplyToFallbackEnd != 0) {
setInReplyToFallback(
messageIdentifier.id, inReplyToFallbackStart, inReplyToFallbackEnd);
}
}
@Query(
"UPDATE message SET"
+ " inReplyToMessageId=null,inReplyToStanzaId=:stanzaId,inReplyToMessageEntityId=:inReplyToMessageEntityId"
+ " WHERE id=:id")
protected abstract void setInReplyToStanzaId(
final long id, String stanzaId, Long inReplyToMessageEntityId);
@Query(
"UPDATE message SET"
+ " inReplyToMessageId=:messageId,inReplyToStanzaId=null,inReplyToMessageEntityId=:inReplyToMessageEntityId"
+ " WHERE id=:id")
protected abstract void setInReplyToMessageId(
final long id, String messageId, Long inReplyToMessageEntityId);
@Query(
"UPDATE message SET"
+ " inReplyToFallbackStart=:inReplyToFallbackStart,inReplyToFallbackEnd=:inReplyToFallbackEnd"
+ " WHERE id=:id")
protected abstract void setInReplyToFallback(
final long id, final int inReplyToFallbackStart, final int inReplyToFallbackEnd);
@Query(
"SELECT id FROM message WHERE chatId=:chatId AND fromBare=:fromBare AND"
+ " messageId=:messageId")
protected abstract Long getMessageByMessageId(long chatId, BareJid fromBare, String messageId);
@Query("SELECT id FROM message WHERE chatId=:chatId AND stanzaId=:stanzaId")
protected abstract Long getMessageByStanzaId(long chatId, String stanzaId);
}

View file

@ -1,19 +0,0 @@
package im.conversations.android.database.dao;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import im.conversations.android.database.entity.NickEntity;
import im.conversations.android.database.model.Account;
import org.jxmpp.jid.Jid;
@Dao
public abstract class NickDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
protected abstract long insert(NickEntity nickEntity);
public long set(final Account account, final Jid address, final String nick) {
return insert(NickEntity.of(account.id, address, nick));
}
}

View file

@ -1,93 +0,0 @@
package im.conversations.android.database.dao;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
import im.conversations.android.database.entity.PresenceEntity;
import im.conversations.android.database.model.Account;
import im.conversations.android.database.model.PresenceShow;
import im.conversations.android.database.model.PresenceType;
import im.conversations.android.xmpp.model.muc.user.MucUser;
import java.util.Arrays;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.Jid;
import org.jxmpp.jid.parts.Resourcepart;
@Dao
public abstract class PresenceDao {
@Query("DELETE FROM presence WHERE accountId=:account")
public abstract void deletePresences(long account);
@Query("DELETE FROM presence WHERE accountId=:account AND address=:address")
abstract void deletePresences(long account, BareJid address);
@Query(
"DELETE FROM presence WHERE accountId=:account AND address=:address AND"
+ " resource=:resource")
abstract void deletePresence(long account, BareJid address, Resourcepart resource);
@Insert(onConflict = OnConflictStrategy.REPLACE)
abstract void insert(PresenceEntity entity);
public void set(
@NonNull final Account account,
@NonNull final BareJid address,
@NonNull final Resourcepart resource,
@Nullable final PresenceType type,
@Nullable final PresenceShow show,
@Nullable final String status,
@Nullable final String vCardPhoto,
@Nullable final String occupantId,
@Nullable final MucUser mucUser) {
if (resource.equals(Resourcepart.EMPTY)
&& Arrays.asList(PresenceType.ERROR, PresenceType.UNAVAILABLE).contains(type)) {
deletePresences(account.id, address);
}
if (type == PresenceType.UNAVAILABLE) {
if (!resource.equals(Resourcepart.EMPTY)) {
deletePresence(account.id, address, resource);
}
// unavailable presence only delete previous nothing left to do
return;
}
final var entity =
PresenceEntity.of(
account.id,
address,
resource,
type,
show,
status,
vCardPhoto,
occupantId,
mucUser);
insert(entity);
}
@Query(
"SELECT mucUserJid FROM presence WHERE accountId=:account AND address=:address AND"
+ " occupantId=:occupantId")
protected abstract Jid getMucUserJidByOccupantId(
long account, BareJid address, String occupantId);
@Query(
"SELECT mucUserJid FROM presence WHERE accountId=:account AND address=:address AND"
+ " resource=:resource")
protected abstract Jid getMucUserJidByResource(
long account, BareJid address, Resourcepart resource);
public BareJid getMucUserJidByOccupantId(Account account, BareJid address, String occupantId) {
final var jid = getMucUserJidByOccupantId(account.id, address, occupantId);
return jid == null ? null : jid.asBareJid();
}
public BareJid getMucUserJidByResource(
final Account account, final BareJid address, final Resourcepart resource) {
final var jid = getMucUserJidByResource(account.id, address, resource);
return jid == null ? null : jid.asBareJid();
}
}

View file

@ -1,83 +0,0 @@
package im.conversations.android.database.dao;
import static androidx.room.OnConflictStrategy.REPLACE;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Transaction;
import com.google.common.base.Strings;
import im.conversations.android.database.entity.RosterItemEntity;
import im.conversations.android.database.entity.RosterItemGroupEntity;
import im.conversations.android.database.model.Account;
import im.conversations.android.xmpp.model.roster.Item;
import java.util.Collection;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.Jid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Dao
public abstract class RosterDao extends GroupDao {
private static final Logger LOGGER = LoggerFactory.getLogger(RosterDao.class);
@Insert(onConflict = REPLACE)
protected abstract long insert(RosterItemEntity rosterItem);
@Query("DELETE FROM roster WHERE accountId=:account")
protected abstract void clear(final long account);
@Query("DELETE FROM roster WHERE accountId=:account AND address=:address")
protected abstract void delete(final long account, final Jid address);
@Query("UPDATE account SET rosterVersion=:version WHERE id=:account")
protected abstract void setRosterVersion(final long account, final String version);
@Query("SELECT EXISTS (SELECT id FROM roster WHERE accountId=:account AND address=:address)")
public abstract boolean isInRoster(final long account, final BareJid address);
@Transaction
public void set(
final Account account, final String version, final Collection<Item> rosterItems) {
LOGGER.info("items: " + rosterItems);
clear(account.id);
for (final Item item : rosterItems) {
final long id = insert(RosterItemEntity.of(account.id, item));
insertRosterGroups(id, item.getGroups());
}
setRosterVersion(account.id, version);
deleteEmptyGroups();
}
@Transaction
public void update(
final Account account, final String version, final Collection<Item> updates) {
for (final Item item : updates) {
final Item.Subscription subscription = item.getSubscription();
if (subscription == null) {
continue;
}
if (subscription == Item.Subscription.REMOVE) {
delete(account.id, item.getJid());
}
final RosterItemEntity entity = RosterItemEntity.of(account.id, item);
final long id = insert(entity);
insertRosterGroups(id, item.getGroups());
}
setRosterVersion(account.id, version);
deleteEmptyGroups();
}
protected void insertRosterGroups(final long rosterItemId, Collection<String> groups) {
for (final String group : groups) {
if (Strings.isNullOrEmpty(group)) {
continue;
}
insertRosterGroup(RosterItemGroupEntity.of(rosterItemId, getOrCreateId(group)));
}
}
@Insert
protected abstract void insertRosterGroup(RosterItemGroupEntity entity);
}

View file

@ -1,32 +0,0 @@
package im.conversations.android.database.dao;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
import im.conversations.android.database.entity.ServiceRecordCacheEntity;
import im.conversations.android.database.model.Account;
import im.conversations.android.dns.ServiceRecord;
@Dao
public abstract class ServiceRecordDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
protected abstract void insert(ServiceRecordCacheEntity entity);
public void insert(final Account account, final ServiceRecord serviceRecord) {
insert(
ServiceRecordCacheEntity.of(
account, account.address.getDomain().toString(), serviceRecord));
}
@Query(
"SELECT ip,hostname,port,directTls,priority,authenticated FROM service_record_cache"
+ " WHERE accountId=:account AND domain=:domain LIMIT 1")
protected abstract ServiceRecord getCachedServiceRecord(
final long account, final String domain);
public ServiceRecord getCachedServiceRecord(final Account account) {
return getCachedServiceRecord(account.id, account.address.getDomain().toString());
}
}

View file

@ -1,42 +0,0 @@
package im.conversations.android.database.entity;
import androidx.annotation.NonNull;
import androidx.room.Embedded;
import androidx.room.Entity;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import im.conversations.android.database.model.Connection;
import im.conversations.android.database.model.Proxy;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.parts.Resourcepart;
@Entity(
tableName = "account",
indices = {
@Index(
value = {"address"},
unique = true)
})
public class AccountEntity {
@PrimaryKey(autoGenerate = true)
public Long id;
@NonNull public BareJid address;
public Resourcepart resource;
public byte[] randomSeed;
public boolean enabled;
public boolean quickStartAvailable = false;
public boolean loginAndBind = true;
public boolean showErrorNotification = true;
public String rosterVersion;
@Embedded public Connection connection;
@Embedded(prefix = "proxy")
public Proxy proxy;
}

View file

@ -1,63 +0,0 @@
package im.conversations.android.database.entity;
import androidx.annotation.NonNull;
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import im.conversations.android.database.model.Account;
import org.jxmpp.jid.Jid;
@Entity(
tableName = "archive_page",
foreignKeys =
@ForeignKey(
entity = AccountEntity.class,
parentColumns = {"id"},
childColumns = {"accountId"},
onDelete = ForeignKey.CASCADE),
indices = {
@Index(
value = {"accountId", "archive", "type"},
unique = true)
})
public class ArchivePageEntity {
@PrimaryKey(autoGenerate = true)
public Long id;
@NonNull public Long accountId;
@NonNull public Jid archive;
@NonNull public Type type;
@NonNull public String start;
@NonNull public String end;
public boolean reachedMaxPages;
public static ArchivePageEntity of(
final Account account,
final Jid archive,
final Type type,
final String start,
final String end,
final boolean reachedMaxPages) {
final var entity = new ArchivePageEntity();
entity.accountId = account.id;
entity.archive = archive;
entity.type = type;
entity.start = start;
entity.end = end;
entity.reachedMaxPages = reachedMaxPages;
return entity;
}
public enum Type {
START,
MIDDLE,
LIVE
}
}

View file

@ -1,38 +0,0 @@
package im.conversations.android.database.entity;
import androidx.annotation.NonNull;
import androidx.room.Embedded;
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import im.conversations.android.database.model.AvatarExternal;
import im.conversations.android.xmpp.model.avatar.Info;
@Entity(
tableName = "avatar_additional",
foreignKeys =
@ForeignKey(
entity = AvatarEntity.class,
parentColumns = {"id"},
childColumns = {"avatarId"},
onDelete = ForeignKey.CASCADE),
indices = {@Index(value = {"avatarId"})})
public class AvatarAdditionalEntity {
@PrimaryKey(autoGenerate = true)
public Long id;
@NonNull public Long avatarId;
@NonNull
@Embedded(prefix = "avatar_external_")
public AvatarExternal external;
public static AvatarAdditionalEntity of(final long avatarId, Info info) {
final var entity = new AvatarAdditionalEntity();
entity.avatarId = avatarId;
entity.external = AvatarExternal.of(info);
return entity;
}
}

View file

@ -1,47 +0,0 @@
package im.conversations.android.database.entity;
import androidx.annotation.NonNull;
import androidx.room.Embedded;
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import im.conversations.android.database.model.Account;
import im.conversations.android.database.model.AvatarThumbnail;
import im.conversations.android.xmpp.model.avatar.Info;
import org.jxmpp.jid.BareJid;
@Entity(
tableName = "avatar",
foreignKeys =
@ForeignKey(
entity = AccountEntity.class,
parentColumns = {"id"},
childColumns = {"accountId"},
onDelete = ForeignKey.CASCADE),
indices = {
@Index(
value = {"accountId", "address"},
unique = true)
})
public class AvatarEntity {
@PrimaryKey(autoGenerate = true)
public Long id;
@NonNull public Long accountId;
@NonNull public BareJid address;
@Embedded(prefix = "thumb_")
@NonNull
public AvatarThumbnail thumbnail;
public static AvatarEntity of(final Account account, final BareJid address, final Info info) {
final var entity = new AvatarEntity();
entity.accountId = account.id;
entity.address = address;
entity.thumbnail = AvatarThumbnail.of(info);
return entity;
}
}

View file

@ -1,66 +0,0 @@
package im.conversations.android.database.entity;
import androidx.annotation.NonNull;
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import java.time.Instant;
import org.jxmpp.jid.BareJid;
@Entity(
tableName = "axolotl_device_list",
foreignKeys =
@ForeignKey(
entity = AccountEntity.class,
parentColumns = {"id"},
childColumns = {"accountId"},
onDelete = ForeignKey.CASCADE),
indices = {
@Index(
value = {"accountId", "address"},
unique = true)
})
public class AxolotlDeviceListEntity {
@PrimaryKey(autoGenerate = true)
public Long id;
@NonNull public Long accountId;
@NonNull public BareJid address;
@NonNull public Instant receivedAt;
public String errorCondition;
public boolean isParsingIssue;
public static AxolotlDeviceListEntity of(long accountId, final BareJid address) {
final var entity = new AxolotlDeviceListEntity();
entity.accountId = accountId;
entity.address = address;
entity.receivedAt = Instant.now();
entity.isParsingIssue = false;
return entity;
}
public static AxolotlDeviceListEntity of(
final long accountId, final BareJid address, final String errorCondition) {
final var entity = new AxolotlDeviceListEntity();
entity.accountId = accountId;
entity.address = address;
entity.receivedAt = Instant.now();
entity.errorCondition = errorCondition;
return entity;
}
public static AxolotlDeviceListEntity ofParsingIssue(final long account, BareJid address) {
final var entity = new AxolotlDeviceListEntity();
entity.accountId = account;
entity.address = address;
entity.receivedAt = Instant.now();
entity.isParsingIssue = true;
return entity;
}
}

View file

@ -1,42 +0,0 @@
package im.conversations.android.database.entity;
import androidx.annotation.NonNull;
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
@Entity(
tableName = "axolotl_device_list_item",
foreignKeys =
@ForeignKey(
entity = AxolotlDeviceListEntity.class,
parentColumns = {"id"},
childColumns = {"deviceListId"},
onDelete = ForeignKey.CASCADE),
indices = {
@Index(value = {"deviceListId"}),
@Index(
value = {"deviceListId", "deviceId"},
unique = true)
})
public class AxolotlDeviceListItemEntity {
@PrimaryKey(autoGenerate = true)
public Long id;
@NonNull public Long deviceListId;
public Integer deviceId;
public boolean confirmedInPep;
public static AxolotlDeviceListItemEntity of(
final long deviceListId, final int deviceId, final boolean confirmedInPep) {
final var entity = new AxolotlDeviceListItemEntity();
entity.deviceListId = deviceListId;
entity.deviceId = deviceId;
entity.confirmedInPep = confirmedInPep;
return entity;
}
}

View file

@ -1,51 +0,0 @@
package im.conversations.android.database.entity;
import androidx.annotation.NonNull;
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import im.conversations.android.database.model.Account;
import im.conversations.android.database.model.Trust;
import org.jxmpp.jid.BareJid;
import org.whispersystems.libsignal.IdentityKey;
@Entity(
tableName = "axolotl_identity",
foreignKeys =
@ForeignKey(
entity = AccountEntity.class,
parentColumns = {"id"},
childColumns = {"accountId"},
onDelete = ForeignKey.CASCADE),
indices = {
@Index(
value = {"accountId", "address", "identityKey"},
unique = true)
})
public class AxolotlIdentityEntity {
@PrimaryKey(autoGenerate = true)
public Long id;
@NonNull public Long accountId;
@NonNull public BareJid address;
@NonNull public IdentityKey identityKey;
@NonNull public Trust trust;
public static AxolotlIdentityEntity of(
final Account account,
final BareJid address,
final IdentityKey identityKey,
final Trust trust) {
final var entity = new AxolotlIdentityEntity();
entity.accountId = account.id;
entity.address = address;
entity.identityKey = identityKey;
entity.trust = trust;
return entity;
}
}

View file

@ -1,40 +0,0 @@
package im.conversations.android.database.entity;
import androidx.annotation.NonNull;
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import im.conversations.android.database.model.Account;
import org.whispersystems.libsignal.IdentityKeyPair;
@Entity(
tableName = "axolotl_identity_key_pair",
foreignKeys =
@ForeignKey(
entity = AccountEntity.class,
parentColumns = {"id"},
childColumns = {"accountId"},
onDelete = ForeignKey.CASCADE),
indices = {
@Index(
value = {"accountId"},
unique = true)
})
public class AxolotlIdentityKeyPairEntity {
@PrimaryKey(autoGenerate = true)
public Long id;
@NonNull public Long accountId;
@NonNull public IdentityKeyPair identityKeyPair;
public static AxolotlIdentityKeyPairEntity of(
final Account account, final IdentityKeyPair identityKeyPair) {
final var entity = new AxolotlIdentityKeyPairEntity();
entity.accountId = account.id;
entity.identityKeyPair = identityKeyPair;
return entity;
}
}

View file

@ -1,45 +0,0 @@
package im.conversations.android.database.entity;
import androidx.annotation.NonNull;
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import im.conversations.android.database.model.Account;
import org.whispersystems.libsignal.state.PreKeyRecord;
@Entity(
tableName = "axolotl_pre_key",
foreignKeys =
@ForeignKey(
entity = AccountEntity.class,
parentColumns = {"id"},
childColumns = {"accountId"},
onDelete = ForeignKey.CASCADE),
indices = {
@Index(
value = {"accountId"},
unique = false)
})
public class AxolotlPreKeyEntity {
@PrimaryKey(autoGenerate = true)
public Long id;
@NonNull public Long accountId;
@NonNull public Integer preKeyId;
@NonNull public PreKeyRecord preKeyRecord;
public boolean removed = false;
public static AxolotlPreKeyEntity of(Account account, int preKeyId, PreKeyRecord preKeyRecord) {
final var entity = new AxolotlPreKeyEntity();
entity.accountId = account.id;
entity.preKeyId = preKeyId;
entity.preKeyRecord = preKeyRecord;
entity.removed = false;
return entity;
}
}

View file

@ -1,47 +0,0 @@
package im.conversations.android.database.entity;
import androidx.annotation.NonNull;
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import im.conversations.android.database.model.Account;
import org.jxmpp.jid.BareJid;
import org.whispersystems.libsignal.state.SessionRecord;
@Entity(
tableName = "axolotl_session",
foreignKeys =
@ForeignKey(
entity = AccountEntity.class,
parentColumns = {"id"},
childColumns = {"accountId"},
onDelete = ForeignKey.CASCADE),
indices = {
@Index(
value = {"accountId", "address", "deviceId"},
unique = true)
})
public class AxolotlSessionEntity {
@PrimaryKey(autoGenerate = true)
public Long id;
@NonNull public Long accountId;
@NonNull public BareJid address;
@NonNull public Integer deviceId;
@NonNull public SessionRecord sessionRecord;
public static AxolotlSessionEntity of(
Account account, BareJid address, int deviceId, SessionRecord record) {
final var entity = new AxolotlSessionEntity();
entity.accountId = account.id;
entity.address = address;
entity.deviceId = deviceId;
entity.sessionRecord = record;
return entity;
}
}

View file

@ -1,46 +0,0 @@
package im.conversations.android.database.entity;
import androidx.annotation.NonNull;
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import im.conversations.android.database.model.Account;
import org.whispersystems.libsignal.state.SignedPreKeyRecord;
@Entity(
tableName = "axolotl_signed_pre_key",
foreignKeys =
@ForeignKey(
entity = AccountEntity.class,
parentColumns = {"id"},
childColumns = {"accountId"},
onDelete = ForeignKey.CASCADE),
indices = {
@Index(
value = {"accountId"},
unique = false)
})
public class AxolotlSignedPreKeyEntity {
@PrimaryKey(autoGenerate = true)
public Long id;
@NonNull public Long accountId;
@NonNull public Integer signedPreKeyId;
@NonNull public SignedPreKeyRecord signedPreKeyRecord;
public boolean removed = false;
public static AxolotlSignedPreKeyEntity of(
Account account, int signedPreKeyId, SignedPreKeyRecord record) {
final var entity = new AxolotlSignedPreKeyEntity();
entity.accountId = account.id;
entity.signedPreKeyId = signedPreKeyId;
entity.signedPreKeyRecord = record;
entity.removed = false;
return entity;
}
}

View file

@ -1,38 +0,0 @@
package im.conversations.android.database.entity;
import androidx.annotation.NonNull;
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import org.jxmpp.jid.Jid;
@Entity(
tableName = "blocked",
foreignKeys =
@ForeignKey(
entity = AccountEntity.class,
parentColumns = {"id"},
childColumns = {"accountId"},
onDelete = ForeignKey.CASCADE),
indices = {
@Index(
value = {"accountId", "address"},
unique = true)
})
public class BlockedItemEntity {
@PrimaryKey(autoGenerate = true)
public Long id;
@NonNull public Long accountId;
@NonNull public Jid address;
public static BlockedItemEntity of(final long accountId, final Jid address) {
final var entity = new BlockedItemEntity();
entity.accountId = accountId;
entity.address = address;
return entity;
}
}

View file

@ -1,61 +0,0 @@
package im.conversations.android.database.entity;
import androidx.annotation.NonNull;
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import com.google.common.base.Strings;
import im.conversations.android.xmpp.model.bookmark.Conference;
import im.conversations.android.xmpp.model.bookmark.Nick;
import java.util.Map;
import org.jxmpp.jid.Jid;
import org.jxmpp.jid.impl.JidCreate;
@Entity(
tableName = "bookmark",
foreignKeys =
@ForeignKey(
entity = AccountEntity.class,
parentColumns = {"id"},
childColumns = {"accountId"},
onDelete = ForeignKey.CASCADE),
indices = {
@Index(
value = {"accountId", "address"},
unique = true)
})
public class BookmarkEntity {
@PrimaryKey(autoGenerate = true)
public Long id;
@NonNull public Long accountId;
@NonNull public Jid address;
public String name;
public String nick;
public boolean autoJoin;
public String password;
public static BookmarkEntity of(
final long accountId, final Map.Entry<String, Conference> entry) {
final var address = JidCreate.fromOrNull(entry.getKey());
final var conference = entry.getValue();
if (address == null) {
return null;
}
final Nick nick = conference.getNick();
final var entity = new BookmarkEntity();
entity.accountId = accountId;
entity.address = address;
entity.autoJoin = conference.isAutoJoin();
entity.name = conference.getConferenceName();
entity.nick = Strings.emptyToNull(nick == null ? null : nick.getContent());
return entity;
}
}

View file

@ -1,36 +0,0 @@
package im.conversations.android.database.entity;
import androidx.annotation.NonNull;
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
@Entity(
tableName = "bookmark_group",
primaryKeys = {"bookmarkId", "groupId"},
foreignKeys = {
@ForeignKey(
entity = BookmarkEntity.class,
parentColumns = {"id"},
childColumns = {"bookmarkId"},
onDelete = ForeignKey.CASCADE),
@ForeignKey(
entity = GroupEntity.class,
parentColumns = {"id"},
childColumns = {"groupId"},
onDelete = ForeignKey.RESTRICT),
},
indices = {@Index(value = "groupId")})
public class BookmarkGroupEntity {
@NonNull public Long bookmarkId;
@NonNull public Long groupId;
public static BookmarkGroupEntity of(long bookmarkId, final long groupId) {
final var entity = new BookmarkGroupEntity();
entity.bookmarkId = bookmarkId;
entity.groupId = groupId;
return entity;
}
}

View file

@ -1,41 +0,0 @@
package im.conversations.android.database.entity;
import androidx.annotation.NonNull;
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import im.conversations.android.tls.ScopeFingerprint;
@Entity(
tableName = "certificate_trust",
foreignKeys =
@ForeignKey(
entity = AccountEntity.class,
parentColumns = {"id"},
childColumns = {"accountId"},
onDelete = ForeignKey.CASCADE),
indices = {
@Index(
value = {"accountId", "scope"},
unique = true)
})
public class CertificateTrustEntity {
@PrimaryKey(autoGenerate = true)
public Long id;
@NonNull public Long accountId;
@NonNull public String scope;
@NonNull public byte[] fingerprint;
public static CertificateTrustEntity of(
final long accountId, final ScopeFingerprint scopeFingerprint) {
final var entity = new CertificateTrustEntity();
entity.accountId = accountId;
entity.scope = scopeFingerprint.scope;
entity.fingerprint = scopeFingerprint.fingerprint.array();
return entity;
}
}

Some files were not shown because too many files have changed in this diff Show more