Compare commits

...

3086 Commits

Author SHA1 Message Date
Greyson Parrelli
ebbf8fad4b Bump version to 7.2.2 2024-04-01 20:17:55 -04:00
Greyson Parrelli
5891c6fb2d Update translations and other static files. 2024-04-01 20:17:23 -04:00
Greyson Parrelli
7c96319fb6 Fix potential NPE in forwarding flow. 2024-04-01 20:17:23 -04:00
Greyson Parrelli
0d652ccfd6 Listen for recipient name changes in conversation item. 2024-04-01 19:58:05 -04:00
Greyson Parrelli
d3718aa7ef Make nickname FF hot-swappable and default to true. 2024-04-01 19:17:07 -04:00
Cody Henthorne
fcdcb9fd33 Fix linked device nickname change not syncing bug. 2024-04-01 19:06:08 -04:00
Nicholas Tinsley
a8f925def0 Move delete button in Nickname activity. 2024-04-01 17:49:48 -04:00
Nicholas Tinsley
53cb125712 Prevent NPE in video editor. 2024-04-01 14:33:21 -04:00
Nicholas Tinsley
2a5793d96e Allow saving empty notes with empty nicknames. 2024-04-01 14:13:39 -04:00
Nicholas Tinsley
d460fa7ed4 Disable view-once toggle in media caption editor.
Fixes #13492.
2024-04-01 11:37:34 -04:00
Nicholas Tinsley
5272b13c41 Bump version to 7.2.1 2024-03-29 17:35:46 -04:00
Nicholas Tinsley
a66ac42038 Update translations and other static files. 2024-03-29 17:27:15 -04:00
Nicholas Tinsley
0014a2cba7 Hide camera switch icon during calls for devices with 1 or fewer cameras. 2024-03-29 17:21:27 -04:00
Nicholas Tinsley
7a9c01e6e5 Remove vestigial call camera toggle button. 2024-03-29 17:21:27 -04:00
Nicholas Tinsley
16402e43a5 Make in-app camera compatible with multi-window. 2024-03-29 17:21:27 -04:00
Nicholas Tinsley
b1944da58d Show nickname for 1:1 chat bottom sheet. 2024-03-29 17:21:27 -04:00
Nicholas Tinsley
9081d3c826 Revise recipient bottom sheet. 2024-03-29 17:21:27 -04:00
Nicholas Tinsley
d4ae0ca4cb Update conversation settings string. 2024-03-29 17:21:27 -04:00
Nicholas Tinsley
88f6ab915e Do not show nickname field for Note to Self. 2024-03-29 17:21:27 -04:00
Nicholas Tinsley
939024faff Don't show profile name parentheses if we don't have one. 2024-03-29 12:43:58 -04:00
Nicholas Tinsley
4c6e7991df Preserve username exceptions in ProGuard. 2024-03-29 12:40:12 -04:00
Nicholas Tinsley
036d91c039 Align linked device megaphone lifespan.
Thank you to Signal.DE from the community forum.
2024-03-28 15:59:08 -04:00
oguzhandogdu
869c922532 Remove second bullet span function 2024-03-28 14:57:26 -04:00
oguzhandogdu
217d15a853 Add gap width to bullet span 2024-03-28 14:57:26 -04:00
Nicholas Tinsley
931ffd0ba3 Bump version to 7.2.0 2024-03-27 16:01:18 -04:00
Nicholas Tinsley
fecac297fa Update translations and other static files. 2024-03-27 15:57:11 -04:00
Nicholas Tinsley
b0ea8d7df5 Prevent crash on devices with camera killswitches.
Addresses #13450.
2024-03-27 15:54:35 -04:00
Nicholas Tinsley
f126df2120 Put custom controller behind feature flag. 2024-03-27 15:54:35 -04:00
Nicholas Tinsley
42450024fc Add profile name to about sheet. 2024-03-27 15:54:35 -04:00
Nicholas Tinsley
101db6e164 Apply SignalTheme to NicknameActivity. 2024-03-27 15:54:35 -04:00
Nicholas Tinsley
13bef94bf7 Update icons in conversation settings. 2024-03-27 15:54:35 -04:00
Nicholas Tinsley
02792c5a6f Remove extraneous time unit conversion. 2024-03-27 15:54:35 -04:00
Alex Hart
303929090b Implement the majority of the new nicknames and notes feature. 2024-03-27 15:54:35 -04:00
Alex Hart
7a24554b68 Update ContactRecord proto with new nickname fields. 2024-03-27 15:54:35 -04:00
Greyson Parrelli
5b10aa6fa7 Handle 428 for captcha submissions. 2024-03-27 15:54:35 -04:00
Alex Konradi
e6eefac609 Upgrade to libsignal 0.42.0 2024-03-27 15:54:35 -04:00
Alex Hart
5f5a80dcbe Stub out MoreOptionsSheet and RestoreFromBackupFragment. 2024-03-27 15:54:35 -04:00
Cody Henthorne
7802448b24 Fix unblock icon tint in dark theme. 2024-03-27 15:54:35 -04:00
Clark
16d231f718 Persist blur hash with undownloaded attachments. 2024-03-27 15:54:35 -04:00
Cody Henthorne
62ca6cdd2f Fix can't receive audio and video pip render bug. 2024-03-27 15:54:35 -04:00
Cody Henthorne
7d81ed1150 Fix call controls disappearing when returning from system pip. 2024-03-27 15:54:35 -04:00
Greyson Parrelli
27812bb1ec Don't save duplicate queries in Spinner. 2024-03-27 15:54:35 -04:00
Greyson Parrelli
6854f7eb2a Add an 'internal details' screen for message details. 2024-03-27 15:54:35 -04:00
Clark
de86c5622d Integrate more variation in backup test generation. 2024-03-27 15:54:35 -04:00
Jim Gustafson
6bf1a4295f Update to RingRTC v2.39.2 2024-03-27 15:54:35 -04:00
Alex Hart
7de2f0f460 Add nickname and notes fields to the RecipientTable. 2024-03-27 15:54:35 -04:00
Greyson Parrelli
50149a3803 Show a megaphone when a device is about to unlink. 2024-03-27 15:54:35 -04:00
Greyson Parrelli
d7ee9639fd Be more lenient with quality matches when forwarding attachments. 2024-03-19 14:49:56 -04:00
Nicholas Tinsley
7d5627b17b Fix in-app camera rotation in multiview. 2024-03-19 14:48:38 -04:00
Greyson Parrelli
e24c951d83 Convert MiscellaneousValues to kotlin. 2024-03-19 14:47:58 -04:00
Alex Hart
e6a11c1ccf Revert "Fix pip placement in large calls."
This reverts commit aaeba4efe1.
2024-03-19 14:47:58 -04:00
Greyson Parrelli
3f66981359 Do not show username megaphone after a fresh install. 2024-03-19 14:47:58 -04:00
Cody Henthorne
874f808d56 Add process read sync tests. 2024-03-19 14:47:58 -04:00
Greyson Parrelli
450dc2f368 Improve logging around APNG animation disabling. 2024-03-19 14:47:58 -04:00
Alex Hart
7a69df42a7 Add receive support for new call log event data. 2024-03-19 14:47:58 -04:00
Greyson Parrelli
1ce1e30d32 Carry over the sent media quality when forwarding a video. 2024-03-19 14:47:58 -04:00
Greyson Parrelli
011f1d592e Fix bug with quote deduping. 2024-03-19 14:47:58 -04:00
Greyson Parrelli
1d29b0166d Backfill missing attachment hashes. 2024-03-19 14:47:58 -04:00
Greyson Parrelli
6df1a68213 Refactor and improve attachment deduping logic. 2024-03-19 14:47:58 -04:00
Nicholas Tinsley
b7ee6bfcb3 Don't show transfer overlay for scheduled messages. 2024-03-19 14:47:58 -04:00
Nicholas Tinsley
c0cb2b5e12 Allow seeking in video timeline. 2024-03-19 14:47:58 -04:00
Alex Hart
b38865bdc7 Implement UI element refresh on transfer or restore screen. 2024-03-19 14:47:58 -04:00
Alex Hart
6f46331772 Add call log event proto updates. 2024-03-19 14:47:58 -04:00
Clark
989bd662c6 Add tests to generate backup with large amount of messages and chats. 2024-03-19 14:47:58 -04:00
Nicholas Tinsley
359e593481 Add support for hardware camera features in the in-app camera. 2024-03-19 14:47:58 -04:00
Nicholas Tinsley
b7e0fe22db Add CameraX Extensions and update CameraX to 1.3.2. 2024-03-19 14:47:58 -04:00
Alex Hart
61cfbd6852 Allow ringer to ring in certain dnd situations. 2024-03-19 14:47:58 -04:00
Rashad Sookram
02c0d3ed6e Update to RingRTC v2.39.1 2024-03-19 14:47:58 -04:00
Cody Henthorne
e4d6c3aeb2 Do not send viewed receipt for release channel. 2024-03-19 14:47:58 -04:00
Chris Eager
0c6761fcfd Update Option.RECAPTCHA to Option.CAPTCHA 2024-03-19 14:47:58 -04:00
Greyson Parrelli
8f884fdd5c Fix potential crash when parsing PreKeySyncJobData.
Honestly at this point I have no idea how this is happening.
Maybe somehow getting old data that was empty but not null?
A mystery for the ages.
2024-03-19 14:47:58 -04:00
Greyson Parrelli
07cea1818e Ensure that protocol stores are reset after setting ACI/PNI. 2024-03-19 14:47:58 -04:00
Cody Henthorne
132bc15373 Fix ANR when changing the configuration of a foldable. 2024-03-19 14:47:58 -04:00
Clark
d993748753 Generate backup protos with message backup instrumentation tests. 2024-03-19 14:47:58 -04:00
Greyson Parrelli
3372565a39 Improve logging around consistency checks. 2024-03-19 14:47:58 -04:00
Alex Hart
134ac2b2fd Fix display name resolution for my story. 2024-03-19 14:47:58 -04:00
Alex Hart
0e0e91b4fe Hide invite banner when entering conversation search. 2024-03-19 14:47:58 -04:00
Greyson Parrelli
25b50bdb8f Rotate the libsignal-cdsi feature flag. 2024-03-19 14:47:58 -04:00
Alex Konradi
1988085171 Don't strip libsignal.net classes. 2024-03-19 14:47:58 -04:00
Nicholas Tinsley
f892e9baff Update disappearing messages text. 2024-03-19 14:47:58 -04:00
Alex Konradi
4828d84caf Update libsignal to 0.41.2 2024-03-19 14:47:58 -04:00
Greyson Parrelli
aeae6ac292 Remove deprecated blocked field from DeviceContact. 2024-03-19 14:47:58 -04:00
Alex Hart
0544c1f249 Display group call permissions dialog when trying to start a call in annoucment group when not an admin. 2024-03-19 14:47:58 -04:00
Greyson Parrelli
5027159ed8 Improve handling of unregistered states in profile screen. 2024-03-19 14:47:58 -04:00
Cody Henthorne
ce778be895 Resume call PIP on app foreground. 2024-03-19 14:47:58 -04:00
Cody Henthorne
9e349d2b30 Mute video when closing system PIP during a call. 2024-03-19 14:47:58 -04:00
Fumiaki Yoshimatsu
72f19758db Fix chat search when using Japanese IMEs.
Resolves #13467
2024-03-19 14:47:58 -04:00
Greyson Parrelli
55bce1fa12 Fix potential NPE when pinning a PNI chat. 2024-03-19 14:47:58 -04:00
AsamK
5e1ebaa5d4 Fix various storage service issues.
Resolves #13466
2024-03-19 14:47:58 -04:00
Clark
742c348998 Add test restore flow to staging reg. 2024-03-19 14:47:58 -04:00
Clark
9d46b52786 Backup attachments as Attachment locators. 2024-03-19 14:47:57 -04:00
Clark
ef374952ab Add tests for update messages except for groups and calls. 2024-03-19 14:47:57 -04:00
Clark
f8ef4d5985 Add tests for text messages with mentions, quotes, reactions, and ranges. 2024-03-19 14:47:57 -04:00
Cody Henthorne
85929809f0 Bump version to 7.1.3 2024-03-19 14:36:32 -04:00
Cody Henthorne
068540120e Updated baseline profile. 2024-03-19 14:33:44 -04:00
Cody Henthorne
471c4fc200 Update translations and other static files. 2024-03-19 14:28:49 -04:00
Nicholas Tinsley
398c67362d Improve layout for view once toast for older devices. 2024-03-19 14:24:26 -04:00
Nicholas Tinsley
4ceeda5f02 Fix video review page for API <28. 2024-03-19 14:24:26 -04:00
Nicholas Tinsley
2bf6b993fe Somewhat reduce emoji keyboard jankiness in media review fragment. 2024-03-19 14:24:26 -04:00
Nicholas Tinsley
68363c5b82 Disable emoji button for view-once media. 2024-03-19 14:24:26 -04:00
Nicholas Tinsley
9f47a41017 Restore pinch to zoom gesture in in-app camera. 2024-03-19 13:46:19 -04:00
Nicholas Tinsley
ba70101efd Add view-once button to media caption. 2024-03-19 12:00:36 -04:00
Greyson Parrelli
3aa54c9982 Remove some unused permissions. 2024-03-18 19:21:47 -04:00
Greyson Parrelli
825ca0d737 Remove more SMS vestiges. 2024-03-18 19:21:08 -04:00
Clark Chen
6754fef164 Bump version to 7.1.2 2024-03-11 18:50:24 -04:00
Clark Chen
4c079a8c25 Update translations and other static files. 2024-03-11 18:32:59 -04:00
Nicholas Tinsley
6e09d101b5 Debounce camera switcher button. 2024-03-11 18:26:45 -04:00
Nicholas Tinsley
39aa583297 Respect newlines in media review UI. 2024-03-11 18:26:45 -04:00
Nicholas Tinsley
b08db7a8c5 Fix unexpected trimming behavior with long videos. 2024-03-11 18:26:45 -04:00
Alex Hart
865bf0d056 Fix nav bar getting out of sync with keyboard pager. 2024-03-11 18:26:45 -04:00
Nicholas Tinsley
d52c520c02 Explicitly pause video player when not focused. 2024-03-11 18:26:45 -04:00
Nicholas Tinsley
1eabf11cdb Fix tap-to-focus UI for in-app camera. 2024-03-11 18:26:45 -04:00
Cody Henthorne
cfb16d3f17 Fix link rendering under spoilers in read more view. 2024-03-11 18:26:45 -04:00
Alex Hart
d5707638a6 Apply proper theming to FindByActivity. 2024-03-11 18:26:45 -04:00
Alex Hart
5cda5db7f7 Disable text field when view-once is selected. 2024-03-11 11:46:52 -03:00
Alex Hart
5c5d55d265 Introduce glyph fonts to correct spacing. 2024-03-11 11:14:05 -03:00
Alex Hart
4dd3b92eda Prevent crash when review banner wants to display self. 2024-03-11 09:52:24 -03:00
Cody Henthorne
112579079f Fix bad button text wrapping in message request view. 2024-03-08 16:57:15 -05:00
Greyson Parrelli
9897ba4b28 Properly pluralize a string. 2024-03-08 14:39:28 -05:00
Greyson Parrelli
c64dfff4c7 Fix typo in string. 2024-03-07 22:40:26 -05:00
Alex Hart
915b3f0cd3 Bump version to 7.1.1 2024-03-07 17:02:41 -04:00
Alex Hart
c295d11fc4 Updated baseline profile. 2024-03-07 17:01:52 -04:00
Alex Hart
bc47c5436d Update translations and other static files. 2024-03-07 16:56:59 -04:00
Alex Hart
aaeba4efe1 Fix pip placement in large calls. 2024-03-07 16:53:36 -04:00
Alex Hart
3c0eb58381 Apply alpha to v2 conversation item footer content. 2024-03-07 16:53:36 -04:00
Alex Hart
c4f22449f9 Hide thumbnails in specific cases in quote view. 2024-03-07 16:53:36 -04:00
Greyson Parrelli
bca346ec2f Improve copy for unregistered users. 2024-03-07 16:53:36 -04:00
Alex Hart
e0bd60f87c Adjust styling and sizing for rationale dialog. 2024-03-07 16:53:36 -04:00
Alex Hart
aeedab1531 Adjust spacing for contact and verified images on conversation settings page. 2024-03-07 16:53:35 -04:00
Greyson Parrelli
c959f41c68 Improve message send performance. 2024-03-07 16:53:35 -04:00
Alex Hart
9ba755da16 Add section header to find by username / ph row. 2024-03-07 16:53:35 -04:00
Alex Hart
34026c5538 Add proper tinting to refresh and invite rows. 2024-03-07 10:04:16 -04:00
Nicholas Tinsley
ea64425456 Media sending design improvements. 2024-03-07 09:38:13 -04:00
Alex Hart
eb34a20195 Bump version to 7.1.0 2024-03-06 20:50:34 -04:00
Alex Hart
445513cc32 Updated baseline profile. 2024-03-06 20:50:06 -04:00
Alex Hart
e431518a9d Update translations and other static files. 2024-03-06 20:45:13 -04:00
Alex Hart
61df88e094 Fix TestUsers construction in benchmark. 2024-03-06 20:42:01 -04:00
Greyson Parrelli
891c130e12 Sync the PNI identity used in sent transcripts. 2024-03-06 20:42:01 -04:00
Greyson Parrelli
b4ced5278e Fix recipient merging case that causes a change number event. 2024-03-06 20:42:01 -04:00
Greyson Parrelli
10364e9342 Disable next button in FindByActivity when input is blank. 2024-03-06 20:42:01 -04:00
Alex Hart
74dc222a54 Add Recency support for contact search ordering. 2024-03-06 20:42:01 -04:00
Greyson Parrelli
2e4ac7ede1 Always perform CDSI lookups when starting new chats. 2024-03-06 20:42:01 -04:00
Cody Henthorne
184c1b67cc Add learned profile name event. 2024-03-06 20:42:01 -04:00
Alex Hart
f702338129 Fix hide story action state. 2024-03-06 20:42:01 -04:00
Alex Hart
4b4b263423 Usernames 1.01 Fast-Follow Part 1. 2024-03-06 20:42:01 -04:00
Nicholas Tinsley
83c16a46de Update assets for image editor. 2024-03-06 20:42:01 -04:00
Clark
6383896a79 Fix incoming group updates showing as updated. 2024-03-06 20:42:01 -04:00
Nicholas Tinsley
5fa1560a10 Add stroke to draw tool color bar. 2024-03-06 20:42:01 -04:00
Nicholas Tinsley
9bd6ad36cc Round corners of selected region in video trimmer. 2024-03-06 20:42:01 -04:00
Nicholas Tinsley
83cc7d5181 Adjust media tool button animation. 2024-03-06 20:42:01 -04:00
Nicholas Tinsley
44150673e9 Adjust size of quality selector buttons. 2024-03-06 20:42:01 -04:00
Nicholas Tinsley
5092d723a8 Update media send symbols. 2024-03-06 20:42:01 -04:00
Cody Henthorne
218964cbda Add archive media apis. 2024-03-06 20:42:01 -04:00
Nicholas Tinsley
ccc9752485 Hoist video editor state out of VideoEditorFragment. 2024-03-06 20:42:01 -04:00
Cody Henthorne
619038f27d Improve local fanout send performance. 2024-03-06 20:42:01 -04:00
Alex Hart
9f197b12ed Add support for call log mark as read. 2024-03-06 20:42:01 -04:00
Jim Gustafson
690608cdf3 Update to RingRTC v2.39.0
Co-authored-by: Alex Hart <alex@signal.org>
2024-03-06 20:42:01 -04:00
Alex Hart
4035932340 Fix find-by slide animations. 2024-03-06 20:42:01 -04:00
Clark
fc9d94701c Disable job manager in instrumentation tests by default. 2024-03-06 20:42:01 -04:00
Alex Hart
6d54ae5f3d Update call link info sheet to match new designs. 2024-03-06 20:42:01 -04:00
Nicholas Tinsley
c53abe0941 Video Sending Redesign 2024-03-06 20:42:01 -04:00
Greyson Parrelli
276e253fdf Fix individual send metrics. 2024-03-06 20:42:01 -04:00
Greyson Parrelli
f160e960be Stop setting pq flag since it's no longer read. 2024-03-06 20:42:01 -04:00
Greyson Parrelli
41b57b9207 Remove unnecessary uniqueness constraint on prekey tables. 2024-03-06 20:42:01 -04:00
Alex Konradi
56eae8c7bf Add libsignal-net CDSI implementation. 2024-03-06 20:42:01 -04:00
Alex Hart
46c8b3b690 Replace full text with call link name as title in call info sheet. 2024-03-06 20:42:01 -04:00
Greyson Parrelli
58b11f3c47 Do a CDS refresh when a new chat is created. 2024-03-06 20:42:01 -04:00
Alex Hart
40b4b316b3 Add new options to share call link details from details fragment. 2024-03-06 20:42:01 -04:00
Clark
7a31f69aea Add tests for import/export of call logs. 2024-03-06 20:42:01 -04:00
Greyson Parrelli
648c99e81d Ensure we are updating last resort metadata after change number. 2024-03-06 20:42:01 -04:00
Greyson Parrelli
56b482a26f Allow scanning QR code from 'Find by username' screen. 2024-03-06 20:42:01 -04:00
Cody Henthorne
c6df4af53a Update bank transfer timeline strings. 2024-03-06 20:42:01 -04:00
Clark
32fe927bfc Add import/export tests for backup of recipients and threads. 2024-03-06 20:42:01 -04:00
Ehren Kret
5740b768d0 Use full organization name in README. 2024-03-06 20:42:01 -04:00
Ehren Kret
d8e74c730a Update README copyright year. 2024-03-06 20:42:00 -04:00
Clark
58846bbf42 Add import/export test for initially account data. 2024-03-06 20:42:00 -04:00
Greyson Parrelli
78d30fc479 Remove deprecated SVR2 enclaves. 2024-03-06 20:42:00 -04:00
Cody Henthorne
86afa988a0 Add ability to scan username qr from gallery. 2024-03-06 20:42:00 -04:00
Greyson Parrelli
6104ef62df Detect username QR codes in our camera-first capture flow. 2024-03-06 20:42:00 -04:00
Cody Henthorne
3f89acf9bd Disable parallel gradle in GH actions. 2024-03-06 20:42:00 -04:00
Cody Henthorne
591d499462 Show system contact icon in more places. 2024-03-06 20:42:00 -04:00
Greyson Parrelli
c31a7152bc Update built-in emoji to v15.1 2024-03-06 20:42:00 -04:00
Greyson Parrelli
343cc3ca67 Update third party licenses. 2024-03-06 20:42:00 -04:00
Cody Henthorne
23e18cee22 Add nobody can find me by number setting warning. 2024-03-06 20:42:00 -04:00
Cody Henthorne
5b2c458bcf Show username in all display name locations if only option. 2024-03-06 20:42:00 -04:00
Cody Henthorne
c10c64a6a6 Prepopulate find number with local user country code. 2024-03-06 20:42:00 -04:00
Cody Henthorne
957221e118 Fix cut off icon in conversation header. 2024-03-06 20:42:00 -04:00
Cody Henthorne
e18e4454e4 Fix multi-invite group create dialog. 2024-03-06 20:42:00 -04:00
Greyson Parrelli
e1067e30de Add support for endpoint checking prekey consistency. 2024-03-06 20:42:00 -04:00
Greyson Parrelli
09b0f15294 Remove unused capabilities. 2024-03-06 20:42:00 -04:00
Greyson Parrelli
b1d6ff4bbd Remove the PNP build variant. 2024-03-06 20:42:00 -04:00
Cody Henthorne
a49e9dd96d Fix crash adjusting constraints during large calls. 2024-03-06 20:42:00 -04:00
Clark
5e428e2c4d Backup and restore mentions. 2024-03-06 20:42:00 -04:00
Clark
0f6ff3c101 Integrate backup file validation to backup playground. 2024-03-06 20:42:00 -04:00
Clark
1ade8b502f Convert and store new group changes in MessageExtras. 2024-03-06 20:42:00 -04:00
Alex Konradi
cc25f0685c Update libsignal version to v0.40.1 2024-03-06 20:42:00 -04:00
Nicholas Tinsley
e91ed88785 CameraX Custom Controller.
Addresses #12817, #13316, #13389
2024-03-06 20:42:00 -04:00
Jon Chambers
39bc6d5eb3 Remove legacy signed prekey endpoint. 2024-02-23 16:42:58 -05:00
Nicholas Tinsley
b7f472b0cd Update Google services libraries. 2024-02-23 16:42:58 -05:00
Cody Henthorne
942f4a45bf Fix reply icon not mirroring in RTL. 2024-02-23 16:42:58 -05:00
Cody Henthorne
767896b14c Fix infinite pending link preview on large previews. 2024-02-23 16:42:58 -05:00
Nicholas Tinsley
8c35628863 Adjust copy depending on PNP settings. 2024-02-23 16:42:58 -05:00
Cody Henthorne
d555370076 Fix edit call link copy in bottom sheet. 2024-02-23 16:42:58 -05:00
Cody Henthorne
bbbe76697d Fix invalid date on group member search results. 2024-02-23 16:42:58 -05:00
Nicholas Tinsley
fc1d60e65b Fallback to matching video decoder by MIME type. 2024-02-23 16:42:57 -05:00
Nicholas Tinsley
9dc856202a Don't use 0 milliseconds as sending retry interval. 2024-02-23 16:42:57 -05:00
Jim Gustafson
6bc41776b1 Update to RingRTC v2.38.0 2024-02-23 16:42:57 -05:00
Greyson Parrelli
940cee0f30 Bump version to 7.0.2 2024-02-23 16:26:12 -05:00
Greyson Parrelli
cdb6c16473 Update translations and other static files. 2024-02-23 16:25:05 -05:00
Greyson Parrelli
c4842ae7c5 Attempt to prevent message retry loops. 2024-02-23 15:36:23 -05:00
Greyson Parrelli
dc32e51ac2 Make a specific crash more clear to improve debuggability. 2024-02-23 15:36:23 -05:00
Greyson Parrelli
43caaf7efc Update a specific recipient case to merge rather than just steal PNI. 2024-02-23 15:36:23 -05:00
Greyson Parrelli
dcd0d433b0 Fix potential charset crash on some devices. 2024-02-23 15:36:23 -05:00
Cody Henthorne
763e891dfd Show username in group invite flow. 2024-02-23 15:36:23 -05:00
Cody Henthorne
c04f761f5a Show rate limit specific error message on username reservation. 2024-02-23 15:36:23 -05:00
Cody Henthorne
b147882e4f Fix text input selection handle colors. 2024-02-23 15:36:23 -05:00
Cody Henthorne
c9f5f91aad Fix black bars on username scan crosshair. 2024-02-23 15:36:23 -05:00
Cody Henthorne
0a3de42729 Fix username QR image generation for multiline usernames. 2024-02-23 15:36:23 -05:00
Greyson Parrelli
64fc0209f4 Remove unused endpoint. 2024-02-23 15:36:23 -05:00
Cody Henthorne
418ad51e77 Fix share your username popup icon tint. 2024-02-23 15:36:23 -05:00
Cody Henthorne
16faf41a84 Fix profile name not updating correctly. 2024-02-23 15:36:23 -05:00
Cody Henthorne
d5cf8d36b3 Fix username recovery UX bugs. 2024-02-23 15:36:23 -05:00
Greyson Parrelli
755fafb0b6 Always use US locale when logging rounded numbers. 2024-02-21 13:08:43 -05:00
Greyson Parrelli
8fc9893ecd Improve logging around retries archiving sessions. 2024-02-21 13:04:42 -05:00
Greyson Parrelli
9071fd0024 Bump version to 7.0.1 2024-02-20 21:45:57 -05:00
Greyson Parrelli
9a3233bb28 Update translations and other static files. 2024-02-20 21:45:30 -05:00
Greyson Parrelli
e77bc9170a Fix RTL rendering of username edit screen. 2024-02-20 21:36:24 -05:00
Greyson Parrelli
23d6a71a3b Update username validation to use libsignal. 2024-02-20 21:36:24 -05:00
Greyson Parrelli
67c3f41dff Fix crash when attempting to start a call via username. 2024-02-20 16:02:57 -05:00
Greyson Parrelli
e22fa499c2 Reduce username debounce rate to 500ms. 2024-02-20 15:25:27 -05:00
Cody Henthorne
fdef13ae92 Apply LQA feedback. 2024-02-20 15:02:39 -05:00
Greyson Parrelli
c0a6f2316c Do not acknowledge retry receipts sent to PNI. 2024-02-20 12:24:32 -05:00
Greyson Parrelli
7d6a87c825 Attempt to fix missing session crash for resends. 2024-02-20 12:13:54 -05:00
Greyson Parrelli
6c863fe99c Clean up capability logging. 2024-02-18 18:34:18 -05:00
Greyson Parrelli
5cf8242ea0 Bump version to 7.0.0 2024-02-16 16:44:19 -05:00
Greyson Parrelli
a804e8a27c Update translations and other static files. 2024-02-16 16:42:53 -05:00
Greyson Parrelli
3b598e2f07 Update string for username creation. 2024-02-16 15:53:05 -05:00
Greyson Parrelli
03c5a254e8 Fix issue with receiving server delivery receipts at PNI. 2024-02-15 22:05:57 -05:00
Greyson Parrelli
bdb34e16c6 Update UI and strings for the duplicate name review screen. 2024-02-15 21:43:36 -05:00
Cody Henthorne
e7c018283a Perform directory refresh after a PNI invite accept. 2024-02-15 21:43:36 -05:00
Cody Henthorne
ebd8d85a3d Implement UX feedback in new conversation start flows. 2024-02-15 21:43:36 -05:00
Greyson Parrelli
3f8a9e1be2 Reduce max discriminator length to 9. 2024-02-15 21:43:36 -05:00
Greyson Parrelli
0d5961baf9 Update string on find by username screen. 2024-02-15 21:43:36 -05:00
Greyson Parrelli
872ee805d1 Assume PNP capability is true. 2024-02-15 21:43:36 -05:00
Nicholas Tinsley
b19bcd88b9 Null check VideoPlayer during view binding. 2024-02-15 21:43:36 -05:00
Greyson Parrelli
5c9d65386b Fix username deletion sync issue. 2024-02-15 21:43:36 -05:00
Greyson Parrelli
a86a0938ce Fix avatar fallback photo for self when useSelfProfileAvatar=true. 2024-02-15 21:43:36 -05:00
Greyson Parrelli
a886e5f9a0 Directly show about sheet when you show a recipient sheet for yourself. 2024-02-15 21:43:36 -05:00
Greyson Parrelli
83c1bd61cb Add bottom sheet handle to RecipientBottomSheet. 2024-02-15 21:43:36 -05:00
Greyson Parrelli
4ce1789110 Do not show the discriminator field until one is chosen. 2024-02-15 21:43:36 -05:00
Greyson Parrelli
f484fdbbac Remove unused 'registration' variant of username screen. 2024-02-15 21:43:36 -05:00
Greyson Parrelli
57ac7cb328 Show some more info in the about sheet. 2024-02-15 21:43:36 -05:00
Greyson Parrelli
47cdc50a81 Add confirmation dialog when changing username would reset link. 2024-02-15 21:43:36 -05:00
Greyson Parrelli
555ddb5b20 Show dialog for successfully resetting your username link. 2024-02-15 21:43:36 -05:00
Greyson Parrelli
8e8ba23da7 Do not show the QR code shortuct if you have no username. 2024-02-15 21:43:36 -05:00
Greyson Parrelli
54a1b97167 Update username description string in edit profile screen. 2024-02-15 21:43:36 -05:00
Greyson Parrelli
7530d44d28 Refactor username link share screen to enable previews. 2024-02-15 21:43:36 -05:00
Greyson Parrelli
252aa3714e Sync the 'hasCompletedUsernameOnboarding' flag. 2024-02-15 21:43:36 -05:00
Greyson Parrelli
ce09e9a217 Update UI for PNP launch megaphone. 2024-02-15 21:43:35 -05:00
Greyson Parrelli
8797236b5a Add migration for ensuring we set the latest pnp settings. 2024-02-15 21:43:35 -05:00
Greyson Parrelli
6097e6c305 Default discoverability to 'off' until registration is complete. 2024-02-15 21:43:35 -05:00
Greyson Parrelli
879fca0e11 Interpret unknown phone number sharing setting as 'off'. 2024-02-15 21:43:35 -05:00
Greyson Parrelli
c359ddf3c8 Inline the pnp feature flag. 2024-02-15 21:43:35 -05:00
Greyson Parrelli
8ad77ac7aa Inline the username flag. 2024-02-15 21:43:35 -05:00
Cody Henthorne
bd3b779282 Bump version to 6.47.4 2024-02-15 21:43:16 -05:00
Cody Henthorne
42b805eb91 Updated baseline profile. 2024-02-15 21:37:08 -05:00
Cody Henthorne
107f2cd3b1 Update translations and other static files. 2024-02-15 21:31:52 -05:00
Nicholas Tinsley
c713ccf76c Don't mark outgoing media as upload only. 2024-02-15 21:26:30 -05:00
Cody Henthorne
dd9c65012b Fix NPE when find by phone row not supported. 2024-02-15 19:36:26 -05:00
Cody Henthorne
31e872a34e Bump version to 6.47.3 2024-02-14 20:05:57 -05:00
Cody Henthorne
81579dc9bf Update translations and other static files. 2024-02-14 20:00:20 -05:00
Greyson Parrelli
3c6c03cd75 Fix bug when syncing username deletions. 2024-02-14 11:59:15 -05:00
Greyson Parrelli
ba41df19bb Fix thread merges where one thread is inactive. 2024-02-14 11:15:49 -05:00
Cody Henthorne
0cc7178cdc Bump version to 6.47.2 2024-02-13 15:49:18 -05:00
Cody Henthorne
239e4a7e66 Updated baseline profile. 2024-02-13 15:46:30 -05:00
Cody Henthorne
0b031d35e3 Update translations and other static files. 2024-02-13 15:43:43 -05:00
Nicholas Tinsley
32e81049f5 Cycle audio remux feature flag. 2024-02-13 15:39:04 -05:00
Nicholas Tinsley
6a65a1c149 Default to 1x camera in video recording.
Addresses #11955, #12482, #13017

Shout out to @tedgravlin for #13411.
2024-02-13 15:39:04 -05:00
Cody Henthorne
36ea2a7f5d Fix active call manager.
For real this time.
2024-02-13 15:39:04 -05:00
Cody Henthorne
cc40c9d09f Bump version to 6.47.1 2024-02-12 16:32:01 -05:00
Cody Henthorne
fb9e731e00 Updated baseline profile. 2024-02-12 16:27:07 -05:00
Cody Henthorne
264607e0f3 Update translations and other static files. 2024-02-12 16:21:55 -05:00
Greyson Parrelli
cdfc42cc38 Fix possible crash in story replies. 2024-02-12 15:01:58 -05:00
Cody Henthorne
19cfae1da5 Remove duplicate future code. 2024-02-12 15:00:27 -05:00
Jim Gustafson
577b11a349 Update to RingRTC v2.37.1 2024-02-12 09:15:55 -05:00
Clark Chen
671dfceac3 Bump version to 6.47.0 2024-02-09 19:43:41 -05:00
Clark Chen
5626fb74ae Update translations and other static files. 2024-02-09 19:28:27 -05:00
Greyson Parrelli
88d6c91517 Fix some bugs related to how the conversation header renders. 2024-02-09 18:41:26 -05:00
Cody Henthorne
aa76cefb1c Update spam UX and reporting flows. 2024-02-09 18:41:26 -05:00
Cody Henthorne
a4fde60c1c Fix crash in ActiveCallManager for lower Android versions. 2024-02-09 15:19:25 -05:00
Nicholas Tinsley
8e86612fc2 Derive PIN keyboard type if none set.
Addresses #13338.
2024-02-09 15:11:38 -05:00
Clark
f6ded23383 Ignore message latency when latency is before device boot time. 2024-02-09 14:55:58 -05:00
Nicholas Tinsley
2ab689c59b Add plural string for chat deletion. 2024-02-09 13:32:05 -05:00
Greyson Parrelli
155f6a88f8 Ensure one-time kyber prekeys are generated during change number. 2024-02-09 13:32:05 -05:00
Greyson Parrelli
3d84fc9c98 Use 'deleted account' as name for certain unregisterd user cases. 2024-02-09 13:32:05 -05:00
Greyson Parrelli
976e146248 Only count active threads when determining possible communication. 2024-02-09 13:32:05 -05:00
Greyson Parrelli
d9b0723194 Make message footer clickable when there's an error. 2024-02-09 13:32:05 -05:00
Greyson Parrelli
0630a6910a Update strings and previews for pnp settings. 2024-02-09 13:32:05 -05:00
Greyson Parrelli
bc930345b9 Remove blockSSE feature flag. 2024-02-08 14:10:20 -05:00
Greyson Parrelli
d7d7963101 Defer to the latest revision for backup inclusion. 2024-02-08 14:10:20 -05:00
Greyson Parrelli
fa2551dfcf Always show the new 'find by phone number' item. 2024-02-08 14:10:20 -05:00
Greyson Parrelli
b260a47b49 Fix issue where media sent transcripts didn't trigger thread updates. 2024-02-08 14:10:20 -05:00
Greyson Parrelli
47e55fc621 Do not show add to contacts if there's no e164. 2024-02-08 14:10:20 -05:00
Alex Hart
700fe5e463 Add Find By Username and Find By Phone Number interstitials.
Co-authored-by: Greyson Parrelli <greyson@signal.org>
2024-02-08 14:10:20 -05:00
Greyson Parrelli
ca3d239ce2 Bump version to 6.46.7 2024-02-08 12:54:26 -05:00
Greyson Parrelli
12385b9c5a Update stores to use a constant accountId for PNIs. 2024-02-08 12:53:22 -05:00
Clark Chen
65b26adb3d Bump version to 6.46.6 2024-02-06 15:26:38 -05:00
Clark Chen
31b8927291 Update translations and other static files. 2024-02-06 15:16:55 -05:00
Cody Henthorne
94ffbb3e8e Fix NPE crash when scanning safety numbers. 2024-02-06 10:32:20 -05:00
Greyson Parrelli
1b7616b4db Fix issue with PNIs in contact sync. 2024-02-06 09:47:12 -05:00
Clark Chen
71850e1e35 Bump version to 6.46.5 2024-02-05 18:56:05 -05:00
Clark Chen
380fb60791 Update translations and other static files. 2024-02-05 18:36:27 -05:00
Greyson Parrelli
2cf9fa0524 Fix crash when opening group story replies. 2024-02-05 16:30:54 -05:00
Nicholas Tinsley
c8c4fdc65e Bump version to 6.46.4 2024-02-05 10:59:36 -05:00
Nicholas Tinsley
9fb16be74f Updated baseline profile. 2024-02-05 10:56:19 -05:00
Nicholas Tinsley
2d4d4aab66 Update translations and other static files. 2024-02-05 10:53:17 -05:00
Nicholas Tinsley
f18070b78c Revert "Don't recreate attachment InputStream if we don't have to."
This reverts commit 467dae8132.
2024-02-05 10:49:05 -05:00
Nicholas Tinsley
adf1d8a43a Bump version to 6.46.3 2024-02-03 14:08:31 -05:00
Nicholas Tinsley
f360934ddd Updated baseline profile. 2024-02-03 14:04:58 -05:00
Nicholas Tinsley
b986c4d54c Update translations and other static files. 2024-02-03 14:02:12 -05:00
Nicholas Tinsley
4545e70384 Remix audio remuxing into its own feature flag. 2024-02-03 13:57:49 -05:00
Nicholas Tinsley
3c5cbc3114 Bump version to 6.46.2 2024-02-03 12:45:27 -05:00
Nicholas Tinsley
879b0ad95d Updated baseline profile. 2024-02-03 12:41:31 -05:00
Nicholas Tinsley
9982810edb Update translations and other static files. 2024-02-03 12:38:24 -05:00
Greyson Parrelli
699b788187 Fix possible stack overflow in backup export. 2024-02-03 12:26:38 -05:00
Nicholas Tinsley
bd61044643 Bump version to 6.46.1 2024-02-02 15:24:32 -05:00
Nicholas Tinsley
6804d58323 Updated baseline profile. 2024-02-02 15:17:04 -05:00
Nicholas Tinsley
bfe57b4b8f Update translations and other static files. 2024-02-02 15:12:26 -05:00
Greyson Parrelli
e3fe852a34 Do not backup past revision if latest revision is soon-to-expire. 2024-02-02 15:07:30 -05:00
Cody Henthorne
673fe2625c Show iDEAL 0,01 temporary charge warning dialog for monthly subscription. 2024-02-02 15:07:30 -05:00
Cody Henthorne
e798feb1d7 Use paymentId from create PayPal intent instead of confirm. 2024-02-02 15:07:30 -05:00
Nicholas Tinsley
fa937f9f43 Bump version to 6.46.0 2024-01-31 22:42:48 -05:00
Nicholas Tinsley
ec5f3fc333 Updated baseline profile. 2024-01-31 22:37:58 -05:00
Nicholas Tinsley
d85c150a8c Update translations and other static files. 2024-01-31 22:35:13 -05:00
Nicholas Tinsley
a5faf0e098 Match output video file size limit in video editor. 2024-01-31 22:13:46 -05:00
Alex Hart
1234c63836 Add scaffolding for backupsV2. 2024-01-31 22:13:46 -05:00
Cody Henthorne
91920319c7 Cycle calling without a service remote config. 2024-01-31 22:13:46 -05:00
Nicholas Tinsley
950d9d5a4c Validate image preview as URI.
Resolves #13392.
2024-01-31 22:13:46 -05:00
Nicholas Tinsley
467dae8132 Don't recreate attachment InputStream if we don't have to. 2024-01-31 22:13:46 -05:00
Nicholas Tinsley
d1ef9d5dcf Align video compression limit with attachment limit. 2024-01-31 22:13:46 -05:00
Nicholas Tinsley
a60419a442 Add remote config for GIF keyboard page. 2024-01-31 22:13:46 -05:00
Nicholas Tinsley
f97a034c34 Null check multi share attachment quality guard. 2024-01-31 22:13:46 -05:00
Greyson Parrelli
4d0fbe2343 Add a dark theme for spinner. 2024-01-31 22:13:46 -05:00
Jameson Williams
1d5e108cd4 QA target can pass on ARM MacBook
The Conscrypt library does not have a native library suitable for
running on the ARM MacBooks. As a result, unit tests that rely on
Conscrypt also don't work on this hardware.

Fortunately, though, the tests will pass without Conscrypt anyway. As a
workaround, we can avoid using Conscrypt only when running the unit test
suite on these machines.

See also: https://github.com/google/conscrypt/issues/1034

Resolves #13382.
2024-01-31 22:13:46 -05:00
Greyson Parrelli
716afc98ac Sync PNI verification status to storage service. 2024-01-31 22:13:46 -05:00
Greyson Parrelli
459607adae Clear local state after requesting to ignore battery optimizations. 2024-01-31 22:13:46 -05:00
Cody Henthorne
784b705265 Fix spoiler in notification with emoji prefix. 2024-01-31 22:13:46 -05:00
Alex Hart
326b95bd10 Upgrade Compose and AndroidX libraries. 2024-01-31 22:13:46 -05:00
Greyson Parrelli
466acaf504 Fix possible exception when rendering SSE event.
Fixes #13390
2024-01-31 22:13:46 -05:00
Maksym Moroz
838165c3e6 Register gradle tasks instead of creating eagerly.
Signed-off-by: Maksym Moroz <maksymmoroz@duck.com>

Resolves #13391
2024-01-31 22:13:46 -05:00
Jim Gustafson
e1d7ad7d03 Update to RingRTC v2.37.0 2024-01-31 22:13:46 -05:00
Greyson Parrelli
3776e86b83 Update the types of messages we backup. 2024-01-31 22:13:46 -05:00
Greyson Parrelli
ea60858a07 Improve logging around 'contact joined' messages. 2024-01-31 22:13:46 -05:00
Greyson Parrelli
d4488c72fb Simplify contact splitting when reading from storage service. 2024-01-31 22:13:46 -05:00
Greyson Parrelli
9146f2fb30 Remove some unused methods.
Fixes ##13384
2024-01-31 22:12:17 -05:00
Greyson Parrelli
92993f967e Clear MSL after SN change. 2024-01-31 22:12:17 -05:00
Greyson Parrelli
2c2735af6d Do not create new threads to show error messages. 2024-01-31 22:12:17 -05:00
Alex Hart
80c0e19692 Improve sizing of shareable QR image. 2024-01-31 22:12:17 -05:00
Alex Hart
c9e2162afc Remove old username education fragment. 2024-01-31 22:12:17 -05:00
Nicholas Tinsley
9a52f4e3ff Remux audio if possible when transcoding.
Addresses #11712, #12674, #12945, #13084, #13346.
2024-01-31 22:12:17 -05:00
Greyson Parrelli
c0235d4cc2 Ensure contacts are split after profile 404. 2024-01-31 22:12:17 -05:00
Cody Henthorne
f1704fbb57 Fix file attachment dedup logic. 2024-01-31 22:12:17 -05:00
Alex Hart
38d5d3ad1b Add polish to usernames UX. 2024-01-31 22:12:17 -05:00
Jameson Williams
ec96b4e3aa Update Glide to use ksp, drop kapt.
Resolves #13381
2024-01-31 22:12:17 -05:00
Cody Henthorne
aa33fd44b8 Remove SMS export. 2024-01-31 22:12:17 -05:00
Clark
98865d61dd Convert gv2 update messages to backup distinct protos. 2024-01-31 22:12:17 -05:00
Cody Henthorne
0036b8e2d6 Migrate legacy png and webp to signal symbols. 2024-01-31 22:12:17 -05:00
Alex Hart
c021d26103 Add polish to PNP registration settings. 2024-01-31 22:12:17 -05:00
Alex Hart
8bca5b4901 Update PnP description text for everyone listing. 2024-01-31 22:12:17 -05:00
Greyson Parrelli
d4db6c8912 Keep some recipient logs longer. 2024-01-31 22:12:17 -05:00
Jameson Williams
c93b4909f4 Remove Jetifier from build.
materialish-progress and subsampling-scale-image-view were bringing in
Android Support libraries as transitive dependencies. This required
Jetifier to be run as part of the build. More recent versions of these
dependencies have been released, which now use AndroidX directly. By
upgrading these dependencies, Jetifier is no longer needed to build
Signal.

Resolves #13378
2024-01-31 22:12:17 -05:00
Maksym Moroz
67d3c8e777 Clean up app module build.gradle.kts
Closes #13379

Signed-off-by: Maksym Moroz <maksymmoroz@duck.com>
2024-01-31 22:12:17 -05:00
Nicholas Tinsley
8d44222097 Enable logcat in the video sample app. 2024-01-31 22:12:17 -05:00
Nicholas Tinsley
9cb2024334 Option to persist task in video sample app. 2024-01-31 22:12:17 -05:00
Alex Hart
e71bb33b23 Fix conversation bubbles becoming too long.
Co-authored-by: Cody Henthorne <cody@signal.org>
2024-01-31 22:12:17 -05:00
Alex Hart
4ada7c9be9 Implement several polish items for Call Links. 2024-01-31 22:12:16 -05:00
Nicholas Tinsley
56a2d8891f Bump version to 6.45.2 2024-01-31 22:10:10 -05:00
Nicholas Tinsley
4585e90a00 Updated baseline profile. 2024-01-31 21:25:05 -05:00
Nicholas Tinsley
86d2ddc168 Update translations and other static files. 2024-01-31 21:18:34 -05:00
Greyson Parrelli
9d1514308a Fix potential crash during contact splits. 2024-01-31 19:39:39 -05:00
Greyson Parrelli
7abff55981 Bump version to 6.45.1 2024-01-25 19:10:08 -05:00
Greyson Parrelli
f9d7eba7d4 Update translations and other static files. 2024-01-25 19:09:11 -05:00
Greyson Parrelli
9ce021afa2 Improve SenderKeyDistributionMessage envelope validation. 2024-01-25 18:59:57 -05:00
Nicholas Tinsley
6fc9055221 Calculate video encoding settings based on trimmed duration. 2024-01-25 18:59:57 -05:00
Greyson Parrelli
a3438d3345 Improve DecryptionErrorMessage envelope validation. 2024-01-25 14:42:50 -05:00
Nicholas Tinsley
d2cbf11264 Don't fail video send on postprocess failure. 2024-01-25 13:32:03 -05:00
Greyson Parrelli
c584156c86 Bump version to 6.45.0 2024-01-23 13:27:17 -05:00
Greyson Parrelli
78e04f3ad8 Update baseline profile. 2024-01-23 13:18:57 -05:00
Greyson Parrelli
6302725678 Update translations and other static files. 2024-01-23 13:18:57 -05:00
Nicholas Tinsley
431e65808d Rotate custom video muxer flag. 2024-01-23 13:18:57 -05:00
Clark
653914f47e Allow multiple message latency percentiles. 2024-01-23 13:18:57 -05:00
Cody Henthorne
96823e944d Introduce ActiveCallManager to prevent android service crashes during call handling. 2024-01-23 13:18:57 -05:00
Nicholas Tinsley
ee19520e1b Improve video sample app UI. 2024-01-23 13:18:56 -05:00
Nicholas Tinsley
6f16b3fee7 Add video sample app test dependencies. 2024-01-23 13:18:56 -05:00
Nicholas Tinsley
89ee7e8e19 Update AndroidX test library verisons. 2024-01-23 13:18:56 -05:00
Nicholas Tinsley
3c7996aa99 Rotate instant video feature flag. 2024-01-23 13:18:56 -05:00
Nicholas Tinsley
3a314c565c Deduplicate call participants in call info screen.. 2024-01-23 13:18:56 -05:00
Nicholas Tinsley
8c9b668cd7 Rotate incrementalMac proto field. 2024-01-23 13:18:56 -05:00
Nicholas Tinsley
7666462de2 Support Dolby Vision HDR videos in transcoder. 2024-01-23 13:18:56 -05:00
Nicholas Tinsley
54cf11a78b Request notification permissions for video sample app. 2024-01-23 11:52:04 -05:00
Greyson Parrelli
16b78f0843 Update username recovery flow. 2024-01-23 11:52:04 -05:00
Greyson Parrelli
5e97a6b192 Do not show username education if username is set. 2024-01-23 11:52:04 -05:00
Nicholas Tinsley
595cced5b7 Display overflow button with reactions OR raise hand. 2024-01-23 11:52:04 -05:00
Alex Hart
d17f12dd76 Update call event state after revoking a call link. 2024-01-23 11:52:04 -05:00
Alex Hart
8b5498cfbd Fix if check for call links. 2024-01-23 11:52:04 -05:00
Clark Chen
e4b755ced8 Correctly count message events for notification heuristic. 2024-01-23 11:52:04 -05:00
Nicholas Tinsley
5b7eb9c332 Update libsignal-client to 0.39.2 2024-01-23 11:52:04 -05:00
Alex Hart
cd8e07c102 Do not jump to top of the list when no search query present in story privacy. 2024-01-23 11:52:04 -05:00
Nicholas Tinsley
a36f31c2d0 Allow full disablement of StreamingTranscoder output size limit. 2024-01-23 11:52:04 -05:00
Nicholas Tinsley
ac0812a6dd Update AndroidX Media3 to 1.2.1. 2024-01-23 11:52:04 -05:00
Nicholas Tinsley
69c864f984 Allow slight variations in video output frame count. 2024-01-23 11:52:04 -05:00
Nicholas Tinsley
3c9a7fd329 Reorganize video converter library packages 2024-01-23 11:52:04 -05:00
Alex Hart
f81dc11f61 Prevent crash if a call doesn't have a message associated with it. 2024-01-23 11:52:04 -05:00
Greyson Parrelli
ce9a8f62d4 Check for network connectivity before making any substantial username alteration request. 2024-01-23 11:52:04 -05:00
Alex Hart
7e00d50078 Fix add to groups display without query. 2024-01-23 11:52:04 -05:00
Alex Hart
4af3f5038f Fix call labeling in settings page. 2024-01-23 11:52:04 -05:00
Nicholas Tinsley
7bb1c58452 Refactor Mp4FaststartPostProcessor. 2024-01-23 11:52:04 -05:00
Nicholas Tinsley
8e7383be05 Make Mp4Writer output compatible with Quicktime. 2024-01-23 11:52:04 -05:00
Cody Henthorne
bc5d27ed90 Prevent calling service crash loop when system restarts us in background. 2024-01-23 11:52:04 -05:00
Greyson Parrelli
ae884d79a1 Remove system contact links from undiscoverable contacts. 2024-01-23 11:52:04 -05:00
Pratyush Venkatakrishnan
cf89c988cf Set structured name when creating contacts.
Fixes #12305. When creating copies of contacts, set the structured name
fields instead of the unstructured name fields. This fixes bugs where
certain types of names are displayed incorrectly because the structured
name is inaccurately reconstructed from the unstructured name.

Closes #13366
2024-01-23 11:52:04 -05:00
Cody Henthorne
c54313c32e Add another verification code test case. 2024-01-23 11:52:04 -05:00
Cody Henthorne
3e001ddf1b Fix poor calling audio on certain devices. 2024-01-23 11:52:04 -05:00
Nicholas Tinsley
c41795e7f0 Extract postprocessor lambda into interface. 2024-01-23 11:52:04 -05:00
Nicholas Tinsley
52120afdbd Remove dependency on Guava limited stream. 2024-01-23 11:52:04 -05:00
Alex Hart
73d98da32b Polish about sheet UX. 2024-01-23 11:52:04 -05:00
Alex Hart
99f936ff97 Fix about sheet groups in common placeholder count. 2024-01-23 11:52:04 -05:00
Cody Henthorne
15afaeabe3 Use WebpSanitizer. 2024-01-23 11:52:04 -05:00
Greyson Parrelli
debf964b5f Add more info to internal conversation details. 2024-01-23 11:52:04 -05:00
Nicholas Tinsley
393730cea9 Fix video sample app output. 2024-01-23 11:52:04 -05:00
Greyson Parrelli
2194fbd535 Add support for fetching archive media metadata. 2024-01-23 11:52:04 -05:00
Alex Hart
cf59249d3d Rewrite chat colors delegation. 2024-01-23 11:52:04 -05:00
Alex Hart
2c554a3a20 Implement new ways to connect splash and megaphone. 2024-01-23 11:52:04 -05:00
Alex Hart
7b9554a42c Align profile screen with figma. 2024-01-23 11:52:03 -05:00
Alex Hart
dd527ce33c Align pnp privacy settings screens with figma. 2024-01-23 11:52:03 -05:00
Nicholas Tinsley
ddcc06c6b7 Update video transcoding sample cleanup file extension. 2024-01-23 11:52:03 -05:00
Alex Hart
a827033f25 Ensure incoming story reactions are marked as such. 2024-01-23 11:52:03 -05:00
Alex Hart
01841a4aa8 Set body on OutgoingMessage when adding to a group story. 2024-01-23 11:52:03 -05:00
Alex Hart
bb52e7159c Pluralize groups in common. 2024-01-23 11:52:03 -05:00
Ruben De Smet
3988b46a60 Fix typo for generateLastRes*t*ortKyberPreKey. 2024-01-23 11:52:03 -05:00
Nicholas Tinsley
caa5e233df Expose StreamingTranscoder configuration options in sample app. 2024-01-23 11:52:03 -05:00
Nicholas Tinsley
c7609f9a2a StreamingTranscoder sample app. 2024-01-23 11:52:03 -05:00
Greyson Parrelli
750fd4efe1 Improve safety of update and delete database methods. 2024-01-23 11:52:03 -05:00
Greyson Parrelli
e361795184 Improve logging and the naming of some fields. 2024-01-23 11:52:03 -05:00
Greyson Parrelli
64fff2adb2 Do not show contact info for non-discoverable contacts. 2024-01-23 11:52:03 -05:00
farewelltospring
846e398577 Add emoji to content description in EmojiImageView.
Fixes #13358
2024-01-23 11:52:03 -05:00
Greyson Parrelli
c1e9ee7a66 Allow changing the PNP config remotely. 2024-01-23 11:52:03 -05:00
Greyson Parrelli
8dc9e09f31 Bump version to 6.44.3 2024-01-23 11:44:33 -05:00
Greyson Parrelli
d1930d4936 Update baseline profile. 2024-01-23 11:44:31 -05:00
Greyson Parrelli
14539eb036 Update translations and other static files. 2024-01-23 11:28:39 -05:00
Greyson Parrelli
45f1853c44 Fix issue opening files in external apps. 2024-01-23 11:16:09 -05:00
Cody Henthorne
6eaebd112b Bump version to 6.44.2 2024-01-16 12:00:33 -05:00
Cody Henthorne
f0503faeff Updated baseline profile. 2024-01-16 11:54:11 -05:00
Cody Henthorne
64052d9dd2 Update translations and other static files. 2024-01-16 11:48:28 -05:00
Greyson Parrelli
db4634a0dd Fix potential NPE during group send. 2024-01-16 10:13:21 -05:00
Alex Hart
c725a2fabb Bump version to 6.44.1 2024-01-12 16:40:49 -04:00
Alex Hart
1ddececa16 Updated baseline profile. 2024-01-12 16:36:13 -04:00
Alex Hart
4e2e6cd83e Update translations and other static files. 2024-01-12 16:33:05 -04:00
Alex Hart
efcfe2dafc Fix call disposition update issue. 2024-01-12 15:59:45 -04:00
Alex Hart
b8ddb9e673 Fix several story issues around bad manifest interactions. 2024-01-12 10:11:06 -04:00
Alex Hart
a1f19e9d8a Bump version to 6.44.0 2024-01-11 17:13:48 -04:00
Alex Hart
5464edf639 Updated baseline profile. 2024-01-11 17:13:27 -04:00
Alex Hart
179c3790e6 Update translations and other static files. 2024-01-11 17:10:48 -04:00
Greyson Parrelli
cfae9753a3 Cleanup 2024-01-11 15:56:52 -05:00
Greyson Parrelli
61a4a3b322 Add support for restoring usernames post-registration. 2024-01-11 15:56:51 -05:00
Nicholas Tinsley
c16bf65a80 Change audio tone for raise hand. 2024-01-11 14:09:08 -05:00
Greyson Parrelli
16ea1912b4 fixup! Combine username confirmation and link creation into a single operation. 2024-01-11 12:29:40 -05:00
Greyson Parrelli
54012cb33a Combine username confirmation and link creation into a single operation. 2024-01-11 12:00:44 -05:00
Alex Hart
459c5c0a55 Username UX polish. 2024-01-11 11:32:38 -04:00
Greyson Parrelli
4216b56443 Update types of messages we backup. 2024-01-11 10:26:05 -05:00
Alex Hart
d7b79314d9 Fix crash if donation error dialog is dismissed after fragment disappears. 2024-01-11 11:09:37 -04:00
Cody Henthorne
a340b13f65 Fix group call continuing to ring after accepted on another device. 2024-01-11 10:01:27 -05:00
Alex Hart
72f6b15dba Hide header decorations when no subtitle or description is set. 2024-01-11 09:48:27 -04:00
Alex Hart
64dbb77e63 Fix clipping and padding on about sheet. 2024-01-10 17:01:56 -04:00
Alex Hart
af10b0e4f6 Clip avatar click indication to avatar circle. 2024-01-10 16:49:54 -04:00
Cody Henthorne
6f15c16a42 Add notification profile specific events for missed calls. 2024-01-10 15:30:45 -05:00
Alex Hart
86158027d7 Fix conversation header margins on thinner screens. 2024-01-10 16:25:26 -04:00
Greyson Parrelli
50369890f7 Refactor username state to use Username models. 2024-01-10 14:57:31 -05:00
Cody Henthorne
b8dea25aef Fix loss of formatted text on copy. 2024-01-10 14:08:02 -05:00
Cody Henthorne
64e9324aa0 Default new notification profiles to allow calls. 2024-01-10 11:53:08 -05:00
Cody Henthorne
20f8c69b07 Add donate_friend remote megaphone action. 2024-01-10 11:52:55 -05:00
Nicholas Tinsley
dd1a15c249 fixup! Refactor video testapp. 2024-01-10 11:52:20 -05:00
Nicholas Tinsley
8b24498fa7 Refactor video testapp. 2024-01-10 11:50:54 -05:00
Nicholas Tinsley
3673fa4908 Null safety for TransformProperties during attachment compression. 2024-01-09 16:59:20 -05:00
Nicholas Tinsley
960c1df5e7 Apply faststart to videos transcoded using Streams. 2024-01-09 16:59:20 -05:00
Greyson Parrelli
8c3c7c18ad Fix backup restore issue with new attachment table name. 2024-01-09 16:17:14 -05:00
Greyson Parrelli
b96a5af133 Fix issue when opening view-once messages. 2024-01-09 15:24:47 -05:00
Alex Hart
d0d4008100 Add cleanup job for group ringing. 2024-01-09 13:40:50 -04:00
Alex Hart
17a6fcafa1 Add ability to set custom username discriminators. 2024-01-09 11:37:39 -04:00
Bernie Dolan
fb75440769 Update payments to 6.0.1 2024-01-09 11:12:48 -04:00
Greyson Parrelli
fe39b5e4e2 Clean up AttachmentTable schema. 2024-01-09 11:12:48 -04:00
Alex Hart
62b142cdeb Add new state transitions for group call disposition. 2024-01-09 11:12:48 -04:00
Nicholas Tinsley
ffce7213b4 fixup! Fix width of attachment download status text. 2024-01-09 11:12:48 -04:00
Nicholas Tinsley
4205934806 fixup! Fix width of attachment download status text. 2024-01-09 11:12:48 -04:00
Nicholas Tinsley
7aab86643a Fix width of attachment download status text. 2024-01-09 11:12:48 -04:00
Alex Hart
1bb0c55d88 Cleanup unused imports in AttachmentTable. 2024-01-09 11:12:48 -04:00
Jim Gustafson
d22ac9ee00 Update to RingRTC v2.36.0 2024-01-09 11:12:48 -04:00
Greyson Parrelli
80a7db2511 Fix crash when sending trimmed videos. 2024-01-09 11:12:48 -04:00
Nicholas Tinsley
e0fb102572 Prevent back gesture during video trimming. 2024-01-09 11:12:48 -04:00
Cody Henthorne
8d1a16dcd6 Remove story references from multi-recipient saftey number change sheet. 2024-01-09 11:12:48 -04:00
Cody Henthorne
0b4bbd5db2 Fix unread decorator position when read follow unread. 2024-01-09 11:12:48 -04:00
Jim Gustafson
78b714e019 Remove legacy call message fields 2024-01-09 11:12:48 -04:00
Nicholas Tinsley
5022d81d9a Allow canceling media attachment send. 2024-01-09 11:12:48 -04:00
Nicholas Tinsley
deacf28d77 Make entire video preview file size bubble clickable. 2024-01-09 11:12:48 -04:00
Cody Henthorne
5e8d324860 Fix large balance issues. 2024-01-09 11:12:48 -04:00
Greyson Parrelli
3554f82ea3 Convert AttachmentTable and models to kotlin. 2024-01-09 11:12:48 -04:00
Alex Hart
888a40a5c4 Bump version to 6.43.2 2024-01-09 10:50:04 -04:00
Alex Hart
363953a0a4 Updated baseline profile. 2024-01-09 10:35:17 -04:00
Alex Hart
e599d9b14e Updated baseline profile. 2024-01-09 10:29:00 -04:00
Alex Hart
a33be1fad3 Update translations and other static files. 2024-01-09 10:22:14 -04:00
Greyson Parrelli
b6528e843e Fix profile fetches for empty groups. 2024-01-08 10:43:49 -05:00
Clark Chen
10c31e6591 Bump version to 6.43.1 2024-01-05 16:34:19 -05:00
Clark Chen
8fba64cb8f Update translations and other static files. 2024-01-05 16:21:05 -05:00
Nicholas Tinsley
c2fd08ca80 Display attachment download progress in MB. 2024-01-05 15:34:12 -05:00
Nicholas Tinsley
940bf0603e Only play consecutive voice notes. 2024-01-04 16:52:35 -05:00
Nicholas Tinsley
4d8a3dafe0 Do not play end tone for individual voice memo. 2024-01-04 16:23:40 -05:00
Nicholas Tinsley
d237bb0136 WebRtcCallView cleanups 2024-01-04 15:52:56 -05:00
Nicholas Tinsley
d42dfd3edd Don't hide call controls when interacting with PiP. 2024-01-04 15:50:31 -05:00
Alex Hart
ab4f17d55f Fix contentColor in dark mode on bottom sheets. 2024-01-04 15:54:14 -04:00
Alex Hart
07968febe8 Fix color tinting of icons in conversation header view. 2024-01-04 11:36:40 -04:00
Alex Hart
67ff0892d5 Fix bug where description would overwrite subtitle of conversation header. 2024-01-04 11:32:40 -04:00
Alex Hart
f1ee168657 Hide header decoration when in the release notes chat. 2024-01-04 11:18:28 -04:00
Nicholas Tinsley
5fef60c2b0 Only play raise hand sound if no other hands are raised. 2024-01-04 09:59:01 -05:00
Clark Chen
4afffc7dd3 Bump version to 6.43.0 2024-01-03 15:54:16 -05:00
Clark Chen
7e6346a694 Update translations and other static files. 2024-01-03 15:33:59 -05:00
Cody Henthorne
abf22eff44 Fix gift donation getting stuck in pending. 2024-01-03 15:12:45 -05:00
Cody Henthorne
3fa3b93c85 Fix improper notifications when delaying for linked device activity. 2024-01-03 15:12:45 -05:00
Nicholas Tinsley
549ef9dabc Revert "Merge database writes for attachment compression."
This reverts commit c50993bbf7c4a9e10a253f8d234c621ededffc47.
2024-01-03 15:12:45 -05:00
Nicholas Tinsley
59c75663b1 Adjust download size margin. 2024-01-03 15:12:45 -05:00
Greyson Parrelli
820a5bc363 Add extra guard during db migration.
Relates to #13183
2024-01-03 15:12:45 -05:00
Nicholas Tinsley
1b9cf631be Play sound for raised hand. 2024-01-03 15:12:45 -05:00
Nicholas Tinsley
b4f2208bae Merge database writes for attachment compression. 2024-01-03 15:12:45 -05:00
Greyson Parrelli
f4bcfca323 Add upload support for the main backup file in backupV2. 2024-01-03 15:12:45 -05:00
Nicholas Tinsley
f93a9a0f22 Sort call participants by raised hand. 2024-01-03 15:12:45 -05:00
Clark Chen
e5652197eb Fix content description for send message button. 2024-01-03 15:12:45 -05:00
Nicholas Tinsley
4a102d44cb Show raise hand on each particpiant. 2024-01-03 15:12:45 -05:00
Nicholas Tinsley
c837840e04 Properly hide toolbar gradient in calling view. 2024-01-03 15:12:45 -05:00
Nicholas Tinsley
b280ff7495 Instant video processing metadata. 2024-01-03 15:12:45 -05:00
Nicholas Tinsley
f6d8dcf6fd Accurate remaining download size for partially downloaded galleries. 2024-01-03 15:12:45 -05:00
Nicholas Tinsley
fa0661f58a Instant video playback for very small video files. 2024-01-03 15:12:45 -05:00
Nicholas Tinsley
e2fe137b05 Attachment download progress view fixes. 2024-01-03 15:12:45 -05:00
Alex Hart
b434e955ac Fix dropped gradient background from text stories sent from desktop. 2024-01-03 15:12:45 -05:00
Clark
d74b302edb Add remaining non-group update messages for backup. 2024-01-03 15:12:45 -05:00
Nicholas Tinsley
0200430346 TransferControlView padding tweak. 2024-01-03 15:12:45 -05:00
Alex Hart
d70ebc2398 Update style for conversation header view. 2024-01-03 15:12:45 -05:00
Alex Hart
2b606a2dec Add update-tick for call log timestamps. 2024-01-03 15:12:45 -05:00
Nicholas Tinsley
9c7f2250b9 Don't set attachment progress to 1 if it's complete. 2024-01-03 15:12:45 -05:00
Greyson Parrelli
c2ee621f64 Move maybeMarkRegistrationComplete to be non-blocking. 2024-01-03 15:12:45 -05:00
Greyson Parrelli
b2cdb46c84 Remove now-unnecessary data from prekey upload request. 2024-01-03 15:12:45 -05:00
Haris Dautovic
6d150aa5cb Move attachment constraints check to a background thread.
Fixes #13296
Closes #13306
2024-01-03 15:12:45 -05:00
Greyson Parrelli
62ece66f36 Fix bug where scheduled messages don't update snippets. 2024-01-03 15:12:45 -05:00
Nicholas Tinsley
628cd3896c Don't attempt to launch viewer for deleted view-once media. 2024-01-03 15:12:45 -05:00
Greyson Parrelli
f10418face Convert RetrieveProfileJob to kotlin. 2024-01-03 15:12:45 -05:00
Alex Hart
ca9a629804 Restyling review banner and cards. 2024-01-03 15:12:45 -05:00
Greyson Parrelli
bb30535afb Respect the phoneNumberSharing setting on the profile. 2024-01-03 15:12:44 -05:00
Cody Henthorne
624f863da4 Ensure call links UX is still available post new calling features. 2024-01-03 15:12:44 -05:00
Nicholas Tinsley
b55a9f253e Improve animations for reactions feed. 2024-01-03 15:12:44 -05:00
Nicholas Tinsley
5b9ef5b6b6 Separate string resources for edited message footer. 2024-01-03 15:12:44 -05:00
Cody Henthorne
e7c8ecbd31 Fix storage sync validation crash with local only unknown ids. 2024-01-03 15:12:44 -05:00
Cody Henthorne
592dfec8db Fix spacing between donate gateway buttons. 2024-01-03 15:12:44 -05:00
Cody Henthorne
23ebccc041 Fix notification profile toast crash. 2024-01-03 15:12:44 -05:00
Greyson Parrelli
036bd51298 Update libsignal-client to 0.37.0 2024-01-03 15:12:44 -05:00
Nicholas Tinsley
6d9a66cc41 Send download job cancellations upon remote delete. 2024-01-03 15:12:44 -05:00
Nicholas Tinsley
1923b84a01 Expand forwarding search touch target. 2024-01-03 15:12:44 -05:00
Alex Hart
9924e293c9 Implement new about sheet. 2024-01-03 15:12:44 -05:00
Cody Henthorne
490d3549e2 Attempt to fix message replies bottom sheet overlap. 2024-01-03 15:12:44 -05:00
Nicholas Tinsley
45d2a5d0b6 Make emoji burst more "out of the reaction". 2023-12-19 11:14:04 -05:00
Jim Gustafson
4d3929948c Update to RingRTC v2.35.0 2023-12-19 11:14:04 -05:00
Nicholas Tinsley
56ea09431f Make time duration dialog scrollable.
Addresses #13202.
2023-12-19 11:14:04 -05:00
Nicholas Tinsley
a53a5f4685 Calling 2.1 Improvements 2023-12-19 11:14:04 -05:00
Clark
52f3ff5ff6 Fix case where we delete unknown remote records but also handle unknown ids. 2023-12-19 11:14:04 -05:00
Alex Hart
7150783848 Revert "Fix case where we delete unknown remote records. "
This reverts commit ab29d194bb677ac51c2ad225e894e37f10cf6599.
2023-12-19 11:14:04 -05:00
Nicholas Tinsley
c03d3520d6 Raise hand polish. 2023-12-19 11:14:02 -05:00
Clark
d2e19c5129 Fix case where we delete unknown remote records. 2023-12-19 11:12:57 -05:00
Alex Hart
a829165f2d Clone overflow spannable in attempt to reduce flickering. 2023-12-19 11:12:57 -05:00
Alex Hart
f2707d053d Fix ANR when deleting a video during story creation. 2023-12-19 11:12:57 -05:00
Haris Dautovic
2a4ccf69b2 Use ViewCompat.setTransitionName in a safe way.
Fixes #13307
2023-12-19 11:12:57 -05:00
Alex Hart
818356dfed Add gift badge title to row item to mirror iOS. 2023-12-19 11:12:57 -05:00
Alex Hart
49d6743cbb Fix conversation list jank after returning from calls tab. 2023-12-19 11:12:57 -05:00
Nicholas Tinsley
9ed80d46b6 Add confirmation dialog for lowering a raised hand. 2023-12-19 11:12:57 -05:00
Nicholas Tinsley
c2f5a6390e Initial raise hand support. 2023-12-19 11:12:57 -05:00
Greyson Parrelli
f2a7824168 Fix error message interaction on text-only bubbles. 2023-12-19 11:12:57 -05:00
Greyson Parrelli
3439861f74 Stop writes to the deprecated SVR2 enclave. 2023-12-19 11:12:57 -05:00
Alex Hart
06ee096746 Fix crash when launching 'turn off contact joined' option via activity. 2023-12-19 11:12:56 -05:00
Greyson Parrelli
6230a7553d Add some initial backupV2 network infrastructure. 2023-12-19 11:12:56 -05:00
Nicholas Tinsley
e17b07bb12 Bump version to 6.42.3 2023-12-18 22:57:23 -05:00
Nicholas Tinsley
ea2d4e9206 Updated baseline profile. 2023-12-18 22:53:07 -05:00
Nicholas Tinsley
2fffc86a5a Update translations and other static files. 2023-12-18 22:49:43 -05:00
Nicholas Tinsley
1bb0af72ee Fix static IP resolver for macOS. 2023-12-18 21:34:55 -05:00
Cody Henthorne
23a58ac064 Fix showing control bottom sheet on incoming rings. 2023-12-15 12:04:33 -05:00
Nicholas Tinsley
af9d16852e Fix speaker icon colors for "small". 2023-12-15 11:58:01 -05:00
Cody Henthorne
ee47c1ea10 Fix calling controll visibility bugs. 2023-12-15 11:41:47 -05:00
Cody Henthorne
2b318152fa Fix call buttons overflowing bottom sheet. 2023-12-14 10:29:53 -05:00
Greyson Parrelli
10a363248e Bump version to 6.42.2 2023-12-13 18:22:38 -05:00
Greyson Parrelli
11d4bde18a Update translations and other static files. 2023-12-13 18:21:49 -05:00
Greyson Parrelli
b88b992cb6 Fix reading from the deprecated SVR2 enclave during the reglock flow. 2023-12-13 18:12:50 -05:00
Cody Henthorne
6e3e1b56fb Fix toolbar showing incorrectly bug. 2023-12-13 11:15:22 -05:00
Cody Henthorne
7dfda2598d Fix audio level background showing during ringing. 2023-12-13 10:52:40 -05:00
Cody Henthorne
853862c475 Fix call info sheet scroll position after dismissing. 2023-12-13 10:24:53 -05:00
Cody Henthorne
5627bb6bed Fix switch camera tooltip showing incorrectly. 2023-12-13 10:17:28 -05:00
Cody Henthorne
b646e69b6b Fix self-PIP boundaries in calls. 2023-12-13 10:15:28 -05:00
Cody Henthorne
632aeed00b Bump version to 6.42.1 2023-12-08 12:00:38 -05:00
Cody Henthorne
3f5b4bad62 Update translations and other static files. 2023-12-08 11:46:29 -05:00
Cody Henthorne
7bba4ed820 Move switch camera button to self pip. 2023-12-08 11:40:02 -05:00
Nicholas Tinsley
e22ff1bbfe Preserve lobby audio device choice. 2023-12-08 11:40:02 -05:00
Nicholas Tinsley
ab66567db6 Instant Video design improvements. 2023-12-08 11:40:02 -05:00
Cody Henthorne
a763e1729c Update audio indicator for new designs. 2023-12-08 11:40:02 -05:00
Nicholas Tinsley
6aac250990 Send reactions. 2023-12-07 15:18:05 -05:00
Greyson Parrelli
a749b97707 Migrate to a new SVR2 enclave. 2023-12-07 15:14:44 -05:00
Greyson Parrelli
f966b23f3a Update libsignal-client to 0.36.1 2023-12-07 14:23:33 -05:00
Greyson Parrelli
763025d19b Disable notification thumbnails on some devices.
Relates to #13287
2023-12-07 13:56:30 -05:00
Cody Henthorne
0bf2ae6075 Fix various UI quirks with new calling controls.
- Change nav bar color
- Fix padding in info list
2023-12-07 10:41:12 -05:00
Cody Henthorne
71f947484e Bump version to 6.42.0 2023-12-06 17:11:11 -05:00
Cody Henthorne
5160164111 Updated baseline profile. 2023-12-06 16:54:43 -05:00
Cody Henthorne
7501e029ab Update translations and other static files. 2023-12-06 16:49:31 -05:00
Cody Henthorne
a678555d8d Receive calling reactions support and control ux refactor.
Co-authored-by: Nicholas <nicholas@signal.org>
2023-12-06 16:42:04 -05:00
Clark
7ce2991b0f Do not turn screen on automatically for calls. 2023-12-06 08:37:33 -05:00
Greyson Parrelli
befa396e82 Export backupV2 using actual desired file format. 2023-12-04 16:18:56 -05:00
Clark Chen
fb69fc5af2 Add backupV2 support for simple update messages. 2023-12-04 16:18:56 -05:00
Greyson Parrelli
b540b5813e Setup backupV2 infrastructure and testing.
Co-authored-by: Clark Chen <clark@signal.org>
2023-12-04 16:18:56 -05:00
Greyson Parrelli
feb74d90f6 Update libsignal-client to 0.35.0 2023-12-04 16:18:56 -05:00
Greyson Parrelli
a0de2577e8 Add extra data to the provisioning proto. 2023-12-04 16:18:56 -05:00
Greyson Parrelli
dbc5112ada Move send requirement calculations to a background thread. 2023-12-04 16:18:56 -05:00
Greyson Parrelli
9f8335810c Do not resort the chat list based on identity verification updates. 2023-12-04 16:18:56 -05:00
Greyson Parrelli
c54e2388ce Fix potential stack overflow during thread deletion. 2023-12-04 16:18:56 -05:00
Greyson Parrelli
a8a7019411 Fix marking crashes as prompted. 2023-12-04 16:18:56 -05:00
Greyson Parrelli
098da3c3dd Attempt to address a search crash. 2023-12-04 16:18:56 -05:00
Cody Henthorne
71b5645801 Do not show donate megaphone if currently awaiting a donation to clear. 2023-12-04 16:18:56 -05:00
Cody Henthorne
f5d9fbe91c Allow deeplinks back into Signal from iDEAL banking apps. 2023-12-04 16:18:56 -05:00
Clark
420e15c179 Fix infinite identity key storage service clash. 2023-12-04 16:18:56 -05:00
Greyson Parrelli
74619f6f8d Prevent nested SQL error handlers. 2023-12-04 16:18:56 -05:00
Greyson Parrelli
1355a4a28d Fix bug where username may be put in e164 column. 2023-12-04 16:18:56 -05:00
Greyson Parrelli
97c34b889a Update logging format. 2023-12-04 16:18:53 -05:00
Cody Henthorne
0b0c54d874 Perform client side checks on name and email for donation flows. 2023-12-04 16:18:53 -05:00
Jim Gustafson
1005be006f Update to RingRTC v2.34.5 2023-12-04 16:18:53 -05:00
Greyson Parrelli
8db113a19b Fix potential crash in username share sheet. 2023-12-04 16:18:53 -05:00
Greyson Parrelli
075df8a26d Fix crash if you search for a malformed username. 2023-12-04 16:18:53 -05:00
Greyson Parrelli
38cf3f40e1 Fix various places where we should show the username. 2023-12-04 16:18:53 -05:00
Greyson Parrelli
4a0abbbee7 Ensure ACI/PNI are associated after processing a PNI promotion message. 2023-12-04 16:18:53 -05:00
Greyson Parrelli
15f1201a76 Remove leftover deprecated gv1 code. 2023-12-04 16:18:53 -05:00
Nicholas Tinsley
b152723ed2 Restrict StreamingTranscoder usage to feature flag. 2023-12-04 16:18:53 -05:00
Clark
84a2832a65 Fix getAndPossiblyMerge to run after successful transaction in case of nested transactions. 2023-12-04 16:18:53 -05:00
Clark
8037494f7a Stop throwing an assertion error when getting attachment TransformationProperties. 2023-12-04 16:18:53 -05:00
Nicholas Tinsley
97c1ace020 Do not display stop icon on uncancelable progress. 2023-12-04 16:18:53 -05:00
Nicholas
64457b0235 Unique string resource for "edited now". 2023-12-04 16:18:53 -05:00
Nicholas
67ef831681 Only generate incremental mac for faststart videos. 2023-12-04 16:18:53 -05:00
Nicholas Tinsley
1fd6aae3d9 Make "Retry" text clickable when downloading attachment. 2023-12-04 16:18:53 -05:00
Clark
61810cc977 Re-use session objects during multi-recipient encryption. 2023-12-04 16:18:53 -05:00
Nicholas Tinsley
59401e18ed Prevent crash on audio focus permission denied.
Addresses #13283.
2023-12-04 16:18:53 -05:00
Nicholas Tinsley
30eff93fa1 Fix donation FAQ URL. 2023-12-04 16:18:53 -05:00
Greyson Parrelli
7c5bae3b53 Remove unnecessary jcenter repository. 2023-12-04 16:18:53 -05:00
Greyson Parrelli
ee16e4236e Convert the topmost build.gradle to .gradlew.kts. 2023-12-04 16:18:53 -05:00
Greyson Parrelli
30e9cf9dc8 Convert settings and dependencies to .gradle.kts. 2023-12-04 16:18:53 -05:00
Greyson Parrelli
ac5d0bf8a3 Convert main app build.gradle to .gradle.kts. 2023-12-04 16:18:45 -05:00
Greyson Parrelli
923eb05e59 Converted libsignal-service to .gradle.kts. 2023-12-04 16:18:11 -05:00
Greyson Parrelli
8f59e51445 Move test into proper directory. 2023-12-04 16:18:11 -05:00
Greyson Parrelli
766733617e Converted all minor modules to .gradle.kts. 2023-12-04 16:18:11 -05:00
Nicholas Tinsley
d77744c562 Additional logging around retry button. 2023-12-04 16:18:11 -05:00
Nicholas Tinsley
0d6db1305e Don't check recorded voice note size if discarding. 2023-12-04 16:18:11 -05:00
Nicholas
61c2e59f41 Only update profiles if their contents has changed. 2023-12-04 16:18:11 -05:00
Clark
47dd7adf4b Use libsignal to derive access key during group send. 2023-12-04 16:18:11 -05:00
Nicholas
016736c455 Encrypting for multiple senders benchmark. 2023-12-04 16:18:11 -05:00
Cody Henthorne
6d3924ba43 Add group call NOT_ACCEPTED sync handling. 2023-12-04 16:18:10 -05:00
Greyson Parrelli
428f963243 Remove unique constraint from dlist table. 2023-12-04 16:18:10 -05:00
Mridul Barman
dd871b64ea Remove duplicate permission filtering.
Closes #12987
2023-12-04 16:18:10 -05:00
Greyson Parrelli
38863f618a Fix back navigation in username link settings screen. 2023-12-04 16:18:10 -05:00
Greyson Parrelli
8023285b9d Only mark username corrupted after repeated failures. 2023-12-04 16:18:10 -05:00
Greyson Parrelli
1aa7175006 Update order for attachment menu options. 2023-12-04 16:18:10 -05:00
Cody Henthorne
1222c30738 Bump version to 6.41.3 2023-12-04 16:12:20 -05:00
Cody Henthorne
0c8e62add9 Update translations and other static files. 2023-12-04 16:07:13 -05:00
Cody Henthorne
eb1d06b4a6 Fix thumbnail info generation bug in notifications. 2023-12-04 16:01:43 -05:00
Greyson Parrelli
d58c3292d7 Only use apk uploadTimestamp for non-website builds.
Relates to #13273
2023-12-04 15:54:14 -05:00
Greyson Parrelli
4320d26a3d Do not read PNP FF in job. 2023-12-04 15:12:20 -05:00
Cody Henthorne
3ca4e33d94 Fix sepa badge redemption job. 2023-12-04 15:12:20 -05:00
Greyson Parrelli
19e726a630 Bump version to 6.41.2 2023-11-17 15:10:15 -05:00
Greyson Parrelli
96dddef271 Update translations and other static files. 2023-11-17 15:09:35 -05:00
Cody Henthorne
34a228f85e Remove GV1 migration support. 2023-11-17 14:25:47 -05:00
Greyson Parrelli
213d996168 Fix issues with some japanese numbers being detected as shortcodes. 2023-11-17 14:25:47 -05:00
Greyson Parrelli
5a159ce01f Update libphonenumber to 8.13.23 2023-11-17 14:25:47 -05:00
Cody Henthorne
fed9c64113 Fix false-positive CVC errors in credit card donation flow. 2023-11-17 14:25:47 -05:00
Nicholas Tinsley
2d835581a5 Set audio picker bottom sheet text color to onSurface. 2023-11-17 14:25:47 -05:00
Nicholas Tinsley
c8f1ebdf4c Fix speakerphone drawables for selection. 2023-11-17 14:25:47 -05:00
Greyson Parrelli
98e3530acd Bump version to 6.41.1 2023-11-16 17:12:19 -05:00
Greyson Parrelli
1a5b216dd5 Update translations and other static files. 2023-11-16 17:11:47 -05:00
Cody Henthorne
ae98d5e3bd Fix NPE in wifi direct connection establishment. 2023-11-16 16:37:38 -05:00
Greyson Parrelli
750825b3c3 Fix potential bug with the in-app updater. 2023-11-16 16:19:50 -05:00
Cody Henthorne
8c255256c9 Remove mms_config xmls. 2023-11-16 16:19:50 -05:00
Cody Henthorne
19626361ec Fix bug allowing creation of new and sending in existing MMS groups. 2023-11-16 16:19:50 -05:00
Cody Henthorne
df4bd1fa4a Replace monthly badge expires with cancellation dialogs. 2023-11-16 10:22:01 -05:00
Greyson Parrelli
62bf5abd8d Bump version to 6.41.0 2023-11-15 17:32:01 -05:00
Greyson Parrelli
cd9ec9f346 Update translations and other static files. 2023-11-15 17:30:04 -05:00
Greyson Parrelli
cf7d5b3481 Remove deprecated storage service fields. 2023-11-15 17:02:44 -05:00
Cody Henthorne
12f9ac3aa4 Use shorter string for tab for better localization. 2023-11-15 17:02:44 -05:00
Greyson Parrelli
4519cdb49c Remove some unnecessary transactions in MessageContentProcessor. 2023-11-15 17:02:28 -05:00
Jim Gustafson
d20b6f355c Enable opus low bitrate redundancy for internal testing. 2023-11-15 17:02:21 -05:00
Greyson Parrelli
70e64003f9 Unconditionally enable the PNI capability. 2023-11-15 17:02:21 -05:00
Nicholas Tinsley
0a4644e743 Update conversation shortcuts onPause. 2023-11-15 17:02:21 -05:00
Greyson Parrelli
c428d23d8b Install prompt notification should dismiss failures and vice-versa. 2023-11-15 17:02:21 -05:00
Greyson Parrelli
d6b189badc Fix potential binding crash. 2023-11-15 17:02:21 -05:00
Greyson Parrelli
6e899391c0 Add back the foreign key transaction dance. 2023-11-15 17:02:21 -05:00
Greyson Parrelli
e0acbcc32d Perform one database upgrade at a time, saving progress as we go. 2023-11-15 17:02:21 -05:00
Cody Henthorne
95fb9ea117 Remove old remote configs. 2023-11-15 17:02:21 -05:00
Greyson Parrelli
e80b7cf0a2 Store receipt fields as booleans instead of counts. 2023-11-15 17:02:21 -05:00
Cody Henthorne
5e70c06075 Rotate ideal and sepa flags. 2023-11-15 17:02:21 -05:00
Cody Henthorne
1413b74f76 Add 'Add remote donate megaphone' to internal settings. 2023-11-15 17:02:21 -05:00
Cody Henthorne
bf0548e802 Fix donation-based remote config region checks. 2023-11-15 17:02:21 -05:00
Clark
b7e1863526 Fix timezone weirdness with scheduled messages. 2023-11-15 17:02:21 -05:00
Cody Henthorne
f189188563 Fix snackbar colors on older api verisons. 2023-11-15 17:02:21 -05:00
Greyson Parrelli
2f52664820 Merge MediaMmsMessageRecord into MmsMessageRecord. 2023-11-15 17:02:21 -05:00
Greyson Parrelli
5f6fa73be9 Delete NotificationMmsMessageRecord. 2023-11-15 17:02:21 -05:00
Greyson Parrelli
b7ec913cb9 Improve receipt perf by caching the pending PNI signature table. 2023-11-15 17:02:21 -05:00
Greyson Parrelli
ebef4b079c Fix LRUCache to be ordered by access time. 2023-11-15 17:02:21 -05:00
Greyson Parrelli
a81e5c4e6b Improve receipt processing via faster thread updates. 2023-11-15 17:02:21 -05:00
Greyson Parrelli
b0733dcd51 Reduce transactions during getAndPossiblyMerge. 2023-11-15 17:02:21 -05:00
Greyson Parrelli
e9bd35619d Add migration to fix registration state of some users. 2023-11-11 13:34:48 -05:00
Greyson Parrelli
6528b34152 Fix username education layout when text is long. 2023-11-11 13:34:48 -05:00
Rashad Sookram
b60c02e0c7 Update to RingRTC v2.34.4 2023-11-11 13:34:48 -05:00
Greyson Parrelli
a0792d166b Add additional logging around apk updates. 2023-11-11 13:34:48 -05:00
Greyson Parrelli
fcf36c4bc0 Fix color of x in color picker. 2023-11-11 13:34:48 -05:00
Greyson Parrelli
e5b617cd16 Fix text color in username link sharing bottom sheet. 2023-11-11 13:34:48 -05:00
Greyson Parrelli
0acefb4521 Fix storage sync issues with usernames. 2023-11-11 13:34:48 -05:00
Greyson Parrelli
111c8367a9 Fix discoverability setting persistence during registration. 2023-11-11 13:34:48 -05:00
Greyson Parrelli
ead8f209b6 Fix 'next' button alignment during registration. 2023-11-11 13:34:48 -05:00
Greyson Parrelli
96333b616b Add username link share sheet. 2023-11-11 13:34:48 -05:00
Greyson Parrelli
5698e0deda Bump version to 6.40.4 2023-11-11 12:38:36 -05:00
Greyson Parrelli
df2ddebf6c Bump version to 6.40.3 2023-11-11 12:05:20 -05:00
Greyson Parrelli
71ab7528e7 Fix shared group membership check. 2023-11-11 12:04:55 -05:00
Cody Henthorne
b4e459d831 Bump version to 6.40.2 2023-11-10 15:44:59 -05:00
Cody Henthorne
a57d3fdf3f Updated baseline profile. 2023-11-10 15:39:20 -05:00
Cody Henthorne
2c207873be Update translations and other static files. 2023-11-10 15:34:43 -05:00
Cody Henthorne
fc8385113f Fix system ANR when loading avatars for system UI. 2023-11-10 15:27:57 -05:00
Cody Henthorne
95d7d26f11 Add SEPA max amount exceeded dialog. 2023-11-10 15:27:57 -05:00
AsamK
43a13964bd Fix leaking okhttp response in error case.
Closes #13246
2023-11-10 15:27:57 -05:00
Cody Henthorne
d2053d2db7 Bump version to 6.40.1 2023-11-09 16:52:42 -05:00
Cody Henthorne
8ba2bcaa53 Updated baseline profile. 2023-11-09 16:30:25 -05:00
Cody Henthorne
f7abdbe97f Update translations and other static files. 2023-11-09 16:27:36 -05:00
Greyson Parrelli
91af3e60ba Fix potential NPE when building an account record. 2023-11-09 16:13:46 -05:00
Clark
8fe196cd7a Don't renotify every single message on new message. 2023-11-09 12:29:59 -05:00
Cody Henthorne
66d7241c03 Update donation learn more urls in error states. 2023-11-09 12:06:27 -05:00
Cody Henthorne
89d7c0b0d0 Bump version to 6.40.0 2023-11-08 20:11:41 -05:00
Cody Henthorne
d2ec62d681 Updated baseline profile. 2023-11-08 20:02:59 -05:00
Cody Henthorne
b6d38fe8f1 Update translations and other static files. 2023-11-08 19:57:56 -05:00
Cody Henthorne
1edc256148 Rotate ideal and sepa flags. 2023-11-08 19:51:46 -05:00
Cody Henthorne
24ac385898 Fix dark theme issues with compose bottom sheets and donation bank name typo. 2023-11-08 19:51:46 -05:00
Cody Henthorne
f062e58f7b Flesh out monthly iDEAL donation flow. 2023-11-08 19:51:46 -05:00
Greyson Parrelli
96aec401b9 Fix username link settings navigation. 2023-11-08 19:51:46 -05:00
Nicholas Tinsley
7ff0b7aa3c Increase clickable area of media download button. 2023-11-08 19:51:46 -05:00
Greyson Parrelli
e5ab5241d5 Centralize username logic in UsernameRepository. 2023-11-08 19:51:46 -05:00
Greyson Parrelli
0f4f87067e Add some detailed username docs to UsernameRepository. 2023-11-08 19:51:46 -05:00
Greyson Parrelli
3f32f816b0 Convert the UsernameRepository to an object. 2023-11-08 19:51:46 -05:00
Greyson Parrelli
73de2dfda7 Fix opening username links. 2023-11-08 19:51:46 -05:00
Nicholas
d6fd6cb5a3 Optimize thread ID DB query. 2023-11-08 19:51:46 -05:00
Nicholas
39fbbe896f Batch insert group receipts. 2023-11-08 19:51:46 -05:00
Greyson Parrelli
29c70acf4e Leave attachment insert early if there are no attachments. 2023-11-08 19:51:46 -05:00
Greyson Parrelli
5cd2568776 Fix foreground service crash with state tracking. 2023-11-08 19:51:46 -05:00
Greyson Parrelli
60a6535a12 Add internal test buttons to corrupt username state. 2023-11-08 19:51:46 -05:00
Greyson Parrelli
f48b389449 Fix padding in edit profile screen. 2023-11-08 19:51:46 -05:00
Greyson Parrelli
316dd210a0 Minor improvements to username tooltip. 2023-11-07 22:11:08 -05:00
Greyson Parrelli
a60712c09d If both usernames hashes are empty, consider valid. 2023-11-07 14:44:46 -05:00
Nicholas Tinsley
482cd564ff Lower priority of ConversationShortcutUpdateJob. 2023-11-07 13:37:21 -05:00
Greyson Parrelli
ac1171d43b Allow install of nightlies with the same version code but newer upload dates. 2023-11-07 12:51:09 -05:00
Greyson Parrelli
ed8953c430 Fix logging around username link reset failures. 2023-11-07 12:11:22 -05:00
Cody Henthorne
9a8aecaf3f Improve donation strings localization. 2023-11-07 11:56:01 -05:00
Greyson Parrelli
423719e7bc Fix username QR code sharing. 2023-11-07 11:43:40 -05:00
Cody Henthorne
7f2b6a874e Flesh out iDEAL sad path UX and address UI polish feedback. 2023-11-07 11:04:36 -05:00
Greyson Parrelli
cfe5ea3f9b Add the ability to download the current perfetto trace in Spinner. 2023-11-07 09:07:59 -05:00
Greyson Parrelli
07aa058a46 Update username consistency error handling. 2023-11-06 14:49:51 -05:00
Nicholas Tinsley
6cadf93c43 Forward touch events in timestamp of text message. 2023-11-06 14:48:35 -05:00
Cody Henthorne
60eb1332d2 Fix lifespan typo for ExternalLaunchDonationJob. 2023-11-06 11:04:24 -05:00
Nicholas Tinsley
a9ee7e93fd Increase IdentityKey cache size. 2023-11-06 10:46:53 -05:00
Clark
2782216e52 Remove slow getResourceAsStream when loading the Conscrypt provider. 2023-11-06 09:56:11 -05:00
Nicholas Tinsley
d22537c5f2 Fix LocalMetrics for text sends. 2023-11-03 15:24:36 -04:00
Nicholas Tinsley
57aa6c19e1 Set silent group updates to low job priority. 2023-11-03 15:20:38 -04:00
Nicholas Tinsley
761553d392 Avoid unnecessary lock acquisition. 2023-11-03 15:12:29 -04:00
Greyson Parrelli
29350ab7b0 Add a QR code link and tooltip in the profile settings. 2023-11-03 14:33:07 -04:00
Cody Henthorne
528ccc1e9d Navigate to main donation screen if user leaves for external app. 2023-11-03 12:56:03 -04:00
Cody Henthorne
20d26ad7ca Expand spinner timestamp conversion to job tables. 2023-11-03 12:51:17 -04:00
Cody Henthorne
5d23c5c902 Increase sepa receipt request lifespan to cover at least 14 business days. 2023-11-03 12:49:19 -04:00
Greyson Parrelli
145794bf04 Add the ability to set job priority. 2023-11-03 12:21:27 -04:00
Greyson Parrelli
d00f2aa8d0 Convert EditProfileFragment to kotlin. 2023-11-03 10:40:13 -04:00
Greyson Parrelli
3a20375567 Update profile edit screen to remove subtitles. 2023-11-03 09:25:09 -04:00
Greyson Parrelli
7be93a8a44 Rename profile fragments so they make sense. 2023-11-03 09:14:17 -04:00
Jim Gustafson
b5e4c4e92a Update to RingRTC v2.34.3 2023-11-02 21:30:07 -04:00
Greyson Parrelli
20285796bd Fix username link sharing toolbar. 2023-11-02 19:19:00 -04:00
Greyson Parrelli
7826ff94e3 Also check PNI prekey age on message send. 2023-11-02 19:19:00 -04:00
Greyson Parrelli
f1dccbb64d Consider empty usernames as absent. 2023-11-02 19:19:00 -04:00
Greyson Parrelli
528e301ce4 Improve username creation error debouncing. 2023-11-02 19:19:00 -04:00
Greyson Parrelli
af016a9c79 Fix username error message text wrapping. 2023-11-02 19:19:00 -04:00
Greyson Parrelli
cbd5738543 Fix some username creation tinting issues in dark theme. 2023-11-02 19:19:00 -04:00
Greyson Parrelli
2dd0899a3d Fix nightly updates. 2023-11-02 19:19:00 -04:00
Greyson Parrelli
e486a4baef Bump version to 6.39.1 2023-11-02 19:18:37 -04:00
Greyson Parrelli
5fc11baf9e Update translations and other static files. 2023-11-02 19:18:37 -04:00
Nicholas
157777cac1 Batch update DB upon group receipt. 2023-11-02 19:18:37 -04:00
Greyson Parrelli
99d0ee6725 Fix cursor crash in ConversationSettings.
Best way to fix a cursor crash it to... stop using cursors.

Fairly confident the crash was caused by us closing the cursor while it
was read. And there just isn't a good way to avoid that with how it was
written. So this ended up being a great excuse to move over to models.
2023-11-02 11:58:23 -04:00
Greyson Parrelli
b5c1051506 Attempt to fix AccountRecord restore crash.
My guess is that we're seeing a crash when updating because we're using
an out-of-date recipient snapshot that has an old/invalid storageId.

This commit uses a fresher recipient, and it prefers using the raw
record (what's in the DB) instead.
2023-11-02 10:25:17 -04:00
Greyson Parrelli
bba3334df5 Bump version to 6.39.0 2023-11-01 20:45:16 -04:00
Greyson Parrelli
74488feec2 Update translations and other static files. 2023-11-01 20:45:16 -04:00
Greyson Parrelli
54953abc67 Reduce nightly update check interval to 2 hours. 2023-11-01 20:45:16 -04:00
Cody Henthorne
117bbdbcdf Show dialog when attempting to donate again while still processing previous donation. 2023-11-01 20:45:16 -04:00
Nicholas Tinsley
b96b99c1c4 Swallow touch events in forwarding sheet overlay.
Addresses #13239.
2023-11-01 20:45:16 -04:00
Cody Henthorne
6e856a7648 Update bank mandate CTA UX. 2023-11-01 20:45:16 -04:00
Greyson Parrelli
0659edb762 Add a new foreground service for attachment progress. 2023-11-01 20:45:16 -04:00
Greyson Parrelli
dcb870c432 Only show ACI SN's. 2023-11-01 20:45:16 -04:00
Greyson Parrelli
772bafbe43 Inline feature flag to show ACI SN by default. 2023-11-01 20:45:16 -04:00
Greyson Parrelli
a9be6aff44 Fix delete crash. 2023-11-01 20:45:16 -04:00
Cody Henthorne
dcd7ec7383 Treat pnp builds also as staging builds. 2023-11-01 20:45:16 -04:00
Greyson Parrelli
c69a4dda00 Convert GenericForegroundService to kotlin. 2023-11-01 20:45:16 -04:00
Greyson Parrelli
a911926119 Always for a full contact sync via ContactDiscovery.refreshAll(). 2023-11-01 20:45:15 -04:00
Greyson Parrelli
6f30aec4f2 Improve LocalMetrics logging. 2023-11-01 20:45:15 -04:00
Greyson Parrelli
5a005fb809 Build a simple ANR detector. 2023-11-01 20:45:15 -04:00
Cody Henthorne
776a4c5dce Fix string issues. 2023-10-31 10:19:34 -04:00
Jim Gustafson
c53c316303 Update to RingRTC v2.34.2 2023-10-31 09:50:07 -04:00
Greyson Parrelli
622aa844e4 Clear glide memory cache on attachment delete. 2023-10-31 09:50:07 -04:00
Greyson Parrelli
de2cf6026e Fix nightly build. 2023-10-30 18:09:17 -04:00
Greyson Parrelli
a8e02b9ced Move envelope follow-up operations outside of the transaction. 2023-10-30 18:09:17 -04:00
Nicholas Tinsley
297308ad76 Only suggest scheduled message times in the future.
Addresses #13139
2023-10-30 18:09:17 -04:00
Greyson Parrelli
ea0c3dbe5a Add logging around database transactions and group recipient creation. 2023-10-30 18:09:17 -04:00
Greyson Parrelli
b8d229e58e Enable auto-updates for nightly builds. 2023-10-30 18:09:17 -04:00
Greyson Parrelli
c4f5110148 Stop falling back to CDN0 for attachments. 2023-10-30 18:09:17 -04:00
Jim Gustafson
7fdd7e89bd Update to RingRTC v2.34.1 2023-10-30 18:09:17 -04:00
Greyson Parrelli
2378346537 Bump version to 6.38.2 2023-10-30 17:54:17 -04:00
Greyson Parrelli
72fc5fc3b1 Update translations and other static files. 2023-10-30 17:53:56 -04:00
Greyson Parrelli
c063c99ba6 Fix contact joined messages. 2023-10-30 17:44:25 -04:00
Nicholas Tinsley
90341f0a6e Finish updating audio output assets. 2023-10-30 11:48:13 -04:00
Nicholas Tinsley
cdb9df5aba Bump version to 6.38.1 2023-10-27 19:26:48 -04:00
Nicholas Tinsley
1f6d9d6422 Updated baseline profile. 2023-10-27 19:26:28 -04:00
Nicholas Tinsley
ffbda7e521 Update translations and other static files. 2023-10-27 19:23:15 -04:00
Nicholas Tinsley
3b5ef29047 Update IncomingMessage in benchmark. 2023-10-27 18:32:35 -04:00
Nicholas Tinsley
14cf6ceb84 Change audio output assets. 2023-10-26 11:59:20 -04:00
Nicholas Tinsley
5fb940ff2a Update speaker view hint's legibility. 2023-10-26 11:29:26 -04:00
Nicholas Tinsley
f446e18289 Require attachment data to be shown in "All" list. 2023-10-26 11:23:47 -04:00
Cody Henthorne
84f26b32d6 Fix snc causing thread reordering. 2023-10-26 10:43:44 -04:00
Nicholas Tinsley
f7690245aa Bump version to 6.38.0 2023-10-25 15:51:26 -04:00
Nicholas Tinsley
f44e32fd6a Update translations and other static files. 2023-10-25 15:50:48 -04:00
Nicholas Tinsley
8bac34238e Prevent crash on reaction animation end. 2023-10-25 15:44:13 -04:00
Nicholas Tinsley
6d2f6ce2f9 Hide safety verification in bottom sheet for null senders. 2023-10-25 15:44:13 -04:00
Alex Hart
3a465cc56b Account for horizontal padding when calculating available footer space. 2023-10-25 15:44:13 -04:00
Greyson Parrelli
617369dbc0 Make type a mandatory param on IncomingMessage. 2023-10-25 15:44:13 -04:00
Alex Hart
c0fed1498e Utilze visibility instead of isVisible for restoration of view visibility after long press. 2023-10-25 15:44:13 -04:00
Alex Hart
5bdd3ce47a Add background to sticky year header for donation receipts. 2023-10-25 14:30:23 -04:00
Greyson Parrelli
6b3f41d675 Merge IncomingTextMessages into IncomingMessage. 2023-10-25 14:30:23 -04:00
Alex Hart
23b696c9cf Rotate ideal and sepa flags. 2023-10-25 14:30:23 -04:00
Alex Hart
079400f89e Donation error sheet wiring and UI. 2023-10-25 14:30:23 -04:00
Alex Hart
e12d467627 Add ordering strategy for netherlands donation gateways. 2023-10-25 14:30:23 -04:00
Alex Hart
162ca3e21e Add locale based feature flags for iDEAL / SEPA donations. 2023-10-25 14:30:23 -04:00
Alex Hart
dddd0e7b71 Pipe in bank mandate parameter. 2023-10-25 14:30:23 -04:00
Cody Henthorne
95d68e09da Cycle hide contacts remote config. 2023-10-25 14:30:23 -04:00
Alex Hart
aaf0cf53d8 Remove number suffix of iban text as it is redundant. 2023-10-25 14:30:23 -04:00
Cody Henthorne
9c8f759732 Fix group call not ringing/notifying bug when starting a call. 2023-10-25 14:30:23 -04:00
Nicholas Tinsley
a45c685893 Increase logging during registration. 2023-10-25 14:30:23 -04:00
Jordan Rose
87bdebb21c Remove dependency on presentations being present in AddMemberAction. 2023-10-25 14:30:00 -04:00
Greyson Parrelli
4f754ae309 Centralize media message inserts. 2023-10-23 14:31:39 -04:00
Greyson Parrelli
4b004f70ec Update website build to use PackageInstaller. 2023-10-23 14:30:37 -04:00
Greyson Parrelli
d468d4c21b Remove sms/mms receive code.
Simplifying incoming message insert. Removing this dead path as part of
it.
2023-10-23 13:29:07 -04:00
Alex Hart
a4df433d80 Add proper endpoint for setting iDEAL default payment method. 2023-10-23 14:13:13 -03:00
Alex Hart
10eec025d2 Implement pending one-time donation error handling. 2023-10-23 13:50:54 -03:00
Alex Hart
d497ed4195 Handle launch to external bank application. 2023-10-23 09:26:31 -03:00
Alex Hart
e63137d293 Add bank icons and ideal logo. 2023-10-20 15:28:10 -04:00
Cody Henthorne
c744743913 Bump version to 6.37.2 2023-10-20 14:44:28 -04:00
Cody Henthorne
42493c8eb6 Updated baseline profile. 2023-10-20 14:34:45 -04:00
Cody Henthorne
391839028f Update translations and other static files. 2023-10-20 14:29:31 -04:00
Cody Henthorne
d9ecfeadc0 Add prompt to re-enable full screen intent notifications. 2023-10-20 14:22:08 -04:00
Greyson Parrelli
d866646f66 Update enum for phone number sharing mode. 2023-10-20 14:22:08 -04:00
Alex Hart
6295041341 Fix paypal one-time donation handling. 2023-10-20 14:22:08 -04:00
Alex Hart
8c7556427a Fix temporary screenshot security functionality. 2023-10-20 14:22:08 -04:00
Alex Hart
82c91db78c Fix SaveStateHandler viewModel delegate. 2023-10-20 11:26:37 -03:00
Alex Hart
2d969f4fff Reset scroll position to 0 on contact selection list commit. 2023-10-20 10:40:39 -03:00
Alex Hart
e84d46dae7 Add check for call link prefix before parsing. 2023-10-20 10:33:01 -03:00
Alex Hart
b6828b54ca Fix group calling update messages. 2023-10-20 10:17:31 -03:00
Cody Henthorne
f9bd1bac36 Revert "Upgrade eventbus to 3.3.1"
This reverts commit 89199b81ab.
2023-10-19 13:11:13 -04:00
Cody Henthorne
22e2bfacae Bump version to 6.37.1 2023-10-19 10:53:15 -04:00
Cody Henthorne
c446d4bb54 Fix crash in pni typing migration. 2023-10-19 10:39:08 -04:00
Cody Henthorne
23c7e5dc3f Bump version to 6.37.0 2023-10-18 17:08:26 -04:00
Cody Henthorne
661f1e624c Updated baseline profile. 2023-10-18 16:29:37 -04:00
Cody Henthorne
81ff5ef899 Update translations and other static files. 2023-10-18 16:23:23 -04:00
Cody Henthorne
e79364cb03 Fix pni decryption error. 2023-10-18 16:14:58 -04:00
Nicholas
d750e2fe7a Do not update media preview fragment state upon window transition. 2023-10-18 16:14:58 -04:00
Alex Hart
5e1025453a Implement beginnings of support for iDEAL payments. 2023-10-18 16:14:58 -04:00
Alex Hart
280da481ee Implement Stripe Failure Code support. 2023-10-18 16:14:58 -04:00
Jim Gustafson
9da5f47623 Update to RingRTC v2.34.0 2023-10-18 16:14:58 -04:00
Cody Henthorne
45f1f419e1 Add internal setting to log prekey ids. 2023-10-18 16:14:58 -04:00
Alex Hart
92f2ac67d5 Add proguard keep entry for org.signal.donations.json.** 2023-10-18 16:14:58 -04:00
Jordan Rose
d28a62d70b Improve signalwebp JNI. 2023-10-18 16:14:58 -04:00
Alex Hart
f9336f2a28 Rename DonationErrorSource value to MONTHLY. 2023-10-17 11:15:56 -04:00
Alex Hart
940e67b1ca Rename DonationErrorSource value to ONE_TIME and document. 2023-10-17 11:15:56 -04:00
Alex Hart
073e138ab2 Trim IBAN input before validating value. 2023-10-17 11:15:56 -04:00
Alex Hart
5aec4b4571 Remove alpha from pending badge states. 2023-10-17 11:15:56 -04:00
Alex Hart
f9cd3decb1 Fix several issues with proper pending state routing. 2023-10-17 11:15:56 -04:00
Alex Hart
627c47b155 Implement donations one-time pending state. 2023-10-17 11:15:56 -04:00
Greyson Parrelli
57135ea2c6 Add more logging to forwarding bottom sheet. 2023-10-17 11:15:56 -04:00
Greyson Parrelli
609e9fcdb0 Remove all unused KBS/SVR1 code. 2023-10-17 11:15:56 -04:00
Cody Henthorne
5b0e71b680 Fix dialog dismiss crash in debuglog prompt. 2023-10-17 11:15:56 -04:00
Cody Henthorne
9c2d478797 Skip sends to users with prekey failures. 2023-10-17 11:15:56 -04:00
Greyson Parrelli
c55fa13038 Add some new PNP merge tests. 2023-10-17 11:15:56 -04:00
Alex Hart
27b9565d2f Update TextInputLayout Style and Naming. 2023-10-17 11:15:56 -04:00
Greyson Parrelli
4fe6d79fff Unify our Base64 utilities. 2023-10-17 11:15:56 -04:00
Cody Henthorne
e636e38ba1 Fix NPE in contact attachment processing. 2023-10-17 11:15:56 -04:00
Alex Hart
ebc6665224 Implement small screen support for BankTransferMandateFragment. 2023-10-17 11:15:56 -04:00
Alex Hart
7001cedbc7 Add lifecycle aware temporary screenshot security component. 2023-10-17 11:15:56 -04:00
Alex Hart
b14209d5cf Add new styling for active subscription pref item. 2023-10-17 11:15:56 -04:00
Alex Hart
5150564fe2 Reduce donation configuration TTL to 1 hour. 2023-10-17 11:15:56 -04:00
Lakshay Bomotra
b7eaa9e353 Fix issue with new group members count. 2023-10-17 11:15:56 -04:00
Greyson Parrelli
c00943591d Remove PNP flag from reading some settings. 2023-10-17 11:15:56 -04:00
Cody Henthorne
1f9320200a Sync keys with linked devices. 2023-10-17 11:15:56 -04:00
Cody Henthorne
6a6b80cce2 Decrease db thrashing when starting expiration timers for messages. 2023-10-17 11:15:56 -04:00
Alex Hart
05296e3d9b Add proper text for pending sheet. 2023-10-17 11:15:56 -04:00
Alex Hart
7e68050e0a Add proper pending bank transfer urls. 2023-10-17 11:15:56 -04:00
Alex Hart
ab928be1b3 Suppress checking for messages on application foreground. 2023-10-17 11:15:56 -04:00
Alex Hart
65d26d753d Disable SEPA Debit for gifts. 2023-10-17 11:15:56 -04:00
Alex Hart
bf37c09ba0 Implement bank transfer completed sheet. 2023-10-17 11:15:56 -04:00
Grzegorz Bobryk
89199b81ab Upgrade eventbus to 3.3.1 2023-10-17 11:15:56 -04:00
Alex Hart
0dd17673f5 Implement bank transfer pending sheet. 2023-10-17 11:15:56 -04:00
Alex Hart
c17d6c2334 Implement gateway ordering. 2023-10-17 11:15:56 -04:00
Cody Henthorne
5285dd1665 Fix NPE in account record proto parsing. 2023-10-17 11:15:56 -04:00
Alex Hart
046ce30e08 Fix SGNL schema link for call links. 2023-10-17 11:15:56 -04:00
Alex Hart
1601fa5608 Update SEPA mandate acceptance parameters. 2023-10-17 11:15:56 -04:00
Alex Hart
5f7099184d Add new credit card and bank transfer glyphs. 2023-10-17 11:15:56 -04:00
Alex Hart
8425bb4f59 Update IBAN character limit in information string. 2023-10-17 11:15:56 -04:00
Bernie Dolan
e44006f531 Update MobileCoin SDK to 5.0.1 2023-10-17 11:15:56 -04:00
Alex Hart
3423e24de6 Add donation pending sheet for SEPA transfers. 2023-10-17 11:15:56 -04:00
Alex Hart
5ac363232f Implement isLongRunning wiring for receipt redemption jobs. 2023-10-17 11:15:56 -04:00
Greyson Parrelli
9cc020a2c7 Move the video lib to the proper directory. 2023-10-17 11:15:56 -04:00
Alex Hart
d2240f07d8 Add privacy and accounts sheets for SEPA. 2023-10-17 11:15:56 -04:00
Greyson Parrelli
4968db750b Move libsignal-service up a directory. 2023-10-17 11:15:55 -04:00
Alex Hart
6134244244 Update radii and margins of one-time-donation selection grid. 2023-10-17 11:15:55 -04:00
Cody Henthorne
4559ca9f2b Bump version to 6.36.5 2023-10-17 11:12:12 -04:00
Cody Henthorne
9a38920cb8 Updated baseline profile. 2023-10-17 11:02:21 -04:00
Cody Henthorne
2b771931e6 Update translations and other static files. 2023-10-17 10:57:35 -04:00
Cody Henthorne
d72e003f8c Fix delete account bug. 2023-10-17 10:33:30 -04:00
Cody Henthorne
097988e046 Bump version to 6.36.4 2023-10-16 12:58:21 -04:00
Cody Henthorne
4d15bc7ea0 Updated baseline profile. 2023-10-16 12:43:58 -04:00
Cody Henthorne
26f49e2877 Update translations and other static files. 2023-10-16 12:39:05 -04:00
Alex Hart
10aba86e70 Remove clear of chat color in onDestroy. 2023-10-16 10:54:57 -03:00
Alex Hart
9e3d100599 Bump version to 6.36.3 2023-10-13 14:37:13 -03:00
Alex Hart
a7193e321c Updated baseline profile. 2023-10-13 14:24:26 -03:00
Alex Hart
fa15469696 Update translations and other static files. 2023-10-13 14:19:43 -03:00
Cody Henthorne
58b9cdf28f Fix deadlock in JobManager initialization. 2023-10-13 13:02:03 -04:00
Nicholas
8e05fe3b0c Rotate incremental MAC proto field. 2023-10-13 11:43:42 -04:00
Nicholas
af063b2e9e Transfer Control View Improvements. 2023-10-13 10:03:42 -04:00
Alex Hart
5cc85cc860 Fix issue with chat colors not updating properly. 2023-10-13 10:37:09 -03:00
Nicholas Tinsley
eafa1eabee Adjust transfer control view insets. 2023-10-11 16:01:55 -04:00
Nicholas Tinsley
34a1838668 Make blurred thumbnails fill the view. 2023-10-11 16:00:15 -04:00
Alex Hart
df83c94180 Bump version to 6.36.2 2023-10-11 16:28:39 -03:00
Alex Hart
e102b60923 Updated baseline profile. 2023-10-11 16:24:06 -03:00
Alex Hart
02900eaa6d Update translations and other static files. 2023-10-11 16:18:23 -03:00
Nicholas Tinsley
5ed4c51582 Do not check incremental MAC in Glide. 2023-10-11 16:11:30 -03:00
Nicholas Tinsley
81e928f94e Disable incremental MAC changes. 2023-10-11 14:31:06 -04:00
Alex Hart
985b569d29 Fix wacky layout while scrolling in thread. 2023-10-11 14:52:30 -03:00
Nicholas Tinsley
d2d000ef16 Log device type when failing to set audio device. 2023-10-11 10:25:16 -04:00
Alex Hart
520b3a14bc Handle donation-driven 440 errors more gracefully. 2023-10-11 09:56:09 -03:00
Nicholas Tinsley
157d194cc5 Fix downloading outgoing media view. 2023-10-10 17:52:54 -04:00
Cody Henthorne
2785609481 Fix bug with dangling notification clear. 2023-10-10 12:06:15 -04:00
Nicholas Tinsley
6e5e60173b Bump version to 6.36.1 2023-10-06 19:30:50 -04:00
Nicholas Tinsley
f37e938f17 Update translations and other static files. 2023-10-06 19:30:39 -04:00
Nicholas Tinsley
da645acd1c Updated baseline profile. 2023-10-06 19:22:31 -04:00
Nicholas Tinsley
17205b2baf Remove vestigial relayout calls. 2023-10-06 18:24:58 -04:00
Greyson Parrelli
b5ba4d3570 Fix progress text wrapping in TransferControlView. 2023-10-06 17:05:40 -04:00
Alex Hart
17b24d3c24 Add handling for no-bubble outgoing messages without wallpaper. 2023-10-06 16:13:18 -03:00
Alex Hart
044454dca2 Fix story start position when in a mixed read/unread state. 2023-10-06 10:32:47 -03:00
Nicholas Tinsley
88bff9ab6c Bump version to 6.36.0 2023-10-05 19:23:24 -04:00
Nicholas Tinsley
203fde60d6 Update translations and other static files. 2023-10-05 19:23:01 -04:00
Nicholas
82956c4149 New attachment download UI. 2023-10-05 19:13:19 -04:00
Greyson Parrelli
1f41b9e481 Include microbenchmark compilation check in qa. 2023-10-05 19:13:19 -04:00
Greyson Parrelli
945921fa9a Fix compilation of microbenchmarks. 2023-10-05 19:13:19 -04:00
Greyson Parrelli
7d5786ea93 Add a core-util-jvm module.
This is basically a location where we can put common utils that can also
be imported by libsignal-service (which is java-only, no android
dependency).
2023-10-05 19:13:19 -04:00
Cody Henthorne
6be1413d7d Fix link preview overriding edit message with media bug. 2023-10-05 19:13:19 -04:00
Cody Henthorne
fd07ab10ee Fix ISE crash in compose text watcher. 2023-10-05 19:13:19 -04:00
Cody Henthorne
6232656ad4 Fix dangling notifications after clear message history. 2023-10-05 19:13:19 -04:00
Cody Henthorne
8493c7ffe5 Enable split-window support for key activites.
Fixes #13182
2023-10-05 19:13:19 -04:00
Alex Hart
15700b85cb Implement underpinnings of SEPA debit transfer support for donations. 2023-10-05 19:13:19 -04:00
Cody Henthorne
3dfd1c98ba Re-download profile avatars if they fail to load. 2023-10-04 15:00:52 -04:00
Nicholas Tinsley
9a249b0dec Make voice note playback log statement more readable. 2023-10-04 10:32:23 -04:00
Cody Henthorne
b74a431ac9 Prevent incorrect state changes during vanity camera switchover. 2023-10-03 11:27:33 -04:00
Cody Henthorne
880ce18fd0 Pluralize chat length limit custom setting. 2023-10-03 10:16:05 -04:00
Alex Hart
6279149cb8 Add SEPA API endpoints. 2023-10-03 10:00:42 -04:00
Alex Hart
f5c5a34798 CallLink profile sharing via ProfileKeySendJob. 2023-10-03 10:00:42 -04:00
Alex Hart
e9a616c68d Add error handling for PayPal decline codes. 2023-10-03 10:00:42 -04:00
Nicholas Tinsley
f5ee7160cb Bump version to 6.35.3 2023-10-02 21:33:58 -04:00
Nicholas Tinsley
cea671aab5 Update translations and other static files. 2023-10-02 21:21:18 -04:00
Nicholas
da84cde6da Read first frame of backup to validate before proceeding.
Addresses #11952.
2023-10-02 20:30:39 -04:00
Cody Henthorne
e9fbce4e28 Add missing GV2 state update on conversation open. 2023-10-02 14:49:52 -04:00
Alex Hart
913605a065 Fix state snapshot in LinkPreviewViewModelV2. 2023-10-02 13:59:07 -03:00
Alex Hart
4bf49df6fa Fix horizontal ReactionView margins. 2023-10-02 13:53:24 -03:00
Cody Henthorne
91a9d6c68f Fix NPE in group access control. 2023-10-02 12:01:56 -04:00
Cody Henthorne
a477c3c4d9 Fix incorrect assertion for syncing pni only contacts. 2023-10-02 12:00:15 -04:00
Cody Henthorne
0cdd56e0ac Bump version to 6.35.2 2023-09-30 09:24:52 -04:00
Cody Henthorne
abefb894cc Updated baseline profile. 2023-09-30 09:23:24 -04:00
Cody Henthorne
97d482c1ad Update translations and other static files. 2023-09-30 09:20:50 -04:00
Cody Henthorne
d3e9303d6d Fix incorrect data migration. 2023-09-30 09:15:37 -04:00
Cody Henthorne
df7bb13752 Bump version to 6.35.1 2023-09-29 20:27:48 -04:00
Cody Henthorne
d28f6f5922 Updated baseline profile. 2023-09-29 20:23:45 -04:00
Cody Henthorne
c90ad7c1e2 Fix bugs around PNI only contacts and storage service. 2023-09-29 20:15:34 -04:00
Alex Hart
7fbdcb8a88 Add several SavedStateHandle delegates. 2023-09-29 11:28:36 -03:00
Alex Hart
d46daed49a Add SavedStateHandle support to LinkPreviewViewModelV2. 2023-09-29 09:25:17 -03:00
Nicholas Tinsley
f18a03ee6d Add incremental mac chunk size to attachment pointer. 2023-09-28 21:12:05 -04:00
Cody Henthorne
1d052e7c1b Bump version to 6.35.0 2023-09-28 20:10:05 -04:00
Cody Henthorne
2611165f21 Updated baseline profile. 2023-09-28 20:05:46 -04:00
Cody Henthorne
f059aa7407 Update translations and other static files. 2023-09-28 20:01:19 -04:00
Jim Gustafson
ac27df1f0e Update to RingRTC v2.33.0 2023-09-28 19:57:33 -04:00
Alex Hart
76b28593ea Suppress dialog if error is regarding user cancellation. 2023-09-28 19:57:33 -04:00
Alex Hart
0940c88c20 CallLink NullMessage sending. 2023-09-28 19:57:33 -04:00
Clark Chen
c3408040fc Skip optimized notifications check if flag disabled. 2023-09-28 19:57:33 -04:00
Alex Hart
d2ffc11749 Allow MediaStore permission check to function with only images enabled. 2023-09-28 19:57:32 -04:00
Alex Hart
4d640ec467 Donation CreatePaymentMethod 409 error recovery. 2023-09-28 19:57:32 -04:00
Alex Hart
c409d49f14 Hide call link warning card when entering call. 2023-09-28 19:57:32 -04:00
Nicholas Tinsley
2c0dbf1062 Condense BubbleUtil debug info to a single line. 2023-09-28 19:57:32 -04:00
Alex Hart
25f0208e61 Upgrade AndroidX Core to 1.12.0 2023-09-28 19:57:32 -04:00
Nicholas
d063cfe36a Upgrade libsignal to 0.32.1 2023-09-28 19:57:32 -04:00
Cody Henthorne
5c089e1d77 Fix crash on poorly formatted group change update. 2023-09-28 19:44:46 -04:00
Nicholas Tinsley
867006d29c Increase Bubble diagnostic logging. 2023-09-28 19:44:46 -04:00
Greyson Parrelli
6a974c48ef Add a log viewer to Spinner.
This is more of a proof-of-concept/demo for using a websocket with
Spinner. Gives an example of how we could push live updates to the
webapp.

Also, the logger is actually nice. Guaranteed to never get cluttered
with system logs. Looks basically identical to our other log viewers.
Filtering is basic but fast. And we could build much better tooling on
top of this.
2023-09-28 19:44:46 -04:00
Jim Gustafson
c314918c6b Update to RingRTC v2.32.0 2023-09-28 19:44:46 -04:00
Greyson Parrelli
e2e2a076c7 Fix error log in Spinner console. 2023-09-28 19:44:46 -04:00
Greyson Parrelli
8ee12b9f26 Fix compile issue with some sample apps. 2023-09-28 19:44:46 -04:00
Cody Henthorne
7377293f81 Bump version to 6.34.5 2023-09-28 19:43:18 -04:00
Cody Henthorne
29ae49b5f1 Updated baseline profile. 2023-09-28 19:40:19 -04:00
Cody Henthorne
195d967b3f Update translations and other static files. 2023-09-28 19:37:34 -04:00
Cody Henthorne
eac74bf9c1 Fix NPE crash in group permissions screen. 2023-09-28 19:32:40 -04:00
Alex Hart
9f2dbf7b6c Fix context usage in ConversationDataSource. 2023-09-28 19:24:12 -04:00
Cody Henthorne
9e836ba586 Bump version to 6.34.4 2023-09-26 20:03:14 -04:00
Cody Henthorne
cc6dc1b3a2 Updated baseline profile. 2023-09-26 19:57:24 -04:00
Cody Henthorne
f49da2c9bf Update translations and other static files. 2023-09-26 19:52:35 -04:00
Cody Henthorne
96c1077238 Revert "Add more logging to forwarding bottom sheet."
This reverts commit 3fc26733ad.
2023-09-26 19:43:48 -04:00
Cody Henthorne
8d72b27e1d Fix gboard gif playback. 2023-09-26 19:41:49 -04:00
Cody Henthorne
0ea0d139dd Fix odd scaling issues during decoding. 2023-09-26 19:34:55 -04:00
Nicholas Tinsley
b81ff4d672 Increase prominence of network errors during re-registration. 2023-09-26 10:43:57 -04:00
Alex Hart
f380ac5e43 Fix username search issue for non-alpha-underscore characters. 2023-09-26 10:05:38 -03:00
Alex Hart
962d42292d Remove deprecated API endpoint call for setting the default payment method. 2023-09-26 09:11:42 -03:00
Alex Hart
15df15556d Always display footer underneath if text has mixed directions. 2023-09-26 09:08:22 -03:00
Cody Henthorne
6b29841cc8 Bump version to 6.34.3 2023-09-25 21:42:21 -04:00
Cody Henthorne
4f4c1a9bb8 Updated baseline profile. 2023-09-25 21:36:27 -04:00
Cody Henthorne
5f7630b906 Update translations and other static files. 2023-09-25 21:31:44 -04:00
Cody Henthorne
8a831889f9 Decode using aspect ratio preserving scaling. 2023-09-25 21:25:00 -04:00
Nicholas Tinsley
bce133ac28 Add more logging around missing RecipientId. 2023-09-25 21:25:00 -04:00
Alex Hart
f5215d715a Utilize timestamp from table instead of relying on getCurrentMillis. 2023-09-25 21:25:00 -04:00
Alex Hart
fde0f3bba1 Fix call log clear history error handling. 2023-09-25 21:25:00 -04:00
Alex Hart
e7b18bd3a2 Tie CallLogViewModel lifecycle to the activity. 2023-09-25 21:25:00 -04:00
Alex Hart
e5e86e639a Update getAdapterPosition to utilize the binding adapter position instead of absolute. 2023-09-25 21:25:00 -04:00
Alex Hart
f44b44a354 Fix timestamp update on conversation re-entry from background. 2023-09-25 21:25:00 -04:00
Alex Hart
b3399b5242 Fix RTL display of CIV2 bubble corners. 2023-09-25 21:25:00 -04:00
Alex Hart
7d4ebd9d3b Fix strange padding on some CIV2 items. 2023-09-25 10:16:03 -03:00
Cody Henthorne
3bb2131375 Fix crash when prompting for debuglogs. 2023-09-24 21:29:01 -04:00
Cody Henthorne
d7314ec2a4 Fix NPE in group change message processing. 2023-09-24 21:20:54 -04:00
Cody Henthorne
cc6c724ee8 Fix crash if pixels are null. 2023-09-24 20:57:24 -04:00
Cody Henthorne
d3b0559b72 Fix link preview processing when missing a date. 2023-09-23 23:06:50 -04:00
Cody Henthorne
1e24caec31 Fix SignalServiceGroupV2 proto parsing. 2023-09-23 23:00:36 -04:00
Cody Henthorne
65cdc143da Fix incorrect handling of hangup message. 2023-09-23 22:40:46 -04:00
Cody Henthorne
5d612f020c Bump version to 6.34.2 2023-09-22 16:19:23 -04:00
Cody Henthorne
ccef2cc178 Use https for submodule. 2023-09-22 16:16:21 -04:00
Alex Hart
9337160583 Bump version to 6.34.1 2023-09-22 17:02:41 -03:00
Alex Hart
bf9d570c3d Updated baseline profile. 2023-09-22 17:02:20 -03:00
Alex Hart
306b0096be Update translations and other static files. 2023-09-22 16:56:56 -03:00
Alex Hart
45583ea469 Revert "Instant Video Playback UI"
This reverts commit f8283acfae.
2023-09-22 16:50:20 -03:00
Cody Henthorne
15c6c372ba Fix quoted mentioned showing in regular message bug. 2023-09-22 16:50:20 -03:00
Ehren Kret
770a89507a Fix background color on internal search fragment. 2023-09-22 16:50:20 -03:00
Alex Hart
ddc9aa7506 Remove unused padding in ContactSelectionListFragment. 2023-09-22 16:50:20 -03:00
Cody Henthorne
a7d9fd19d9 Enable WebP decoding in Signal using libwebp v1.3.2
Co-authored-by: Greyson Parrelli <greyson@signal.org>
Co-authored-by: Greyson Parrelli <greyson@pop-os.localdomain>
2023-09-22 16:50:20 -03:00
Alex Hart
091f7c49ab Fix issue where story contact list would reset when selecting contacts.
Fixes #13174
2023-09-22 16:50:20 -03:00
Cody Henthorne
b443f59078 Rebuild wire-handler-1.0.0.jar without extra logging. 2023-09-22 16:50:20 -03:00
Clark Chen
27bcf92e9b Update remote delete send threshold. 2023-09-22 16:50:20 -03:00
Alex Hart
31100c3d82 Fix bug causing WifiDirect transfers to not initialize.
Fixes #13173
2023-09-22 16:50:20 -03:00
Alex Hart
119da2e76e Fix crash in welcome fragment click handling. 2023-09-22 16:50:20 -03:00
Greyson Parrelli
588a6cf74f Remove PNP flag checks in some areas. 2023-09-22 16:50:20 -03:00
Greyson Parrelli
eb6394eb6a Fix SSE event bug and make the assertion guarded by a separate flag. 2023-09-21 15:56:03 -04:00
Alex Hart
76de183ec2 Bump version to 6.34.0 2023-09-21 16:29:16 -03:00
Alex Hart
ba31ceb3e7 Updated baseline profile. 2023-09-21 16:24:23 -03:00
Alex Hart
e94e0f8a6b Update translations and other static files. 2023-09-21 16:19:36 -03:00
Nicholas
f8283acfae Instant Video Playback UI 2023-09-21 15:12:11 -04:00
Alex Hart
f8cb26ca74 Replace TypingIndicatorItemDecoration with TypingIndicatorAdapter. 2023-09-21 14:05:49 -03:00
Alex Hart
190b9da6c7 Fix icon alignment in CIV2 footer. 2023-09-21 13:59:52 -03:00
Alex Hart
f84b46148c Show delivery status in forced footers for CIV2. 2023-09-21 13:59:52 -03:00
Alex Hart
12db8b5ee1 Fix swipe to reply positioning in CIV2. 2023-09-21 13:59:52 -03:00
Alex Hart
05b5078aa9 Hide footer end pad in CIV2 non-end items. 2023-09-21 13:59:52 -03:00
Alex Hart
85b7ee85f3 Display date in forced footer for CIV2. 2023-09-21 13:59:52 -03:00
Alex Hart
326b728d4b Always show expiration timer if there is one. 2023-09-21 13:59:52 -03:00
Alex Hart
2e45e131b1 Fix tinting of CIV2 expiration icon. 2023-09-21 13:59:52 -03:00
Alex Hart
1aa95c057b Fix edit message label. 2023-09-21 13:59:52 -03:00
Alex Hart
6de7a849b3 Increment CIV2 feature flag. 2023-09-21 13:59:52 -03:00
Nicholas
268091b10e Close media preview upon remote delete. 2023-09-21 13:59:52 -03:00
Alex Hart
3920c85ab7 Increment edit message feature flag. 2023-09-21 13:59:52 -03:00
Alex Hart
524565f0bb Add animations for add name fragment in call links. 2023-09-21 13:59:52 -03:00
Nicholas Tinsley
69c1c856d9 Prevent crash from toolbar subtitle in call view. 2023-09-21 13:59:52 -03:00
Nicholas Tinsley
dd62d92ffb Don't stop playback on seek. 2023-09-21 13:59:52 -03:00
Nicholas Tinsley
f7e89d75a4 Deduplicate audio devices by name. 2023-09-21 13:59:52 -03:00
Nicholas Tinsley
023f31eadd Set recipients name in safety number verification screen.
Addresses #13171.
2023-09-21 13:59:52 -03:00
Alex Hart
da8df5beac Avoid triggering requestLayout during measure pass for setBubbleWidth. 2023-09-21 13:59:52 -03:00
Alex Hart
f3a8825cb9 Revert "Add proper tinting to delivery status icon."
This reverts commit c4ac63ea7a89e44f478b0321901eaf43e2745502.
2023-09-21 13:59:52 -03:00
Cody Henthorne
835fd47482 Fix crashes related to activity starts. 2023-09-21 13:59:52 -03:00
Cody Henthorne
efbd5cab85 Convert SignalService, Database, Group, Payment, and other remaining protos to wire. 2023-09-21 13:59:52 -03:00
Alex Hart
a6b7d0bcc5 Set outgoing download tint to onCustom. 2023-09-21 13:59:52 -03:00
Alex Hart
e06126d889 Fix pulse on quote press. 2023-09-21 13:59:51 -03:00
Alex Hart
4bf8e2c488 Fix auto-update timestamps. 2023-09-21 13:59:51 -03:00
Alex Hart
1c55ad21a3 Add background to group sender name in CIV2. 2023-09-21 13:59:51 -03:00
Alex Hart
3a601e1e65 Rename binding fields for CIV2. 2023-09-21 13:59:51 -03:00
Alex Hart
c953003c2f Fix footer background sizing. 2023-09-21 13:59:51 -03:00
Alex Hart
18de51a531 Add proper tinting to delivery status icon. 2023-09-21 13:59:51 -03:00
Alex Hart
ab6d3b5e8d Set bubble width in onMeasure. 2023-09-21 13:59:51 -03:00
Alex Hart
151980c6de Bump version to 6.33.3 2023-09-21 13:51:58 -03:00
Alex Hart
375527b765 Updated baseline profile. 2023-09-21 13:42:50 -03:00
Alex Hart
2978e567d4 Update translations and other static files. 2023-09-21 13:37:48 -03:00
Alex Hart
8ad50ab61c Check for database initialisation in AvatarProvider#openFile. 2023-09-21 13:13:52 -03:00
Cody Henthorne
2145ded2f2 Improve network reliability. 2023-09-21 12:10:27 -04:00
Clark Chen
29c4d9f4d6 Bump version to 6.33.2 2023-09-18 14:09:36 -04:00
Clark Chen
c7de3d299a Update translations and other static files. 2023-09-18 13:57:13 -04:00
Greyson Parrelli
8bad476315 Fix backoff interval scheduling for jobs. 2023-09-18 12:06:42 -04:00
Clark Chen
bc8eb44a53 Bump version to 6.33.1 2023-09-15 15:15:43 -04:00
Clark Chen
f98e22cb76 Update translations and other static files. 2023-09-15 15:03:38 -04:00
Cody Henthorne
5b6326e462 Fix thread body ellipsizing in Conversation List. 2023-09-15 14:20:22 -04:00
Greyson Parrelli
342f249fab Fix possible crash during JobDatabase upgrade.
This seems to be a SQLite/SQLCipher caching issue.

Fixes #13172
2023-09-14 17:02:52 -04:00
Greyson Parrelli
09ba6d834a Ensure signed prekeys are rotated even if someone turns their clock back. 2023-09-14 13:20:55 -04:00
Alex Hart
61654f815d Bump version to 6.33.0 2023-09-13 14:25:11 -03:00
Alex Hart
bf450766b2 Updated baseline profile. 2023-09-13 13:40:12 -03:00
Alex Hart
2f813f3d91 Update translations and other static files. 2023-09-13 13:35:45 -03:00
Clark
51e46db42d Fail websocket drain if keepalive token is removed. 2023-09-13 13:28:43 -03:00
Cody Henthorne
11e0dd18d3 Remove use of legacy hangup in sending flow. 2023-09-13 13:28:43 -03:00
Nicholas Tinsley
ff5b024074 Fix crash when closing app during voice memo. 2023-09-13 13:28:43 -03:00
Nicholas
2f53c1a860 Display error when initial attachment selection is too large. 2023-09-13 13:28:43 -03:00
Alex Hart
53b1544b58 Fix bad feeling on-back behavior for in-conversation search. 2023-09-13 13:28:43 -03:00
Alex Hart
846fc9008c Fix issue on lower API devices with insets.
Fixes #13156
2023-09-13 13:28:43 -03:00
Alex Hart
cf7455c661 Do not display clear filter when no filter is applied in call log. 2023-09-12 10:58:39 -03:00
Alex Hart
ea52bbea42 Fix footer padding for very narrow bodies. 2023-09-12 10:48:00 -03:00
Alex Hart
712c41d927 Remove busy log line from call link processing. 2023-09-12 10:31:44 -03:00
Greyson Parrelli
c33da4a5ae Do not block CDS if PIN creation failed. 2023-09-12 09:28:13 -04:00
Alex Hart
10aecb9390 Order call links in reverse chron on calls tab. 2023-09-12 10:16:49 -03:00
Alex Hart
a1eafe311e Set correct color on join button in SignalCallRow. 2023-09-12 10:13:46 -03:00
Alex Hart
df416be43e Rotate CIV2 flag. 2023-09-12 10:01:10 -03:00
Alex Hart
08035bf8a5 Rotate edit message flag. 2023-09-12 10:00:25 -03:00
Alex Hart
05a990e228 Add CIV2 callback for clicking an edited message footer. 2023-09-12 10:00:02 -03:00
Nicholas Tinsley
4d7d1699f9 Simplify VoiceNote playback logic. 2023-09-11 17:12:26 -04:00
Alex Hart
92b0ebb6f6 Call Link single-user request sheet. 2023-09-11 17:07:12 -03:00
Greyson Parrelli
e41accf52d Fix bug where name wouldn't refresh when getting a message request. 2023-09-11 17:07:12 -03:00
Greyson Parrelli
1cca60fa53 Include decryption time in success log. 2023-09-11 17:07:12 -03:00
Alex Hart
69f489ffc5 Fix reactions position when parent isn't re-laid-out. 2023-09-11 17:07:12 -03:00
Alex Hart
903e305519 Drop verbose logs from ConversationItemAnimator. 2023-09-11 17:07:12 -03:00
Cody Henthorne
9ed3e8befb Fix infinite spinner on scheduled link preview bug. 2023-09-11 17:07:12 -03:00
Greyson Parrelli
cd38c99f7e Reduce websocket timeout if we have no network. 2023-09-11 17:07:12 -03:00
Greyson Parrelli
3fc26733ad Add more logging to forwarding bottom sheet. 2023-09-11 17:07:12 -03:00
Greyson Parrelli
e24134ff6f Reduce AvatarProvider logging. 2023-09-11 17:07:12 -03:00
Greyson Parrelli
901063f4c9 Improve third-party license display. 2023-09-11 17:07:12 -03:00
Cody Henthorne
be8742f69e Add promote PNI change profile keys to change processing. 2023-09-11 17:07:12 -03:00
Alex Hart
dbd6b4bd52 Update recipient after related call link has a state change. 2023-09-11 17:07:12 -03:00
Alex Hart
8a39e8094c Force CallLinkInfoSheet to always be dark mode. 2023-09-11 17:07:12 -03:00
Alex Hart
a154a6cce5 Add 'edit' state for call name button. 2023-09-11 17:07:12 -03:00
Alex Hart
052ec14a6b Rotate ad-hoc calling flag. 2023-09-11 17:07:12 -03:00
Alex Hart
fa9034d57b Add logic for handling disconnect reason while connected to call link. 2023-09-11 17:07:12 -03:00
Alex Hart
266adf788c Add support for received call link previews. 2023-09-11 17:07:12 -03:00
Greyson Parrelli
b19aedd17c Upgrade CameraX to 1.3.0-rc01 2023-09-11 17:07:12 -03:00
Greyson Parrelli
f959543c19 Add UI for prompting about crashes. 2023-09-11 17:07:11 -03:00
Alex Hart
0a6c3baf24 Bump version to 6.32.4 2023-09-11 17:03:34 -03:00
Alex Hart
5a33c1eed6 Updated baseline profile. 2023-09-11 16:59:24 -03:00
Alex Hart
ce1196e17a Updated language translations. 2023-09-11 16:56:56 -03:00
Nicholas Tinsley
a9fd5a3162 Fix DB query for incremental media. 2023-09-11 14:56:49 -04:00
Alex Hart
18b33a7776 Ensure lower api levels do not try to use Uri based IconCompat. 2023-09-11 15:27:58 -03:00
Cody Henthorne
b72fe0d7a2 Fix unread mention indicator showing incorrectly bug. 2023-09-11 13:53:42 -04:00
Nicholas Tinsley
551e5a0a25 Bump version to 6.32.3 2023-09-09 13:08:03 -04:00
Nicholas Tinsley
92d4a580c1 Updated language translations. 2023-09-09 13:07:48 -04:00
Greyson Parrelli
b367701a96 Fix bug where default reactions were dropped in AccountRecord. 2023-09-08 19:42:04 -04:00
Nicholas Tinsley
8595863afe Bump version to 6.32.2 2023-09-08 17:54:55 -04:00
Nicholas Tinsley
21492ed88e Updated language translations. 2023-09-08 17:54:36 -04:00
Nicholas Tinsley
4dc14ab7f9 Fix translation tool postprocessing bug. 2023-09-08 17:50:50 -04:00
Nicholas Tinsley
5caf3409db Match incremental MAC calculation. 2023-09-08 17:50:50 -04:00
Greyson Parrelli
1565c32162 Fix crash when opening license screen. 2023-09-07 13:51:55 -04:00
Nicholas Tinsley
45edb4e5da Bump version to 6.32.1 2023-09-07 11:06:31 -04:00
Nicholas Tinsley
5bf1c4f433 Updated baseline profile. 2023-09-07 11:04:46 -04:00
Nicholas Tinsley
3cc692d3fb Rotate the edit message feature flag. 2023-09-07 10:28:54 -04:00
Alex Hart
e42b2490f0 Rotate flag for civ2-text-only views. 2023-09-07 11:27:34 -03:00
Alex Hart
454b1f69ed Suppress LayoutTransition during scroll events. 2023-09-07 11:26:45 -03:00
Greyson Parrelli
b410756dfd Remove dashes from help option. 2023-09-07 10:17:09 -04:00
Nicholas
1458919549 Voice note playlist improvements. 2023-09-07 10:02:16 -04:00
Alex Hart
48ae8c2465 Utilize Bitmap shortcut on API29 and under. 2023-09-07 10:42:39 -03:00
Alex Hart
0a78bcb374 Remove payments beta tag. 2023-09-06 16:27:51 -04:00
Greyson Parrelli
61cdb48273 Fix issue where notification settings were slow to open. 2023-09-06 16:23:14 -04:00
Nicholas Tinsley
b3350b22b6 Remove extraneous "Learn more" from Payments screen. 2023-09-06 15:12:42 -04:00
Nicholas Tinsley
d35d22c7d8 Fix voice note playback for single voice notes. 2023-09-06 11:53:41 -04:00
Alex Hart
24cd11152b Prevent several re-layout calls in ConversationItem. 2023-09-06 11:18:36 -03:00
Nicholas Tinsley
d21254ac02 Bump version to 6.32.0 2023-09-06 10:15:12 -04:00
Nicholas Tinsley
70f08c806a Updated baseline profile. 2023-09-06 10:08:51 -04:00
Nicholas Tinsley
e7c3fb02e8 Updated language translations. 2023-09-06 09:57:00 -04:00
Alex Hart
3d3cf1d76e Add logging for slide enqueue and move dropped animation message to verbose. 2023-09-06 09:34:49 -04:00
Nicholas
2bf385fe38 Upgrade libsignal to 0.32.0 2023-09-06 09:34:49 -04:00
Nicholas
7ba595be55 Ignore Bluetooth devices with Watch in their product name.
Addresses #13141
2023-09-06 09:34:49 -04:00
Alex Hart
c45e79c588 Split reaction view updates to separate width from adding views. 2023-09-06 09:34:49 -04:00
Alex Hart
f37568b050 Stopgap for reaction display in conversation item v2. 2023-09-06 09:34:49 -04:00
Nicholas Tinsley
b5afc1cd1c Fix AttachmentCipherTest 2023-09-06 09:34:49 -04:00
Alex Hart
e9777ccfc6 Fix scroll button when only one giant message is displayed. 2023-09-06 09:34:49 -04:00
Alex Hart
898404fc65 Fix poor spacing of footer in short group text messages. 2023-09-06 09:34:49 -04:00
Alex Hart
131212b158 Fix improper bubble spacing caused by swipe to reply icon. 2023-09-06 09:34:49 -04:00
Greyson Parrelli
3f1d3149e9 Attempt to open db as read-write during error recovery.
Relates to #13034
2023-09-06 09:34:49 -04:00
Nicholas
bfc8b199b6 Hopefully prevent VoiceNotePlaybackService startup crash.
Addresses #13140
2023-09-06 09:34:49 -04:00
Alex Hart
6d4b487428 Update shortcut drawable to use content id. 2023-09-06 09:34:49 -04:00
Cody Henthorne
9337201ffb Prevent okhttp from auto-retrying attachment uploads. 2023-09-06 09:34:49 -04:00
Greyson Parrelli
494b2c6786 Add an index specifically for improving thread count perf. 2023-09-06 09:34:49 -04:00
Alex Hart
bc1c8032c1 Add support for shade and arbitrary overlay drawables to CIV2 Media items. 2023-09-06 09:34:49 -04:00
Alex Hart
21b0a4d370 Fix UriChatWallpaper loading issue where wrong thread was used for setting the imageView resource. 2023-09-06 09:34:49 -04:00
Alex Hart
133effccfc Move delegate creation to a lazy field. 2023-09-06 09:34:49 -04:00
Cody Henthorne
62b4ebc4a9 Fix mention excessive haptic feedback bug. 2023-09-06 09:34:49 -04:00
Cody Henthorne
12941ea19e Fix attachment editor and schedule message bar UI overlap bug. 2023-09-06 09:34:49 -04:00
Alex Hart
f94bd706a4 Fix sender name color. 2023-09-06 09:34:48 -04:00
Greyson Parrelli
3cbbc29c00 Rotate the edit message feature flag. 2023-09-06 09:34:48 -04:00
Cody Henthorne
0827c18eeb Update edit message awareness bottom sheet copy. 2023-09-06 09:34:48 -04:00
Cody Henthorne
6c4ebc9f58 Fix incorrect type value being used for unknown storage records. 2023-09-06 09:34:48 -04:00
Alex Hart
1f2bfe8245 Replace internal setting for CIV2 TextOnly with a FeatureFlag. 2023-09-06 09:34:48 -04:00
Jim Gustafson
305d7485c1 Update to RingRTC v2.31.2 2023-09-06 09:34:48 -04:00
Alex Hart
4ded05bbd1 Implement groundwork for proper ConversationItemV2 payload processing. 2023-09-06 09:34:48 -04:00
Alex Hart
540a2b1876 ConversationItemV2 Quote support and various fixes. 2023-09-06 09:34:48 -04:00
Cody Henthorne
153d3ad388 Fix story group replies layout in RTL. 2023-09-06 09:34:48 -04:00
Alex Hart
a3e36d2453 Update target API to 33 2023-09-06 09:34:48 -04:00
Nicholas Tinsley
b9449a798b Increase Glide exception coverage. 2023-09-06 09:34:48 -04:00
Greyson Parrelli
9da149a868 Convert DateUtils to kotlin, improve perf with caching. 2023-09-06 09:34:48 -04:00
Cody Henthorne
d505c00403 Add CDN3 upload and download support. 2023-09-06 09:34:48 -04:00
Nicholas Tinsley
4d7a0a361f Dismiss Voice Note player notification upon completion. 2023-09-06 09:34:48 -04:00
Greyson Parrelli
e08e02ae80 Update Stopwatch to log fractional milliseconds. 2023-09-06 09:34:48 -04:00
Greyson Parrelli
95c6f569d6 Fetch data in ConversationDataSource in parallel. 2023-09-06 09:34:48 -04:00
Nicholas Tinsley
e46759f436 Update view-once Toast string. 2023-09-06 09:34:48 -04:00
Greyson Parrelli
b42dd5289b Remove unnecessary context args in slide creation. 2023-09-06 09:34:48 -04:00
Greyson Parrelli
a911a007d2 Change job scheduling to be relative rather than absolute. 2023-09-06 09:34:48 -04:00
Nicholas
64babe2e42 Streamable Video. 2023-09-06 09:34:48 -04:00
Greyson Parrelli
099c94c215 Fix handling of some PNI initial contact flows. 2023-08-31 14:33:54 -04:00
Alex Hart
75b81a0fd2 Add the groundwork for the ConversationItemV2 Media item. 2023-08-31 14:33:54 -04:00
Greyson Parrelli
f9ab5d4013 Fix SVR2 typo. 2023-08-31 14:33:53 -04:00
Cody Henthorne
b83080e2d7 Fix payments spinning forever. 2023-08-31 14:33:53 -04:00
Cody Henthorne
6a21106347 Convert StorageService protos to wire. 2023-08-31 14:33:53 -04:00
Greyson Parrelli
9a7d8c858d Convert JobDatabase to Kotlin. 2023-08-31 14:33:53 -04:00
Greyson Parrelli
8339c0d8de Convert JobManager tests to kotlin. 2023-08-29 09:33:45 -04:00
Greyson Parrelli
2b1136ea02 Fix loading states for username editing. 2023-08-29 09:33:45 -04:00
Greyson Parrelli
84b4d69913 Fix error display when entering invalid username characters.
Also convert UsernameEditViewModel to kotlin.
2023-08-29 09:33:45 -04:00
Alex Hart
3fe9ce378e Mock out glideRequests dependency for instrumented test. 2023-08-29 09:33:45 -04:00
Greyson Parrelli
57b9571d86 Don't store blank usernames. 2023-08-29 09:33:45 -04:00
Alex Hart
ae3071d318 Fix bottom constraint of sender photo in civ2. 2023-08-29 09:33:45 -04:00
Greyson Parrelli
8a93814bac Update to the new username link spec. 2023-08-29 09:33:45 -04:00
Alex Hart
a6dd4345ab Rewrite quote view using constraint layout and stubs. 2023-08-29 09:33:45 -04:00
Greyson Parrelli
c71456444f Bump version to 6.31.2 2023-08-28 18:58:47 -04:00
Greyson Parrelli
b916605a24 Updated language translations. 2023-08-28 18:58:23 -04:00
Alex Hart
553da1e7e8 Speed up AvatarProvider. 2023-08-28 18:51:43 -04:00
Cody Henthorne
847651ead7 Revert "Update to RingRTC v2.31.1"
This reverts commit 4ab82c99a8.
2023-08-28 11:55:41 -04:00
Alex Hart
f977f261d6 Utilize iconless person objects until we can fix AvatarProvider. 2023-08-28 12:52:45 -03:00
Greyson Parrelli
3fa9e89e8e Fix typo when reading feature flag. 2023-08-28 10:01:58 -04:00
Cody Henthorne
0662959e1d Bump version to 6.31.1 2023-08-25 16:34:48 -04:00
Cody Henthorne
e5e03f9693 Updated baseline profile. 2023-08-25 16:27:39 -04:00
Cody Henthorne
4203900365 Updated language translations. 2023-08-25 16:24:57 -04:00
Greyson Parrelli
eb7794ba47 Fix flag for battery saver prompt, enable for internal users. 2023-08-25 11:22:29 -04:00
Alex Hart
9626f33768 Fix viewer count in story viewer. 2023-08-25 11:57:28 -03:00
Cody Henthorne
cfc0ace41e Bump version to 6.31.0 2023-08-24 16:05:00 -04:00
Cody Henthorne
ce2947c756 Updated baseline profile. 2023-08-24 15:58:30 -04:00
Cody Henthorne
87fc10ad24 Updated language translations. 2023-08-24 15:34:07 -04:00
Greyson Parrelli
cae71559a0 Updated libphonenumber to 8.13.19 2023-08-24 15:11:54 -04:00
Cody Henthorne
3cf7920a22 Fix various media send failed to compress bugs. 2023-08-24 15:11:54 -04:00
Cody Henthorne
fba9b46fe9 Convert Provisioning, ResumeableUploads, and StickerResources protos to wire. 2023-08-24 15:11:54 -04:00
Alex Hart
611f074a9d Add main thread assertion for setting call status. 2023-08-24 15:11:54 -04:00
Cody Henthorne
7909703f4c Convert CDSI, KBS, and WebSocket protos to wire. 2023-08-24 15:11:54 -04:00
Cody Henthorne
dcbf4b8faf Prevent empty message sends with enter-key sends enabled. 2023-08-24 15:11:54 -04:00
Cody Henthorne
c5edcf47bd Rotate edit message flag. 2023-08-24 15:11:54 -04:00
Alex Hart
02e6b89fdd Fix message clustering for CIV2. 2023-08-24 15:11:54 -04:00
Alex Hart
c4109a19d6 Extract V2TextOnlyViewHolder to its own file. 2023-08-24 15:11:54 -04:00
Alex Hart
630d9492cd Add proper context menu positioning for CIV2. 2023-08-24 15:11:54 -04:00
Alex Hart
b762d95622 Fix issue where StoryPostFragment tries to post updates after fragment is detached from Context. 2023-08-24 15:11:54 -04:00
Alex Hart
3738997832 Add proper click handling support to ConversationItem V2. 2023-08-24 15:11:54 -04:00
Alex Hart
21c70039f4 Upgrade Compose BOM to 23.08.00 2023-08-23 09:29:48 -04:00
Cody Henthorne
23e3385290 Remove unused resources. 2023-08-23 09:29:48 -04:00
Jim Gustafson
4ab82c99a8 Update to RingRTC v2.31.1 2023-08-23 09:29:48 -04:00
Alex Hart
f4df37da23 Compute ConversationItem dates in the background. 2023-08-23 09:29:48 -04:00
Alex Hart
4494d8652d Add several performance improvements to ConversationItemV2. 2023-08-23 09:29:48 -04:00
Alex Hart
32ae4393e2 Fix issue with CIV2 where avatars would not load. 2023-08-23 09:29:48 -04:00
Alex Hart
ea5c3a7c5e Update compileSdk to 34. 2023-08-23 09:29:48 -04:00
Cody Henthorne
f9d9af4fe9 Bump version to 6.30.4 2023-08-22 20:30:44 -04:00
Cody Henthorne
098ef61b5d Updated baseline profile. 2023-08-22 20:24:11 -04:00
Cody Henthorne
e926f56f6b Updated language translations. 2023-08-22 20:21:02 -04:00
Cody Henthorne
9b1da3cfa0 Revert "Do not manually handle orientation changes in ConversationActivity."
This reverts commit 8b2a535f19.
2023-08-22 19:49:39 -04:00
Alex Hart
1fbcd9b362 Fix possible threading issue causing issues in group calls. 2023-08-22 15:15:48 -03:00
Alex Hart
38940e0111 Hopeful fix for remote notification crash. 2023-08-22 15:02:16 -03:00
Cody Henthorne
4fa3570d1e Bump version to 6.30.3 2023-08-22 11:49:43 -04:00
Cody Henthorne
d1c78d5062 Updated baseline profile. 2023-08-22 11:41:23 -04:00
Cody Henthorne
c4862bdddf Updated language translations. 2023-08-22 11:38:36 -04:00
Nicholas Tinsley
2b8018727c Fix voice note earpiece playback. 2023-08-22 11:30:43 -04:00
Greyson Parrelli
e3be279f1f Do not allow the sending of whitespace-only messages. 2023-08-22 11:30:43 -04:00
Greyson Parrelli
1e6126d5be Downgrade some logs and add a missing return. 2023-08-22 11:30:43 -04:00
Alex Hart
9a09708842 Use M3 Switch on EditProxyFragment. 2023-08-22 11:30:43 -04:00
Nicholas Tinsley
e861204cb0 Additional logging around incrementally digested uploads. 2023-08-22 11:30:43 -04:00
Alex Hart
afd3afcf0d Utilize M3 switch on chat color and wallpaper screen. 2023-08-22 11:30:43 -04:00
Alex Hart
5055b0c75d Fix rendering issue when opening the story info sheet too fast. 2023-08-22 11:30:43 -04:00
Alex Hart
372104cdfe Fix typing indicator rendering. 2023-08-21 14:22:08 -03:00
Cody Henthorne
acb24fd265 Bump version to 6.30.2 2023-08-18 17:07:03 -04:00
Cody Henthorne
5b7420ba90 Updated baseline profile. 2023-08-18 16:51:07 -04:00
Cody Henthorne
e73dbd5c15 Updated language translations. 2023-08-18 16:46:34 -04:00
Nicholas Tinsley
b5f82beb46 Revert "Fix contact photo upload failure."
This reverts commit 06dc8ccbdd.
2023-08-18 16:40:37 -04:00
Nicholas Tinsley
61b97fd09b Fix MediaController connection exception. 2023-08-18 16:40:37 -04:00
Cody Henthorne
99e34860d4 Increase vertial tap space for compose text to match bubble. 2023-08-18 16:40:37 -04:00
Cody Henthorne
5d44bbe956 Fix scroll jump when reacting with keyboard open. 2023-08-18 16:40:37 -04:00
Cody Henthorne
e7d0b575bb Reshow IME keyboard if it was showing prior to opening attachment keyboard. 2023-08-18 13:00:54 -04:00
Cody Henthorne
8b2a535f19 Do not manually handle orientation changes in ConversationActivity. 2023-08-18 12:34:50 -04:00
Alex Hart
a242dba345 Fix crash with improper fallback size. 2023-08-18 13:24:48 -03:00
Greyson Parrelli
587cb5de16 Fix unexpected SSE's.
Fixes #13115
2023-08-18 11:07:14 -04:00
Greyson Parrelli
e93c6957ac Fix crash in RecipientTable.getAllPnis() 2023-08-18 09:59:12 -04:00
Cody Henthorne
f644115b54 Bump version to 6.30.1 2023-08-17 16:45:36 -04:00
Cody Henthorne
0c753d22b6 Updated baseline profile. 2023-08-17 16:36:36 -04:00
Cody Henthorne
ec7f2c33e7 Updated language translations. 2023-08-17 16:31:38 -04:00
Cody Henthorne
39c1c1e371 Fix ANR-like bug when resuming MainActivity. 2023-08-17 15:02:16 -04:00
Greyson Parrelli
74d5faf3fa Allow PNI-only contact inserts. 2023-08-17 14:51:11 -04:00
Cody Henthorne
15204a2c84 Remove SignalServiceContent. 2023-08-17 14:43:42 -04:00
Nicholas Tinsley
2397cb5428 Fix play-pause button in video player. 2023-08-17 14:34:19 -04:00
Greyson Parrelli
4b6b87d632 Make ACI's optional on ContactRecords. 2023-08-17 14:33:18 -04:00
Alex Hart
2492b8de34 Fix AvatarProvider crash when user does not have a profile photo set. 2023-08-17 15:30:55 -03:00
Greyson Parrelli
635987a420 Add improved error logging for SSE issues. 2023-08-17 13:42:22 -04:00
Alex Hart
51602ed231 Wrap thread get/create into a transaction. 2023-08-17 14:38:45 -03:00
Alex Hart
25aab0f702 Clean up threadId -1 checks in Conversation code. 2023-08-17 14:18:32 -03:00
Greyson Parrelli
23b3c7f1fd Use a consistent SSE condition and use more breadcrums in logs. 2023-08-17 12:51:40 -04:00
Nicholas Tinsley
451ce74fa4 Safely run VoiceNoteProximityWakeLockManager cleanup. 2023-08-17 11:17:19 -04:00
Greyson Parrelli
1fd9609810 Improve logging around SSE exceptions. 2023-08-17 10:23:03 -04:00
Greyson Parrelli
29804e0a2b Add more logging to SVR2 failures. 2023-08-17 09:54:20 -04:00
Clark Chen
26aa7e8332 Bump version to 6.30.0 2023-08-16 17:49:40 -04:00
Clark Chen
e4e00be119 Updated language translations. 2023-08-16 17:33:09 -04:00
Clark Chen
de6b71528b Rotate edit message flag. 2023-08-16 17:06:04 -04:00
Greyson Parrelli
d005ace383 Add some more getAndPossiblyMerge tests. 2023-08-16 17:06:04 -04:00
Cody Henthorne
f566e10710 Drop V2 suffix from MCPv2 classes. 2023-08-16 17:06:04 -04:00
Alex Hart
18f9c6b1f0 Consolidate some constants and add kotlin target JVM version. 2023-08-16 15:29:45 -03:00
Cody Henthorne
fbf4de0ec5 Remove job-based decryption support and MCPv1. 2023-08-16 14:28:14 -04:00
Nicholas Tinsley
3d94122abc Null check for current audio device. 2023-08-16 12:40:35 -04:00
Greyson Parrelli
442a66df2e Update the groups tables to use foreign keys. 2023-08-16 12:23:54 -04:00
Clark
3be5d61ced Fix wrong thread crash when revoking message while editing. 2023-08-16 10:48:51 -04:00
Greyson Parrelli
f137e23b43 Split usernames into it's own feature flag for internal testing. 2023-08-16 10:46:07 -04:00
Greyson Parrelli
f00178cc0d Don't show the safety number and badges sections in note-to-self settings. 2023-08-16 10:26:32 -04:00
Greyson Parrelli
e33c5b055d Fix FTS searches for punctuation and emoji.
Fixes #13047
2023-08-16 10:26:32 -04:00
Greyson Parrelli
f2237a385e Don't show safety number item for the release notes chat. 2023-08-16 10:26:32 -04:00
Nicholas
a9c45f7e78 Video streaming sample app. 2023-08-16 10:26:32 -04:00
Nicholas
11cfe5ee82 Upgrade to AndroidX Media3. 2023-08-16 10:26:32 -04:00
Clark
4cbcee85d6 Add prompt to help troubleshoot slow notifications. 2023-08-16 10:26:32 -04:00
Alex Hart
98ec2cceb4 Add content description to DeliveryStatusView. 2023-08-16 10:26:32 -04:00
Greyson Parrelli
8ce05c8bbe Include urgent flag in delivery latency log. 2023-08-16 10:26:32 -04:00
Greyson Parrelli
a7019b2e60 Rename PushNotificationReceiveJob -> MessageFetchJob. 2023-08-16 10:26:32 -04:00
Greyson Parrelli
0facdc0497 Fix foreground service in PushNotificationReceiveJob. 2023-08-16 10:26:32 -04:00
Greyson Parrelli
25a7560e2e Always attempt to clear FTS index for DB issues. 2023-08-16 10:26:32 -04:00
Greyson Parrelli
063d909572 Log some debug info about image compression. 2023-08-16 10:26:32 -04:00
Greyson Parrelli
2f8e112f3a Rename MessageProcessReceiver -> RoutineMessageFetchReceiver. 2023-08-16 10:26:32 -04:00
Alex Hart
99abfd0d98 Share to signal from CallSheet. 2023-08-16 10:26:32 -04:00
Greyson Parrelli
5fa9a27ee0 Convert WebSocketStrategy.java -> WebSocketDrainer.kt 2023-08-16 10:26:32 -04:00
Greyson Parrelli
b07d675bb4 Remove BackgroundMessageRetriever and clean up old code. 2023-08-16 10:26:32 -04:00
Alex Hart
9f75c37331 Upgrade Glide to 4.15.1 2023-08-16 10:26:31 -04:00
Greyson Parrelli
df96b05863 Improve table display in Spinner. 2023-08-16 10:26:31 -04:00
Greyson Parrelli
d6adfea9b1 Clean up old one-time prekeys. 2023-08-16 10:26:31 -04:00
Greyson Parrelli
389b439e9a Log ServiceId parsing failures. 2023-08-16 10:26:31 -04:00
Greyson Parrelli
046b89fa21 Update libsignal to 0.31.0 2023-08-16 10:26:31 -04:00
Greyson Parrelli
72e5532c6c Perform a legacy session reset if you fail to decrypt a sync message. 2023-08-16 10:26:31 -04:00
Greyson Parrelli
5688d85789 Do not send retry receipts for messages sent to our PNI. 2023-08-16 10:26:31 -04:00
Clark Chen
28b63e08f1 Bump version to 6.29.2 2023-08-15 15:45:35 -04:00
Clark Chen
951ce77853 Updated language translations. 2023-08-15 15:31:11 -04:00
Clark
b37ba63018 Revert remote delete/edit send threshold to 3 hours. 2023-08-15 12:28:59 -04:00
Clark
251d251661 Send read receipts per edit message revision. 2023-08-14 17:20:04 -04:00
Clark Chen
e11750fb75 Bump version to 6.29.1 2023-08-14 16:33:01 -04:00
Clark Chen
1634ddeb25 Updated language translations. 2023-08-14 16:14:17 -04:00
Clark
7d4bcd7f15 Ignore message_fts table if needed in v175 migration. 2023-08-14 15:59:50 -04:00
Cody Henthorne
13d9b6cc5a Fix incorrect unread counts. 2023-08-14 15:59:50 -04:00
Clark
8d0c41baa0 Update edit message and remote delete send/receive thresholds. 2023-08-14 15:59:50 -04:00
Alex Hart
0303c96ee1 Fix usage of setAvatar in ConversationListItem. 2023-08-14 15:59:50 -04:00
Alex Hart
fde6d7921e Bounce message request state update if needed. 2023-08-14 15:59:50 -04:00
Alex Hart
c632d8ebec Remove group calling tooltip. 2023-08-14 15:59:50 -04:00
Alex Hart
31b43e8754 Fix thread set query during row deletion. 2023-08-14 15:59:50 -04:00
Alex Hart
195360a0f9 Fix quote reply scroll-to-bottom behavior. 2023-08-11 14:06:11 -03:00
Alex Hart
f293f88958 Fix strange RTL white screen behavior. 2023-08-11 13:37:16 -03:00
Alex Hart
6ccfab4087 Bump version to 6.29.0 2023-08-10 15:38:34 -03:00
Alex Hart
a45ce55808 Updated language translations. 2023-08-10 15:32:05 -03:00
Greyson Parrelli
c7dabe1b6f Ensure all group recipients have group records. 2023-08-10 15:29:02 -03:00
Alex Hart
ec51268439 Update Fragment and RecyclerView libraries.
Update Fragment to 1.6.1
Update RecyclerView to 1.3.1
2023-08-10 15:29:02 -03:00
Clark
7543b9fa37 Fix hidden recipients instrumentation tests. 2023-08-10 15:29:02 -03:00
Greyson Parrelli
ca3187d0b8 Ungate some PNP receive-side behavior. 2023-08-10 15:29:02 -03:00
Greyson Parrelli
327cd93e3c Save PNI's from CDSv2 for all users. 2023-08-10 15:29:02 -03:00
Alex Hart
13853c708e Implement proper in-call status for call links. 2023-08-10 15:29:02 -03:00
Greyson Parrelli
ee1291c816 Improve logging of (ACI, PNI, E164) tuples. 2023-08-10 15:29:02 -03:00
Greyson Parrelli
6d2d3ae528 Improve ServiceId parsing functions. 2023-08-10 15:29:02 -03:00
Alex Hart
784f94ecdb Fix missed call icon for groups. 2023-08-10 15:29:02 -03:00
Alex Hart
93bf853b5e Fix threading in call link creation sheet. 2023-08-10 15:29:02 -03:00
Clark
bb83ddfe28 Prompt user for debug logs with slow notifications. 2023-08-10 15:29:02 -03:00
Clark
b51ec53e33 Light battery optimizations cleanup. 2023-08-10 15:29:02 -03:00
Alex Hart
ca210f2b6d Add denial dialogs for call links. 2023-08-10 15:29:02 -03:00
Alex Hart
38bddec4ba Fix call deletion sync message sending. 2023-08-10 15:29:02 -03:00
Alex Hart
b866d57814 Hide admin options if user is not a call admin. 2023-08-10 15:29:02 -03:00
Alex Hart
3c9004d87d Remove maximum denial tracking. 2023-08-10 15:29:02 -03:00
Alex Hart
c479dd404c Add invalid call link dialog. 2023-08-10 15:29:02 -03:00
Alex Hart
748667a0b4 Fix bad ad hoc calling flag check. 2023-08-10 15:29:02 -03:00
Alex Hart
6898595f8a Add GroupCall.JoinState.PENDING support. 2023-08-10 15:29:02 -03:00
Alex Hart
30d0b6fd0e Add additional call links moderation ui. 2023-08-10 15:29:02 -03:00
Greyson Parrelli
7c209db146 Fix logging for 'restricted' power bucket. 2023-08-10 15:29:02 -03:00
Greyson Parrelli
49c8c88a22 Put message latency time in decryption log. 2023-08-10 15:29:02 -03:00
Alex Hart
88e530c96c Rotate edit message flag. 2023-08-10 15:29:02 -03:00
Greyson Parrelli
14f3fb5a94 Break message-latency into high/low priority. 2023-08-10 15:29:02 -03:00
Greyson Parrelli
7ac479b78a Log server time offset in FCM log. 2023-08-10 15:29:02 -03:00
Greyson Parrelli
10b356e642 Stop reading the giftBadges capability. 2023-08-10 15:29:02 -03:00
Greyson Parrelli
7f92482d7a Stop reading the stories capability. 2023-08-10 15:29:02 -03:00
Alex Hart
b79a7309aa Allow input panel to grow as text is entered. 2023-08-10 15:29:02 -03:00
Alex Hart
b54781ff56 Utilize AlertDialog.Builder for displayInDialogAboveAnchor. 2023-08-10 15:29:02 -03:00
Clark
6a87495a6d Update contact hiding to spec. 2023-08-10 15:29:02 -03:00
Greyson Parrelli
c5d9346370 Convert all group code to be based on ServiceIds. 2023-08-10 15:05:18 -03:00
Alex Hart
d247e2c111 Implement several parts of the call links admin UX. 2023-08-10 15:05:18 -03:00
Cody Henthorne
b30f47bac4 Remove ComposeText and SendButton sms/mms transport complexity. 2023-08-10 15:05:18 -03:00
Cody Henthorne
2f9498e137 Refactor input panel to use constraint layout. 2023-08-10 15:05:18 -03:00
Alex Hart
067b3513b7 Add content descriptions to call log row item buttons. 2023-08-10 15:05:18 -03:00
Alex Hart
e50ed22c85 Bump version to 6.28.5 2023-08-10 14:47:22 -03:00
Alex Hart
7cf17f3cc4 Updated language translations. 2023-08-10 14:42:19 -03:00
Greyson Parrelli
9f52ecab5c Ensure that inbound messages mark threads as active. 2023-08-10 13:33:23 -04:00
Alex Hart
c8a56d4f78 Bump version to 6.28.4 2023-08-09 14:02:20 -03:00
Alex Hart
71d482ab29 Updated language translations. 2023-08-09 14:00:24 -03:00
Greyson Parrelli
1cc7b46555 Fix PNI prefixing in provisioning message. 2023-08-09 13:57:32 -03:00
Alex Hart
f56a65d30d Bump version to 6.28.3 2023-08-09 09:42:28 -03:00
Alex Hart
aff813b284 Updated language translations. 2023-08-09 09:29:37 -03:00
Alex Hart
181c0e8a60 Revert "Fix unread state using last seen timestamp."
This reverts commit a5e30bc818.
2023-08-09 09:24:40 -03:00
Alex Hart
cf7f614296 Revert "Use local timestamps for in-chat unread counter."
This reverts commit c501a417bb.
2023-08-09 09:24:19 -03:00
Alex Hart
351e37bcee Bump version to 6.28.2 2023-08-07 15:52:47 -03:00
Alex Hart
cc1f27f588 Updated language translations. 2023-08-07 15:49:02 -03:00
Alex Hart
859905c3e4 Fix sticker insertion from system keyboard. 2023-08-07 15:41:49 -03:00
Alex Hart
8af91bffb5 Fix expanding captions. 2023-08-07 15:41:49 -03:00
Alex Hart
06dc8ccbdd Fix contact photo upload failure. 2023-08-07 15:28:22 -03:00
Alex Hart
c501a417bb Use local timestamps for in-chat unread counter. 2023-08-07 13:28:19 -03:00
Alex Hart
0021e229d8 Add virtual file support to file sharing. 2023-08-07 13:27:55 -03:00
Alex Hart
b4ef95a9b4 Add ActivityNotFoundException handling to ConversationFragment. 2023-08-04 15:17:47 -03:00
Greyson Parrelli
f25a716d62 Bump version to 6.28.1 2023-08-04 12:47:40 -04:00
Greyson Parrelli
a9739ed500 Updated language translations. 2023-08-04 12:47:04 -04:00
Alex Hart
a131eeaa4a Add recaptcha triggers to CFV2. 2023-08-04 13:30:05 -03:00
Alex Hart
9382bbd8fd Add first time in group check. 2023-08-04 13:24:00 -03:00
Greyson Parrelli
adb1e292bf Convert Scrubber to kotlin. 2023-08-04 12:17:05 -04:00
Greyson Parrelli
ca79929141 Add additional Scrubber tests. 2023-08-04 11:43:33 -04:00
Greyson Parrelli
ae2998bcf2 Actually use db reference passed into SearchTable.fullyResetTables. 2023-08-04 11:04:52 -04:00
Alex Hart
1e8e09d5c4 Add multiselect callback to conversation fragment. 2023-08-04 11:57:03 -03:00
Alex Hart
a5e30bc818 Fix unread state using last seen timestamp. 2023-08-04 11:53:16 -03:00
Alex Hart
72edf5c08b Ignore call links check if ff is disabled. 2023-08-04 11:34:43 -03:00
Alex Hart
192154a11c Update reminder on ReminderUpdateEvent broadcast. 2023-08-04 10:21:45 -03:00
Cody Henthorne
c3700cf6d9 Fix incorrect read state causing stale notifications and tweak scroll to bottom behavior. 2023-08-04 09:31:03 -03:00
Greyson Parrelli
78bdee61ef Bump version to 6.28.0 2023-08-02 18:00:00 -04:00
Greyson Parrelli
b84eea9620 Updated language translations. 2023-08-02 17:59:21 -04:00
Greyson Parrelli
5f289fa400 Refactor RecipientTable with a PNI constraint. 2023-08-02 17:49:53 -04:00
Cody Henthorne
67b8f468e4 Remove most of Conversation Fragment V1 and friends. 2023-08-02 17:49:53 -04:00
Alex Hart
9c49c84306 Fix poor linkpreview popin behavior in cfv2. 2023-08-02 17:49:53 -04:00
Jon Chambers
b31ee802fc Update KBS service ID in staging. 2023-08-02 12:13:58 -04:00
Clark
b893a0eb76 Refresh contact list after hiding a contact. 2023-08-02 10:21:40 -04:00
Clark
6f1a04abce Add dialog for when you hit edit message limit. 2023-08-02 09:47:38 -04:00
Clark
041ba27efe Show hidden contacts with chats when searching. 2023-08-01 15:51:31 -04:00
Alex Hart
e239036d8b Send 'clear history' event when clearing the call log. 2023-08-01 15:51:31 -04:00
Clark
d3f073e573 Fix edit message when sending via legacy path. 2023-08-01 15:51:31 -04:00
Clark
0b7490dc06 Update edit message history items to match design. 2023-08-01 15:51:31 -04:00
Clark
a0e514dac9 Enqueue download jobs for edit messages. 2023-08-01 15:51:31 -04:00
Alex Hart
2f1eaf7d6b Fix coloring of media overview toolbar in dark mode. 2023-08-01 15:51:31 -04:00
Alex Hart
6cb8b1c439 Fix call link icon tint in call log. 2023-08-01 15:51:31 -04:00
Alex Hart
5363208e4e Fix typo in method name. 2023-08-01 15:51:31 -04:00
Greyson Parrelli
510ff51198 Rotate the edit message feature flag. 2023-08-01 15:51:31 -04:00
Greyson Parrelli
6dde3d55ef Fix Spinner JS imports. 2023-08-01 15:51:31 -04:00
Greyson Parrelli
e3ec53c2d0 Remove deprecated SMS fields from recipient table. 2023-08-01 15:51:31 -04:00
Cody Henthorne
e7972d4903 Update request and response properties for batch identity checks. 2023-08-01 15:51:31 -04:00
Jordan Rose
a2c3b5d64e Adopt libsignal 0.30.0 and ServiceIds for group members.
Co-authored-by: Greyson Parrelli <greyson@signal.org>
2023-08-01 15:51:31 -04:00
Clark
b11d653fc0 Exit edit message mode on message send. 2023-08-01 15:51:31 -04:00
Clark
66792f2d56 Add heuristics for delayed notifications. 2023-08-01 15:51:31 -04:00
Greyson Parrelli
c012ead143 Validate ServiceIds on envelopes. 2023-08-01 15:51:31 -04:00
Greyson Parrelli
82906aee58 Use strongly-typed ACIs and PNIs everywhere. 2023-08-01 15:51:31 -04:00
Nicholas
7ff4a82755 Show popup on switching to/from speakerphone. 2023-08-01 15:51:31 -04:00
Jordan Rose
8ca49c1e18 Update to RingRTC v2.30.0 2023-08-01 15:51:31 -04:00
Nicholas
7d68a57f53 Fall back to AudioCodec if MediaRecorderWrapper fails. 2023-08-01 15:51:31 -04:00
Greyson Parrelli
c68487c0c7 Disable ktlint rule around class naming. 2023-08-01 15:51:31 -04:00
Clark
4adc660705 Stop content provider handler threads on release. 2023-08-01 15:51:31 -04:00
Clark
d78e73bd6f Fix search showing received mention messages as note to self. 2023-08-01 15:51:31 -04:00
Clark Chen
9d33690f34 Show read more for super long scheduled messages. 2023-08-01 15:51:31 -04:00
Greyson Parrelli
4c428e5b5b Update to new CDS flag. 2023-08-01 15:51:31 -04:00
Greyson Parrelli
4c3882689f Let PNP feature flag override CDS compat flag. 2023-08-01 15:51:31 -04:00
Greyson Parrelli
c82ed473fc Bump version to 6.27.10 2023-08-01 15:49:51 -04:00
Greyson Parrelli
06664b4c58 Updated language translations. 2023-08-01 15:49:15 -04:00
Cody Henthorne
5e68388b01 Fix crash when on recipient change called after requesting to remove observer. 2023-08-01 15:31:34 -04:00
Greyson Parrelli
e14fcf8577 Bump version to 6.27.9 2023-07-31 18:30:04 -04:00
Greyson Parrelli
0219c5253b Updated language translations. 2023-07-31 18:29:37 -04:00
Cody Henthorne
adf3d74d91 Fix attachment keyboard not showing. 2023-07-31 15:44:51 -04:00
Cody Henthorne
3acd68e0b3 Fix quoted reply being dropped from voice notes. 2023-07-31 15:44:51 -04:00
Cody Henthorne
e5eccd732d Fix gifs rendering behind compose bar. 2023-07-31 15:44:51 -04:00
Cody Henthorne
eb0df5791a Fix scroll date header going blank at top of conversation. 2023-07-31 15:44:51 -04:00
Cody Henthorne
f5371123da Fix send location not including description location. 2023-07-31 15:44:51 -04:00
Cody Henthorne
6aa723bc22 Fix lifecycle crashes when fragment is destroy before async callbacks. 2023-07-31 11:38:06 -04:00
Cody Henthorne
9ba34df4ae Fix memory leaks and potentially gif playback issues with conversation items. 2023-07-31 11:38:06 -04:00
Cody Henthorne
6194515f8e Fix invalid message request state being used. 2023-07-31 11:16:51 -04:00
Cody Henthorne
39289715bc Fix incorrect text on remote delete dialog in Note to Self. 2023-07-31 11:12:49 -04:00
Cody Henthorne
5fdd2430ca Bump version to 6.27.8 2023-07-28 20:14:40 -04:00
Cody Henthorne
a7f3f485ad Updated baseline profile. 2023-07-28 20:07:37 -04:00
Cody Henthorne
69a76fa1b7 Updated language translations. 2023-07-28 20:04:55 -04:00
Cody Henthorne
2e5e64b05d Fix crash when opening non-gv2 group. 2023-07-28 20:00:09 -04:00
Cody Henthorne
933e3233a7 Show correct note to self delete dialog options in CFv2. 2023-07-28 19:55:43 -04:00
Cody Henthorne
a54df29542 Protected against crash with unread counter that exceeds thread size. 2023-07-28 19:40:24 -04:00
Greyson Parrelli
cdce802b32 Do not retry auth failures in Svr2MirrorJob. 2023-07-28 17:25:14 -04:00
Greyson Parrelli
2abf30e94b Limit RefreshSvrCredentialsJob to registered users. 2023-07-28 17:14:27 -04:00
Cody Henthorne
148cff1b48 Fix missed menu invalidation after opening search. 2023-07-28 15:00:33 -04:00
Cody Henthorne
ce2a21c438 Fix disabled input state for Release Notes Channel. 2023-07-28 13:27:11 -04:00
Clark
d77c0198d1 Make CFv2 date header visible again. 2023-07-28 12:18:11 -04:00
Cody Henthorne
f58a1acff5 Bump version to 6.27.7 2023-07-27 17:54:06 -04:00
Cody Henthorne
aadbddd7e9 Updated baseline profile. 2023-07-27 17:11:48 -04:00
Cody Henthorne
689fe3947b Updated language translations. 2023-07-27 17:09:06 -04:00
Cody Henthorne
fff0b8b187 Fix crash when tapping re-register banner view. 2023-07-27 17:05:29 -04:00
Cody Henthorne
74562432cf Disable compose input when opening conversation with unregistered recipient. 2023-07-27 17:05:29 -04:00
Clark
8e3027642b Remove self from mention picker. 2023-07-27 16:28:39 -04:00
Cody Henthorne
39f96bb12c Revamp group name color generation. 2023-07-27 16:07:38 -04:00
Clark
938309d125 Remove SMS popup from CFv2. 2023-07-27 16:04:07 -04:00
Cody Henthorne
f740b69ffe Fix lifecycle crashes with keyboard fragment and media sending. 2023-07-27 15:36:38 -04:00
Nicholas Tinsley
0a4147aa0e Add margin to the Note to Self bottom sheet. 2023-07-27 14:58:12 -04:00
Greyson Parrelli
dcffc13843 Fix a RRP recovery path. 2023-07-26 20:15:57 -04:00
Cody Henthorne
1e9a0cdc16 Attempt to swallow erroneous cancel alarm security exceptions. 2023-07-26 14:07:10 -04:00
Cody Henthorne
82e7050864 Fix various lifecycle crashes. 2023-07-26 13:51:19 -04:00
Nicholas
72d1e55373 Re-enable bubble menu shortcut on CFv2. 2023-07-26 13:21:00 -04:00
Cody Henthorne
fe5d5df2d7 Bump version to 6.27.6 2023-07-26 12:39:20 -04:00
Cody Henthorne
9bc337373e Updated baseline profile. 2023-07-26 12:32:52 -04:00
Cody Henthorne
5aad879a95 Updated language translations. 2023-07-26 12:27:54 -04:00
Cody Henthorne
a3798dba68 Properly support group calls in CFv2. 2023-07-26 11:59:03 -04:00
Cody Henthorne
b9f7ef5cbd Fix bug with search UI not showing when searching from settings. 2023-07-26 11:20:06 -04:00
Cody Henthorne
0c3b541031 Fix odd keyboard open state when viewing media. 2023-07-26 11:05:18 -04:00
Cody Henthorne
a09bc53b99 Show mention picker after only typing @. 2023-07-26 10:53:21 -04:00
Cody Henthorne
3731723472 Improve group name coloring performance. 2023-07-25 19:12:04 -04:00
Cody Henthorne
ded29619cd Add payload support to CFv2. 2023-07-25 16:53:45 -04:00
Cody Henthorne
a5b39a8f17 Fix conversation banner animation bugs.
When in doubt, put it in a FrameLayout.
2023-07-25 16:53:11 -04:00
Cody Henthorne
26866a7b2c Reduce conversation transition animations to 200ms. 2023-07-25 16:36:33 -04:00
Cody Henthorne
7837f3999f Bump version to 6.27.5 2023-07-25 12:12:38 -04:00
Cody Henthorne
b25f658647 Updated baseline profile. 2023-07-25 12:06:36 -04:00
Cody Henthorne
49625619fe Updated language translations. 2023-07-25 12:04:15 -04:00
Cody Henthorne
912299bcfd Fix invalid type crash when attempting to recover keyboard landscape height.
A bug with setter using long means it easier to just use long going
forward.
2023-07-25 12:01:14 -04:00
Clark
5648fd2e91 Fix avatar provider buffer underflow. 2023-07-25 12:01:14 -04:00
Clark
557ef5820e Fix bad notification for start foreground when hanging up. 2023-07-25 12:01:14 -04:00
Cody Henthorne
4ce512d259 Fix crash when setting starting scroll position. 2023-07-25 12:01:14 -04:00
Cody Henthorne
a68319dae4 Bump version to 6.27.4 2023-07-24 19:36:29 -04:00
Cody Henthorne
080ecf51d3 Updated baseline profile. 2023-07-24 19:31:55 -04:00
Cody Henthorne
657109dae1 Updated language translations. 2023-07-24 19:27:13 -04:00
Greyson Parrelli
019ef02be8 Ensure we use SVR2 endpoint for checking RRP. 2023-07-24 19:22:07 -04:00
Nicholas Tinsley
d4774c963d Clear click listener after view is recycled.
Fixes #13070
2023-07-24 16:48:57 -04:00
Clark
34cb4c579c Fix for intermittent date header in CFv2. 2023-07-24 14:50:19 -04:00
Clark
74c261f913 Do not regenerate url preview if url has not changed. 2023-07-24 14:43:01 -04:00
Cody Henthorne
7420123519 Fix input panel moving behind navigation bar. 2023-07-24 14:29:31 -04:00
Cody Henthorne
374910736e Fix crash when data observer called after fragment destroy. 2023-07-24 13:43:35 -04:00
Clark
3a71696a49 Fix CFv2 Voice Note Drafts. 2023-07-24 13:34:58 -04:00
Cody Henthorne
73792905a2 Clear compose input immediately on send to match behavior of v1. 2023-07-24 13:32:16 -04:00
Cody Henthorne
05fc30e6e8 Fix CFv2 initial scrolling bugs. 2023-07-24 12:46:17 -04:00
Nicholas
9c308588b5 Bump version to 6.27.3 2023-07-21 22:30:59 -04:00
Nicholas
2f53200096 Updated language translations. 2023-07-21 22:30:36 -04:00
Cody Henthorne
8c1f2c6064 Fix attachment pointer crash when missing incremental digest. 2023-07-21 19:54:57 -04:00
Cody Henthorne
f5fc2acf50 Prevent attachment send of duplicate data with different transforms from failing. 2023-07-21 19:43:31 -04:00
Alex Hart
306fa24d6b Fix crash when text draft save debouncer fires after fragment is destroyed. 2023-07-21 16:32:28 -03:00
Fynn Godau
f5ee9d4a3b Call all lifecycle methods on snapshot mapView 2023-07-21 15:22:02 -04:00
Alex Hart
e7a5f64fe5 Fix scroll to bottom behavior during fast fling. 2023-07-21 16:10:37 -03:00
Alex Hart
6191e003fc Ensure edit history always starts scrolled to top. 2023-07-21 16:03:41 -03:00
Clark
fad401941e Hide old edit revisions from media preview gallery. 2023-07-21 15:00:27 -04:00
Alex Hart
1e0733bd46 Add log-line to see how often setTypists is called. 2023-07-21 15:43:16 -03:00
Clark
d0a44c3f14 Small screen fixes for ACI safety number screen. 2023-07-21 14:41:07 -04:00
Alex Hart
3cee0c1bd5 Fix possible data race in ThumbnailView after image send. 2023-07-21 13:38:49 -03:00
Clark Chen
f5d403e97d Fix tap to scan not wrapping to next line. 2023-07-21 11:11:43 -04:00
Alex Hart
4f1d021aa8 Fix crash when accessing message edit history in details fragment. 2023-07-21 10:11:27 -03:00
Nicholas
dca8c042ab Bump version to 6.27.2 2023-07-20 17:09:05 -04:00
Nicholas
ae1ccadcc8 Updated language translations. 2023-07-20 17:08:55 -04:00
Clark
9ac12c2532 Update safety number screen to be in line with design. 2023-07-20 16:53:46 -04:00
Clark
18337c97e2 Remove underline from safety number learn more. 2023-07-20 16:50:34 -04:00
Clark
1e652d497e Dont allow editing failed messages. 2023-07-20 16:50:21 -04:00
Cody Henthorne
b53cad2808 Fix various CFv2 scrolling issues. 2023-07-20 16:50:10 -04:00
Alex Hart
4520ff78ff Fix issue with icon pop in attachment keyboard. 2023-07-20 16:44:54 -03:00
Cody Henthorne
b887129cd7 Fix crash when leaving conversation. 2023-07-20 13:52:12 -04:00
Cody Henthorne
ec25831a37 Fixes for CFv2.
- Status bar color being incorrect when entering a screen that changes it and then returning (e.g., Message Details)
- Fix crash in enter sends mode
- Fix warning about non-closed cursor
- Prevent message abandonment (via trim thread) when it's the first in an inactive thread
- Fix payment attachment button flashing on attachment keyboard open if payments disabled
- Fix reactionDelegate crash
- Fix attachment preview (file, mp3, location, etc) not getting cleared on send
2023-07-20 13:50:32 -04:00
Clark
744f74b498 Address UI issues on safety number verification screen. 2023-07-20 13:09:37 -04:00
Clark Chen
52aaf93f37 Fix copy of the safety number fragment. 2023-07-20 12:53:22 -04:00
Cody Henthorne
2d92d4ad87 Fix jumbo emoji having bubbles bugs. 2023-07-19 19:54:12 -04:00
Cody Henthorne
7617cc0a80 Remove Phase 2 in preparation for CFv2. 2023-07-19 19:46:45 -04:00
Cody Henthorne
dc69bcf6f2 Fix view once media send in CFv2. 2023-07-19 19:41:17 -04:00
Nicholas
0775fc7ead Bump version to 6.27.1 2023-07-19 17:48:36 -04:00
Nicholas
034aef483b Updated language translations. 2023-07-19 17:48:06 -04:00
Nicholas Tinsley
ee5b99fed4 Rotate edit message feature flag. 2023-07-19 17:40:41 -04:00
Nicholas
e031da1337 Bump version to 6.27.0 2023-07-19 17:23:29 -04:00
Nicholas
8f1514642c Updated language translations. 2023-07-19 17:22:58 -04:00
Clark
5aa304ea9a Always show verify safety numbers option. 2023-07-19 17:12:19 -04:00
Alex Hart
c5a27b2cc7 Fix remote story deletion syncing. 2023-07-19 17:12:19 -04:00
Clark
0fde404da8 Add you may have messages notification. 2023-07-19 17:12:19 -04:00
Cody Henthorne
5242b9af39 Rotate CFv2 feature flag. 2023-07-19 17:12:19 -04:00
Cody Henthorne
5e2d6fc05f Fix incorrect unread divider behavior when receiving new messages. 2023-07-19 17:12:18 -04:00
Clark Chen
7e08a1f321 Only show one safety number education dialog at a time. 2023-07-19 17:12:18 -04:00
Cody Henthorne
076295eae8 Fix rendering bug when scrolling a chat with a background in CFv2.
"When in doubt, put it in a FrameLayout. - Wayne Gretzky" - MiCHAELSCOTT
2023-07-19 17:12:18 -04:00
Cody Henthorne
c13339ca52 Fix scroll to bottom on send bug in CFv2. 2023-07-19 17:12:18 -04:00
Nicholas
627657e1de Update to the final ExoPlayer release. 2023-07-19 17:12:18 -04:00
Alex Hart
a8349671d0 Add Receive support for the new CallLogEvent proto messages. 2023-07-19 17:12:18 -04:00
Clark
461875b0e4 Add support for displaying both ACI and e164 safety numbers. 2023-07-19 17:12:18 -04:00
Cody Henthorne
00bbb6bc6e Fix spoiler display bug in long message view. 2023-07-19 17:12:18 -04:00
Greyson Parrelli
e1f1181a07 Specifiy SHA256 for docker base image. 2023-07-19 17:12:18 -04:00
Nicholas Tinsley
0e1de39192 Remove Bluetooth mic voice message recording. 2023-07-19 17:12:18 -04:00
Cody Henthorne
05bbeb10da Revert "Attempt to fix crash on call hangup."
This reverts commit 025411c9fb.
2023-07-19 17:12:18 -04:00
Cody Henthorne
7375a9e06b Do not jumbo styled emojis. 2023-07-19 17:12:18 -04:00
Cody Henthorne
4910050891 Fix bubbles jumping around when entering selection mode. 2023-07-19 17:12:18 -04:00
Cody Henthorne
67d4f666ce Add share highwater timestamp protection to CFv2. 2023-07-19 17:12:18 -04:00
Cody Henthorne
e6c9449e3c Fix voice note playback and wave form generation in CFv2. 2023-07-19 17:12:18 -04:00
Alex Hart
b8effba497 Fix crash in hasHeader via range check. 2023-07-19 17:12:18 -04:00
Cody Henthorne
8fcdd7cb8a Update attachment keyboard based on payment availability in CFv2. 2023-07-19 17:12:18 -04:00
Alex Hart
f3fb5ccc3b CFV2 handle keyboard images and gifs. 2023-07-19 17:12:18 -04:00
Alex Hart
b8f55f982f Fix toggle in AdvancedPrivacySettingsFragment. 2023-07-19 17:12:18 -04:00
Cody Henthorne
6db59cb896 Prevent menu creation slowing data load performance in CFv2. 2023-07-18 10:19:17 -04:00
Cody Henthorne
3db83c1602 Fix multiple issues in CFv2. 2023-07-18 10:01:48 -04:00
Nicholas Tinsley
6be9225fbd Include incremental digest when sending attachments. 2023-07-18 09:55:02 -04:00
Nicholas Tinsley
653eff403c Prevent overlap of backup icon on small screens.
Fixes #13064.
2023-07-18 09:55:02 -04:00
Alex Hart
ab410ec0cf CFV2 Message Request state adapter update. 2023-07-18 09:55:02 -04:00
Cody Henthorne
7b75a32394 Clean up remaining CFv2 todos. 2023-07-18 09:55:02 -04:00
Cody Henthorne
daf077b3c9 Fix overlapping date and unread decorations. 2023-07-18 09:55:02 -04:00
Alex Hart
f6bbb59400 Fix crash when accessing binding via delayed runnable. 2023-07-18 09:55:02 -04:00
Cody Henthorne
09813d5dbd Fix crash when dismissing mention picker late. 2023-07-18 09:55:02 -04:00
Cody Henthorne
fe509838f4 Add CFv2 feature flag. 2023-07-18 09:55:02 -04:00
Alex Hart
6a443d0074 Fix clipping around incoming V2 conversation items. 2023-07-18 09:55:02 -04:00
Greyson Parrelli
8fc1065dd6 Rename some protos. 2023-07-18 09:55:02 -04:00
Cody Henthorne
1af50ba0f5 Perform safety number check pre-send in CFv2. 2023-07-18 09:55:02 -04:00
Alex Hart
82b3036b77 Add handling for text slide deck in sendMessage. 2023-07-18 09:55:02 -04:00
Alex Hart
676412019c CFV2 Add search bottom bar to bottom panel barrier ids. 2023-07-18 09:55:02 -04:00
Alex Hart
980f4e00e2 Scroll date header in CFV2. 2023-07-18 09:55:01 -04:00
Cody Henthorne
5731bf023a Add unread divider decoration to CFv2. 2023-07-18 09:55:01 -04:00
Alex Hart
2511ca17aa Add onRequestPermissionsResult to CFV2. 2023-07-18 09:55:01 -04:00
Nicholas Tinsley
fae653540b Make Safety Number Changed dialog scrollable.
This helps with smaller screens.
2023-07-18 09:55:01 -04:00
Alex Hart
b0ca66cc1a Add new active column to ThreadTable. 2023-07-18 09:55:01 -04:00
Nicholas
a65e9c76bc Bump version to 6.26.3 2023-07-17 23:31:59 -04:00
Nicholas
adbac4c557 Updated language translations. 2023-07-17 23:31:48 -04:00
Nicholas
fc7b024e96 Fix benchmark test user generation. 2023-07-17 22:06:00 -04:00
Clark
acee65ba25 Defer TooltipPopup show till anchor has been laid out. 2023-07-17 16:40:08 -04:00
Clark Chen
244902ecfc Bump version to 6.26.2 2023-07-14 18:15:18 -04:00
Clark Chen
4b1a678af2 Updated language translations. 2023-07-14 17:57:49 -04:00
Greyson Parrelli
59dd72b5c0 Fix issue with syncing remote deletes in note to self. 2023-07-14 17:46:40 -04:00
Greyson Parrelli
44c393f11a Fix possible ISE in registration. 2023-07-14 17:46:40 -04:00
Nicholas Tinsley
fddfbd8d2d Fix changing number flow in scenarios where service requires additional verification.
Fixes #12985, #13059.
2023-07-14 17:46:40 -04:00
Clark
e7e00bd428 Disable sticker suggestions when editing message. 2023-07-14 17:46:40 -04:00
Cody Henthorne
07702e69ad Improve spoiler render performance. 2023-07-14 14:41:36 -04:00
Cody Henthorne
e5c3757629 Remove Phase 1 in preparation for CFv2. 2023-07-14 13:51:30 -04:00
Greyson Parrelli
7031bbae43 Close the SVR2 socket when we're done. 2023-07-14 13:11:08 -04:00
Nicholas Tinsley
4a6dfed676 Even more Change Number logging. 2023-07-14 09:56:49 -04:00
Clark Chen
bb0b414d71 Bump version to 6.26.1 2023-07-13 18:11:30 -04:00
Nicholas Tinsley
95e25652c1 Add change number screen logging. 2023-07-13 17:59:53 -04:00
Nicholas Tinsley
58155b0859 Restore previous Registration session handler.
Fixes #12839, #13059.
2023-07-13 15:20:15 -04:00
Clark
f579b79d2e Change websocket keepalive response time to 20s. 2023-07-13 14:22:10 -04:00
Greyson Parrelli
47673be4e0 Bump version to 6.26.0 2023-07-12 16:09:59 -04:00
Clark Chen
f67e6a9e9f Updated language translations. 2023-07-12 15:56:38 -04:00
Greyson Parrelli
110d8259fa Explicity declare exported status in manifest entries.
This shouldn't change any behavior. We're just explicitly declaring the
exported field to be what it would otherwise get set to by default.
2023-07-12 15:48:52 -04:00
Alex Hart
8f253ffc43 Add lazy thread creation throughout in preparation for CFV2. 2023-07-12 15:48:52 -04:00
Greyson Parrelli
6ca9cb6da1 Add migration to cleanup some inconsistent DB state. 2023-07-12 15:48:52 -04:00
Greyson Parrelli
1b63bdec12 Control CDS compat mode with it's own remote config. 2023-07-12 15:48:52 -04:00
Greyson Parrelli
bb52172516 Fix validation of NullMessage types. 2023-07-12 15:48:52 -04:00
Alex Hart
1640495f34 Fix CFV2 selection. 2023-07-12 15:48:52 -04:00
Cody Henthorne
64415a980f Fix text cutoff in device transfer lock dialog.
Fixes #13040
2023-07-12 15:48:52 -04:00
Cody Henthorne
e06d141823 Tweak SpoilerPaint settings to reduce wave effect.
Closes #13041
2023-07-12 15:48:52 -04:00
Clark
ac4d8679a1 Add local metrics for message processing. 2023-07-12 15:48:52 -04:00
Clark
8fc03a67b9 Fix failing AttachmentCipherTest for incremental mac. 2023-07-12 15:48:52 -04:00
Cody Henthorne
648506fe04 Fix empty emoji search index. 2023-07-12 15:48:52 -04:00
Cody Henthorne
2c74ac8bfa Fix spoilers not working in story replies. 2023-07-12 15:48:52 -04:00
Cody Henthorne
963709c552 Fix draft message lost during media send flow. 2023-07-12 15:48:52 -04:00
Clark
9af888a595 Refactor FcmFetchManager to make foreground service clearer. 2023-07-12 15:48:51 -04:00
Cody Henthorne
ec373b5b4d Fix attachment dedupe race where original may be deleted before new usage is recorded. 2023-07-12 15:48:51 -04:00
Cody Henthorne
bfcd57881e Add test to verify sync behavior during VERIFIED to DEFAULT change. 2023-07-12 15:48:51 -04:00
Alex Hart
b277a8c5e0 CFV2 fix possible race when initializing menu. 2023-07-12 15:48:51 -04:00
Cody Henthorne
979a50716e Fix most android tests.
Co-authored-by: Greyson Parrelli <greyson@signal.org>
2023-07-12 15:48:51 -04:00
Jim Gustafson
c5f0da8151 Update to RingRTC v2.29.0 2023-07-12 15:48:51 -04:00
Cody Henthorne
aee0b5268f Improve conversation open benchmark test. 2023-07-12 15:48:51 -04:00
Alex Hart
7e909f2bee Add InternalSettings option for ConversationItem V2. 2023-07-12 15:48:51 -04:00
Cody Henthorne
584c90521a Polish voice notes in CFv2. 2023-07-12 15:48:51 -04:00
Yuval Razieli
23ef8c78bd Fix an issue where the charset in the link preview of some pages was not identified correctly. 2023-07-12 15:48:51 -04:00
Greyson Parrelli
5ca025544e Improve logging around memory usage. 2023-07-12 15:48:51 -04:00
Greyson Parrelli
500ae0c72e Add Spinner support for kyber keys. 2023-07-12 15:48:51 -04:00
Greyson Parrelli
c359207f1f Fix potential NPE in MediaOverviewPageFragment. 2023-07-12 15:48:51 -04:00
Alex Hart
159f2ebec0 Don't crash on invalid window token in tooltip popup. 2023-07-12 15:48:51 -04:00
Alex Hart
a0db812606 Fix action bar background for multiselect in CFV2. 2023-07-12 15:48:51 -04:00
Alex Hart
d4c6a433d7 Add documentation to DeliveryStatusView. 2023-07-12 15:48:51 -04:00
Alex Hart
3a7cde9239 Fix pending rotation pivot point. 2023-07-12 15:48:51 -04:00
Greyson Parrelli
4b6c308ae9 Stop HEAD requests for possibly-unlisted contacts during CDS refresh. 2023-07-12 15:48:51 -04:00
Greyson Parrelli
002279f6a7 Ignore AccountRecord.e164 if PNP-capable. 2023-07-12 15:48:51 -04:00
Greyson Parrelli
c807e52ad9 Support CDSI ignore the useCompat flag. 2023-07-12 15:48:51 -04:00
Greyson Parrelli
9557e3b910 Remove unused feature flag constant. 2023-07-12 15:48:51 -04:00
Greyson Parrelli
ddc77884bd Inline the credit card payments feature flag. 2023-07-12 15:48:51 -04:00
Greyson Parrelli
9d6337d5a8 Inline the chat filters feature flag. 2023-07-12 15:48:51 -04:00
Greyson Parrelli
117dd17215 Rotate edit message feature flag. 2023-07-12 15:48:51 -04:00
Alex Hart
f9eed0f6d0 Fix slide in animation for new messages in CFV2. 2023-07-12 15:48:51 -04:00
Greyson Parrelli
4429145cdf Remove deprecated ignoreResults parameter. 2023-07-12 15:48:51 -04:00
Alex Hart
5ea4cbf9ca CFV2 Add proper body presentation code. 2023-07-12 15:48:51 -04:00
Ehren Kret
c6473ca9e6 Minor improvement to Android Backup file format. 2023-07-12 15:48:51 -04:00
Alex Hart
38b2a2f5b7 Add multi-select support to CFV2. 2023-07-12 15:48:51 -04:00
Greyson Parrelli
ebaa445bee Save last-known server time offset. 2023-07-12 15:48:51 -04:00
Greyson Parrelli
8372c699f7 Update username link QR code styling. 2023-07-12 15:48:51 -04:00
Greyson Parrelli
e1570e9512 Start mirroring to SVR2. 2023-07-12 15:48:51 -04:00
Alex Hart
dfb7304626 Fix external shares in CFV2. 2023-07-11 17:58:09 -04:00
Alex Hart
919531a82b Enable quick toggle camera in CFV2 input panel. 2023-07-11 17:58:09 -04:00
Alex Hart
81b2e9ccd2 Hook up reaction callback. 2023-07-11 17:58:09 -04:00
Alex Hart
4ef2aba4e2 Ensure text-only entries are cached. 2023-07-11 17:58:09 -04:00
Alex Hart
4590655dc5 Fix CI V2 layout bounds when item has a reaction. 2023-07-11 17:58:09 -04:00
Alex Hart
3040b70100 Add initial instrumentation testing for V2 ConversationItem shapes. 2023-07-11 17:58:09 -04:00
Alex Hart
47b97aafc6 Add TypingIndicatorDecoration to CFV2. 2023-07-11 17:58:09 -04:00
Alex Hart
27e7383db6 Add compose divider to CFV2. 2023-07-11 17:58:09 -04:00
Alex Hart
42fe827cb3 Add proper navigation bottom bar color. 2023-07-11 17:58:09 -04:00
Alex Hart
3fa3e8357c Handle video calls for 1:1 conversations in CFV2. 2023-07-11 17:58:09 -04:00
Alex Hart
6260607e1b Launch settings on toolbar press in CFV2. 2023-07-11 17:58:09 -04:00
Rashad Sookram
3c1666e874 Update verification metadata for aapt2. 2023-07-11 17:58:09 -04:00
Clark
f4a082584c Add upload/download size restrictions for attachments based on remote config. 2023-07-11 17:58:09 -04:00
Ehren Kret
87d4dba32b remove whispersystems.org reference 2023-07-11 17:58:09 -04:00
Alex Hart
329f68d167 Upgrade to Gradle 8.0.2 and AGP 8.0.2 2023-07-11 17:58:09 -04:00
Alex Hart
2053cf085a Add retries to CallEventSyncJob. 2023-07-11 17:58:09 -04:00
Iñaqui
16d48984c5 Remove calling stun fallback. 2023-07-11 17:58:09 -04:00
Cody Henthorne
a17800283a Plumb schedule message and edit message send flows for CFv2. 2023-07-11 17:58:09 -04:00
Alex Hart
9b1917cbdc Add story ring to CFV2. 2023-07-11 17:58:09 -04:00
Alex Hart
e1e3d7a85b Small tweaks for footer positioning. 2023-07-11 17:58:09 -04:00
Alex Hart
dc37d1f029 Apply proper background to input panel when wallpaper is enabled. 2023-07-11 17:58:09 -04:00
Alex Hart
53e62f2be0 Add new text-only conversation item. 2023-07-11 17:58:09 -04:00
Alex Hart
e6cc789c6f Add conversation test springboard fragment. 2023-07-11 17:58:09 -04:00
Cody Henthorne
cfaef77b21 Properly plumb attachment keyboard in CFv2. 2023-07-11 17:58:09 -04:00
Clark Chen
36fc9aa82a Add 10s timeout to user facing CDSI requests. 2023-07-11 17:58:09 -04:00
Alex Hart
8d20669e46 Rewrite AlertView to be a single ImageView. 2023-07-11 17:58:09 -04:00
Greyson Parrelli
9d8501cd64 Bump version to 6.25.5 2023-07-11 14:21:30 -04:00
Greyson Parrelli
88827e94f5 Updated language translations. 2023-07-11 14:21:30 -04:00
Alex Hart
99d2a0c0b6 Wrap dialog content in a scroll view. 2023-07-11 14:21:30 -04:00
Alex Hart
1d0582867b Fix crashing when deleting a custom story. 2023-07-11 14:21:30 -04:00
Alex Hart
0ea6d9205d Fix rotation metrics for DeliveryStatusView. 2023-07-11 14:21:30 -04:00
Greyson Parrelli
f438ef543b Fix prekey generation during registration. 2023-07-10 23:05:36 -04:00
Alex Hart
61cd9767c8 Ensure bitmaps are not recycled when getting them from the cache. 2023-07-10 17:08:27 -03:00
Alex Hart
5fbf0a98b9 Bump version to 6.25.4 2023-07-07 15:08:04 -03:00
Alex Hart
1671518ded Updated language translations. 2023-07-07 15:04:13 -03:00
Alex Hart
f628ffca06 Fix avatar blurring during calls. 2023-07-07 14:53:23 -03:00
Alex Hart
1d6f4fd4e7 Bump version to 6.25.3 2023-07-06 16:27:55 -03:00
Alex Hart
9eedc0a36b Updated language translations. 2023-07-06 16:19:42 -03:00
Alex Hart
a870fe9e1a Do not throw an ISE when we cannot start a foreground service from calling. 2023-07-06 16:12:09 -03:00
Alex Hart
d3f779cea9 Fix crash when toggling stories. 2023-07-05 12:16:46 -03:00
Cody Henthorne
fb4f41b996 Extend style to inserted text via paste or auto-correct regardless of content. 2023-06-30 14:05:35 -04:00
Cody Henthorne
11aac76fb6 Fix spoiler rendering in quote views. 2023-06-30 13:46:17 -04:00
Nicholas
98424f6cbb Bump version to 6.25.2 2023-06-30 13:36:03 -04:00
Nicholas
1cf7c59af9 Updated language translations. 2023-06-30 13:33:51 -04:00
Clark
13470fb0c3 Increase FCM push websocket timeout. 2023-06-30 12:41:00 -04:00
Nicholas Tinsley
3aa0fd1937 Reset continue button state when dismissing registration number confirmation dialog. 2023-06-30 12:23:47 -04:00
Clark
9e6f2336d1 Add push websocket fetch stats. 2023-06-30 11:07:05 -04:00
Nicholas Tinsley
8b8d62f598 Only close AttachmentCipher streams if using incremental MAC. 2023-06-30 11:06:51 -04:00
Nicholas
4572ae5886 Bump version to 6.25.1 2023-06-29 18:42:29 -04:00
Nicholas
0802d4beb4 Updated language translations. 2023-06-29 18:40:18 -04:00
Nicholas
9361aa700a Transcode video files in a streamable format. 2023-06-29 18:30:29 -04:00
Greyson Parrelli
be5cad1cec Add support for Emoji v15.0 2023-06-29 15:55:49 -04:00
Greyson Parrelli
fe20de2995 Improve logging around edit and sync messages. 2023-06-29 15:55:12 -04:00
Clark
8714e4298e Specify scale type for glide thumbnails. 2023-06-29 15:32:04 -04:00
Alex Hart
1baebe7475 Remove read log line from AvatarProvider. 2023-06-29 13:17:05 -03:00
Nicholas
6686ae43f3 Bump version to 6.25.0 2023-06-28 17:24:23 -04:00
Nicholas
cc2c0e9561 Updated language translations. 2023-06-28 17:21:07 -04:00
Nicholas
34d252a4bd Add incremental digests to attachment sending. 2023-06-28 17:13:15 -04:00
Cody Henthorne
025411c9fb Attempt to fix crash on call hangup. 2023-06-28 17:13:15 -04:00
Nicholas Tinsley
4edb66d2b9 Fix SimpleProgressDialog. 2023-06-28 17:13:15 -04:00
Alex Hart
a17033dff4 Add ContentProvider for user avatars. 2023-06-28 17:13:15 -04:00
Cody Henthorne
04a5e56da7 Add mentions support to CFv2. 2023-06-28 17:13:15 -04:00
Bernie Dolan
0e6a3dd408 Fix MobileCoin test net config. 2023-06-28 17:13:15 -04:00
Nicholas Tinsley
47f48a6a8c Put backup enable dialog into ScrollView for smaller screens.
Addresses #13033.
2023-06-28 17:13:15 -04:00
Cody Henthorne
2ef7fabade Add inline emoji search to CFv2. 2023-06-28 17:13:15 -04:00
Alex Hart
5c2b475c01 Add randomized testing for ConversationItem. 2023-06-28 17:13:15 -04:00
Clark
559f4bc0d3 Rotate edit message feature flag. 2023-06-28 17:13:15 -04:00
Clark
1357a4816b Update edit history dialog to new style. 2023-06-28 17:13:15 -04:00
Nicholas Tinsley
d1b8a56c0f Revert "Add more logging around Bubble eligibility."
This purely logs the state without affecting the return logic.
2023-06-28 17:13:15 -04:00
Cody Henthorne
7ea38298ea Fix gif playback in CFv2. 2023-06-28 17:13:15 -04:00
Clark
c08f1355db Refresh isConnectionNecessary on network block changes. 2023-06-28 17:13:15 -04:00
Cody Henthorne
b6589637fa Add emoji search to CFv2. 2023-06-28 17:13:15 -04:00
Alex Hart
2ee2d2883a Disallow reacting to pending or failed messages. 2023-06-28 17:13:15 -04:00
Alex Hart
029c8ba917 Fix text story keyboard in text stories. 2023-06-28 17:13:15 -04:00
Alex Hart
60e1ee21ed Prevent ANR from large call logs. 2023-06-28 17:13:15 -04:00
Alex Hart
a96e9158c4 Change call notification flag to UPDATE_CURRENT and change request code. 2023-06-28 17:13:15 -04:00
Alex Hart
e47765d7d5 Add logging to WebRtcActivity Intent. 2023-06-28 17:13:15 -04:00
Clark Chen
e807435c8b Capitalize edited string. 2023-06-28 17:13:15 -04:00
Sgn-32
14b41a93e2 Fix Do not animate spoilers if system animations are disabled.
Closes #13016
2023-06-28 17:13:15 -04:00
Yuichi Araki
f51fb9da29 Report shortcut usage when a message is sent to a group.
Fixes #13029
Close #13030
2023-06-28 17:13:15 -04:00
Sebastian Scheibner
df3ca3d3cc Fix duplicate kyber pre key id in registration
The `PreKeyUtil.generateKyberPreKey` method doesn't update the `nextKyberPreKeyId` in the metadataStore,
so the two `metadataStore.getNextKyberPreKeyId()` calls in this method return the same id.
The first oneTimeKyberPreKey will have the same id as the lastResortKyberPreKey and overwrite it in the database.

Closes #13021
2023-06-28 17:13:15 -04:00
Clark
7786956b11 Fix conversation date header weirdness with edited messages. 2023-06-28 17:13:15 -04:00
Cody Henthorne
c17d62aeab Update ktlint and gradle plugin. 2023-06-28 17:13:15 -04:00
Cody Henthorne
65255121de Add media keyboard support in CFv2. 2023-06-28 17:13:15 -04:00
Greyson Parrelli
b042945fef Add check for internal users around group lock ordering. 2023-06-28 17:13:15 -04:00
Greyson Parrelli
9d979217fa Include 'dont keep activities' setting in debuglog. 2023-06-28 17:13:15 -04:00
Greyson Parrelli
c177de2ec3 Add CDSI and SVR2 to static IP list. 2023-06-26 15:09:39 -04:00
Greyson Parrelli
b4fd57d900 Fix possible NPEs with megaphone container. 2023-06-26 15:09:36 -04:00
Nicholas Tinsley
92339dfdcf Add one more logging statement to the change number flow. 2023-06-26 15:09:36 -04:00
Nicholas
8ae115028e Update PIN switch keyboard button to be more straightforward.
Addresses #12866.
2023-06-26 15:09:36 -04:00
Greyson Parrelli
2dd0221680 Fix issue where FTS recovery path didn't work during migrations.
There was a recursive getDatabase() call because it was happening during
a migration. Solution was to re-use the DB instance we already had.
2023-06-26 15:09:36 -04:00
Greyson Parrelli
1a1923c6c0 Fix docker build by using our own mirror.
snapshot.debian.org is super flaky and unreliable. At this point it's
easier to host our own mirror snapshot.
2023-06-26 15:09:36 -04:00
Cody Henthorne
5801ad4bdb Update FCM to 23.1.2 2023-06-26 15:09:36 -04:00
Cody Henthorne
388f2971e9 Allow libsignal-service to build with JDK17. 2023-06-26 15:09:36 -04:00
Greyson Parrelli
14c3a36ec0 Improve logging for GV2 validation error. 2023-06-26 15:09:36 -04:00
Alex Hart
1ad338ce31 Turn DelveryStatusView into a custom AppCompatImageView. 2023-06-26 15:09:36 -04:00
Greyson Parrelli
71981e8a27 Fix dockerfile for reproducible builds. 2023-06-26 15:09:36 -04:00
Nicholas
5cb10cd054 Improve voice note Bluetooth state handling. 2023-06-26 15:09:36 -04:00
Rashad Sookram
ecf576e9b9 Don't localize audio output in logs. 2023-06-26 15:09:36 -04:00
Alex Hart
09d17659b9 Add media send support to CFV2. 2023-06-26 15:09:36 -04:00
Cody Henthorne
53673be5cb Update AGP to 8.0
Co-authored-by: Greyson Parrelli <greyson@signal.org>
2023-06-26 15:09:36 -04:00
Alex Hart
ed4a1d6ddd Use delete instead of delete for me in call log dialog. 2023-06-26 15:09:36 -04:00
Alex Hart
e3044b8b85 Add preview treatment for call links. 2023-06-26 15:09:36 -04:00
Alex Hart
4ce05a064c Add CFV2 Sticker Suggestions. 2023-06-26 15:09:36 -04:00
Alex Hart
2fbcc23451 Add LinkPreview support to CFV2. 2023-06-26 15:09:36 -04:00
Alex Hart
3bdffed8c9 Add call link scrubbing for logs. 2023-06-26 15:09:36 -04:00
Nicholas
6cc8e87d46 Bump version to 6.24.4 2023-06-26 14:33:40 -04:00
Nicholas
7d8f549d97 Updated baseline profile. 2023-06-26 14:31:28 -04:00
Nicholas
fb2ef265bd Updated language translations. 2023-06-26 14:16:45 -04:00
Nicholas
96e8256781 Fix TestUser constructor. 2023-06-26 14:10:13 -04:00
Cody Henthorne
dd40517f12 Fix crash after hanging up call. 2023-06-26 14:10:13 -04:00
Cody Henthorne
6c95b766d6 Fix answer audio call and video starting bug. 2023-06-26 14:10:13 -04:00
Greyson Parrelli
feffdcb71e Bump version to 6.24.3 2023-06-22 18:53:58 -04:00
Greyson Parrelli
f33e2c49ca Updated language translations. 2023-06-22 18:53:29 -04:00
Clark
1037acd4a2 Load original message and use it for timestamps in Conversation. 2023-06-22 18:42:56 -04:00
Clark
68042fc755 No longer call startForeground in onCreate. 2023-06-22 14:09:23 -04:00
Alex Hart
1bdc77affe Resolve issue with incoming video call state. 2023-06-22 14:52:10 -03:00
Nicholas Tinsley
88f50da4fb Discard voice note recording on error. 2023-06-22 13:32:27 -04:00
Cody Henthorne
bc1fbd9b6c Fix spoilers not animating after leaving and returning to conversation.
Fixes #13015
2023-06-22 11:57:59 -04:00
Cody Henthorne
c1b7b7c95e Fix styling lost in draft state bug. 2023-06-22 11:16:27 -04:00
Greyson Parrelli
b0688eed5c Bump version to 6.24.2 2023-06-21 20:34:12 -04:00
Greyson Parrelli
e06ed03d33 Updated language translations. 2023-06-21 20:34:12 -04:00
Clark
e2a79394ab Always background legacy FCM fallback. 2023-06-21 20:34:12 -04:00
Clark
5db770ca44 Use original message instead of edit message when checking if we can edit. 2023-06-21 17:52:33 -04:00
Cody Henthorne
df8aaa2005 Provide better 'why' when an attachment is not auto-downloaded. 2023-06-21 16:18:16 -04:00
Cody Henthorne
882748f080 Allow formatting text from overflow menu. 2023-06-21 16:16:16 -04:00
Cody Henthorne
15035f4eb3 Fix rendering when editing a message with spoilers. 2023-06-21 13:08:00 -04:00
Cody Henthorne
1d0a87f52a Add ability to clear or toggle formatting. 2023-06-21 13:05:46 -04:00
Clark
59b2cc5f79 Fix edit message date header showing wrong date. 2023-06-21 12:44:22 -04:00
Nicholas Tinsley
d6758fc264 Store PIN keyboard type in file backup. 2023-06-21 11:53:01 -04:00
Greyson Parrelli
36418bec59 Ensure SqlCipherDeletingErrorHandler runs delete. 2023-06-21 10:24:12 -04:00
Cody Henthorne
07bd8b2fa3 Fix NPE in spoiler renderer. 2023-06-21 10:10:01 -04:00
Greyson Parrelli
d989d02af9 Bump version to 6.24.1 2023-06-20 14:26:35 -04:00
Greyson Parrelli
503ce13122 Updated language translations. 2023-06-20 14:24:16 -04:00
Alex Hart
8f77321adb Dispose notification disposable when stopping service. 2023-06-20 12:07:11 -03:00
Clark
60cdcea791 Revert "Call startForeground onCreate for generic foreground service. " 2023-06-20 10:53:28 -04:00
Clark
86cd4c5c30 Fix remote delete for edit messages. 2023-06-20 10:29:47 -04:00
Nicholas Tinsley
62d5f61a0b Allow verification with PIN rather than SMS when restoring from backup. 2023-06-16 16:31:46 -04:00
Clark
25860867bb Use correct receive timestamp for edit message handling. 2023-06-16 16:30:49 -04:00
Greyson Parrelli
272860f071 Do not animate spoilers if system animations are disabled. 2023-06-16 15:51:24 -04:00
Nicholas
767cfbc717 Fix atomic registrations when not using session ID. 2023-06-16 15:38:16 -04:00
Cody Henthorne
55af6ca84e Bump version to 6.24.0 2023-06-15 15:44:35 -04:00
Cody Henthorne
88933ae051 Updated baseline profile. 2023-06-15 15:36:09 -04:00
Cody Henthorne
4781beebee Updated language translations. 2023-06-15 15:36:09 -04:00
Clark Chen
0049c74323 Fix baseline profile generation benchmark. 2023-06-15 15:36:09 -04:00
Alex Hart
361727cec6 Add resend handler to CFV2. 2023-06-15 15:36:09 -04:00
Alex Hart
1b95177e0e Add handleViewPaymentDetails to CFV2. 2023-06-15 15:36:09 -04:00
Alex Hart
8d34c54de2 Add multiselect action support to CFV2. 2023-06-15 15:36:09 -04:00
Alex Hart
2fa0eba3db Add search call-through in CFV2 override. 2023-06-15 15:36:09 -04:00
Cody Henthorne
6cc41e95c6 Remove edit message receive feature flag. 2023-06-15 15:36:09 -04:00
Cody Henthorne
b1523f5b91 Update text formatting feature flag. 2023-06-15 15:36:09 -04:00
Nicholas
d16002546d Create account in single network request. 2023-06-15 15:36:09 -04:00
Clark
186a93f5d1 Use separate PNI key distribution endpoint instead of change number. 2023-06-15 15:36:09 -04:00
Alex Hart
3d4875bcfe Add sticker suggestion send support to CFV2. 2023-06-15 15:36:09 -04:00
Nicholas
441e30971a Add more logging around Bubble eligibility.
To help diagnose #12036.
2023-06-15 15:36:08 -04:00
Alex Hart
ff115c2349 Add voice recording to CFV2. 2023-06-15 15:36:08 -04:00
Clark
f23e5bdb44 Prepare edit message for beta run. 2023-06-15 15:36:08 -04:00
Jim Gustafson
d0a232d86a Update to RingRTC v2.28.1 2023-06-15 15:36:08 -04:00
Alex Hart
9fef8386e6 Fix initial call state when starting from action. 2023-06-15 15:36:08 -04:00
Alex Hart
b1680ba5c6 Add new call notification strings. 2023-06-15 15:36:08 -04:00
Alex Hart
6cd59daf0a Fix several issues with call notifications. 2023-06-15 15:36:08 -04:00
Greyson Parrelli
38f2b39ac4 Add common interface over SVR implementations. 2023-06-15 15:36:08 -04:00
Alex Hart
51222738df Remove avatar color from CallLink table. 2023-06-15 15:36:08 -04:00
Clark
b9835584d8 Add sharing for PNP usernames badge. 2023-06-15 13:32:00 -04:00
Alex Hart
cff01021c2 Ensure call links only ever have one call event associated with them. 2023-06-15 13:32:00 -04:00
Alex Hart
d19b8a125c Do not enable ringing for call links. 2023-06-15 13:32:00 -04:00
Alex Hart
4caaa0033b Re-enable call delete sync events. 2023-06-15 13:32:00 -04:00
Alex Hart
f3a0a059ea Add search and arbitrary jump support to CFV2. 2023-06-15 13:32:00 -04:00
Alex Hart
290b0fe46f Ensure owned call links are revoked on delete. 2023-06-15 13:32:00 -04:00
g1a55er
03a212eee4 Reschedule job if background web socket message retrieval fails.
Closes #12971
2023-06-15 13:31:59 -04:00
Alex Hart
f3b629bc06 Add reaction support to CFV2. 2023-06-15 13:31:59 -04:00
Alex Hart
b9e002f7b1 Update call links parsing. 2023-06-15 13:31:59 -04:00
Alex Hart
c90779beea Fix jump position for quotes. 2023-06-15 13:31:59 -04:00
Alex Hart
bc8c8a049f Add onViewsRevealed implementation. 2023-06-15 13:31:59 -04:00
Alex Hart
1d9dc66265 Update several androidx dependencies.
Navigation to 1.6.0
Fragment to 1.6.0
Compose BOM to 2023.05.01
Lifecycle to 2.6.1
Activity to 1.7.2
2023-06-15 13:31:59 -04:00
Alex Hart
886c149c3f Add in-call info sheet for call links. 2023-06-15 13:31:59 -04:00
Clark
369ca189d3 Disable stickers and gifs when editing message. 2023-06-15 13:31:59 -04:00
Cody Henthorne
02c4bbe816 Add date headers to CFv2. 2023-06-15 13:31:59 -04:00
Clark
523c9f6576 Add workflow for monitoring APK size changes. 2023-06-15 13:31:59 -04:00
Cody Henthorne
c259430b09 Bump version to 6.23.5 2023-06-15 13:04:42 -04:00
Cody Henthorne
fc94b90a03 Revert "Adopt new APIs for network connectivity check."
This reverts commit de4c6ab7b7.
2023-06-15 12:57:35 -04:00
Cody Henthorne
6af521130d Revert "Fix connectivity over VPN on older API versions."
This reverts commit 7e24252447.
2023-06-15 12:57:14 -04:00
Cody Henthorne
9c001e4f35 Bump version to 6.23.4 2023-06-15 11:30:05 -04:00
Cody Henthorne
9e8dee36a6 Updated baseline profile. 2023-06-15 11:25:25 -04:00
Cody Henthorne
26b17d8a3c Updated language translations. 2023-06-15 11:23:11 -04:00
Cody Henthorne
1c5e2e3359 Fix linkify for valid URLs with ... in the path. 2023-06-15 11:20:22 -04:00
Clark
0437d37f23 Fix pending retry receipt cache deadlock. 2023-06-15 11:20:22 -04:00
Cody Henthorne
ec6b1a44de Add text formatting support to release notes channel. 2023-06-15 11:20:22 -04:00
Clark
08c661bb14 Fix foreground service start/stop race. 2023-06-14 14:42:08 -04:00
Cody Henthorne
62f62d89c5 Bump version to 6.23.3 2023-06-14 12:40:51 -04:00
Cody Henthorne
37dd8b40b2 Updated baseline profile. 2023-06-14 12:28:43 -04:00
Cody Henthorne
15825f6c3f Updated language translations. 2023-06-14 12:26:09 -04:00
Clark
ad196bf03c Fix stopForegroundTask crash. 2023-06-14 10:54:21 -04:00
Cody Henthorne
332c4ca26e Improve spoiler performance by reducing number of particles and frame rate. 2023-06-14 10:36:43 -04:00
Cody Henthorne
305edf1928 Fix SQL crash in backup restore by preventing job from running until restore complete. 2023-06-14 10:28:34 -04:00
Nicholas Tinsley
9c0c25ef99 Detect URL patterns that will crash OkHttp.
Addresses #12998.
2023-06-13 15:48:24 -04:00
Cody Henthorne
458dae227f Bump version to 6.23.2 2023-06-13 11:10:20 -04:00
Cody Henthorne
b1ba9fd54f Updated baseline profile. 2023-06-13 10:55:53 -04:00
Cody Henthorne
dd42b5b851 Updated language translations. 2023-06-13 10:51:44 -04:00
Alex Hart
d8e3edc729 Fix incoming call intent. 2023-06-13 10:47:40 -04:00
Cody Henthorne
36aa8623da Improve spoiler performance by stopping animation when backgrounded. 2023-06-12 13:11:15 -04:00
Cody Henthorne
7e24252447 Fix connectivity over VPN on older API versions. 2023-06-12 12:39:18 -04:00
Cody Henthorne
164ce06177 Fix style growing when applying other styles. 2023-06-12 09:01:21 -04:00
Cody Henthorne
c9d298c447 Bump version to 6.23.1 2023-06-09 16:28:20 -04:00
Cody Henthorne
5129613ce8 Updated baseline profile. 2023-06-09 16:22:01 -04:00
Cody Henthorne
b985ace7ed Updated language translations. 2023-06-09 16:19:06 -04:00
Cody Henthorne
d28afac973 Fix baseline benchmark. 2023-06-09 16:15:00 -04:00
Clark
fd9b5ff7c4 Drop failed processed incoming messages. 2023-06-09 15:48:01 -04:00
Cody Henthorne
75580bea27 Update SQLCipher to 4.5.4-S2 2023-06-09 15:40:12 -04:00
Cody Henthorne
db7056c53b Fix bug when processing duplicate text messages within the same batch. 2023-06-09 15:39:13 -04:00
Cody Henthorne
b55181ffe6 Fix localization issue with group call start strings. 2023-06-09 12:30:30 -04:00
Nicholas Tinsley
81149e5aa8 Hide call audio devices that are not of known type. 2023-06-09 10:18:47 -04:00
Cody Henthorne
3a341eee19 Fix revealing spoilers in text stories. 2023-06-09 10:06:35 -04:00
Cody Henthorne
e19c7efbfe Bump version to 6.23.0 2023-06-07 16:01:13 -04:00
Cody Henthorne
7e7c68321b Updated baseline profile. 2023-06-07 15:17:03 -04:00
Cody Henthorne
9fa3f54c7c Updated language translations. 2023-06-07 15:14:47 -04:00
Cody Henthorne
3ff273f1f2 Ignore tests that fail when run on JDK17. 2023-06-07 15:11:26 -04:00
Cody Henthorne
e607b1962c Cycle text formatting send remote config. 2023-06-07 14:54:52 -04:00
Nicholas
2c4c6bf87c Allow registration with landlines. 2023-06-07 14:46:01 -04:00
Clark
bf048e2a75 Dont block main thread when we try to stop FCM fetch service. 2023-06-07 14:12:35 -04:00
Alex Hart
b0c4bb04e7 Hide clear history action menu item when total count is zero. 2023-06-07 14:25:54 -03:00
Cody Henthorne
7e0e6c2786 Fix pool limits and y-translation issues with CFv2 recycler view. 2023-06-07 12:51:08 -04:00
Alex Hart
d6a03df087 Fix db contention when inserting group ring updates. 2023-06-07 13:28:37 -03:00
Nicholas Tinsley
cfc89d2a74 Gracefully handle invalid audio device selection during calls. 2023-06-07 11:24:22 -04:00
Clark
c491c9dc8c Fix chosen location not being sent sometimes. 2023-06-06 12:47:16 -04:00
Cody Henthorne
eae066b3a2 Fix styling issues when covering mentions. 2023-06-06 12:47:16 -04:00
Clark
71aa17bad6 Verify number of backup frames written is read back. 2023-06-06 12:47:16 -04:00
Alex Hart
93df01e266 CallLink treatment for ConversationItem. 2023-06-06 12:47:16 -04:00
Alex Hart
8f96abb41e Update TAG usage throughout call link processors. 2023-06-06 12:47:16 -04:00
Nicholas Tinsley
1457a6fe16 Deduplicate audio output choices. 2023-06-06 12:47:16 -04:00
Alex Hart
290c107698 Implement simple avatar color picking algorithm to align with iOS. 2023-06-06 12:47:16 -04:00
Alex Hart
bf7aaddbf9 Hook in Call Links integration via factory. 2023-06-06 12:47:16 -04:00
Clark
59435e49c8 Call startForeground onCreate for generic foreground service. 2023-06-06 12:47:16 -04:00
Clark
c3499e538e Fix wallpaper scaling on orientation change. 2023-06-06 12:47:16 -04:00
Cody Henthorne
1d41b1c5a3 Remove a couple old dependencies. 2023-06-06 12:47:16 -04:00
Cody Henthorne
e303570b2f Update to libsignal 0.26.0 2023-06-06 12:47:16 -04:00
Alex Hart
62940893f0 Add peek and join capabilities to call links implementation. 2023-06-06 12:47:16 -04:00
Alex Hart
f8434bede5 Add clear all menu action to calls tab. 2023-06-06 12:47:16 -04:00
Jim Gustafson
c08e108fc3 Update to RingRTC v2.28.0
Co-authored-by: Jordan Rose <jrose@signal.org>
2023-06-06 12:47:16 -04:00
Alex Hart
cd9a160cae Fix pip offset. 2023-06-06 12:47:16 -04:00
Cody Henthorne
bba8b8be56 Fix external share when it contains an image and text. 2023-06-06 12:47:16 -04:00
Bernie Dolan
f56b5d58c6 Update MobileCoin payments and enclave values. 2023-06-06 12:47:16 -04:00
Cody Henthorne
ac4b0ed606 Improve auto-leave group behavior. 2023-06-06 12:47:16 -04:00
Clark Chen
d3e71185e6 Fix story ring not updating on recipient screen. 2023-06-06 12:47:16 -04:00
Alex Hart
b4f2cd9ff4 Add updated phone svg icon. 2023-06-06 12:47:16 -04:00
Alex Hart
fd8d305899 Add voice note draft playback impl to cfv2. 2023-06-06 12:47:16 -04:00
Alex Hart
bde7ae944a Add draft handling in toggle button update method. 2023-06-06 12:47:16 -04:00
Alex Hart
99f83e5dc9 Add handleDial implementation in CFV2. 2023-06-06 12:47:16 -04:00
Cody Henthorne
693aef5c04 Add partial share and draft support to CFv2. 2023-06-06 12:47:16 -04:00
Alex Hart
b9ae537706 Add onItemClick handling in CFV2. 2023-06-06 12:47:16 -04:00
Alex Hart
e41dd6d39d CFV2 Add edit message support. 2023-06-06 12:47:16 -04:00
Alex Hart
5d546f46e4 CFV2 Add reply to message support. 2023-06-06 12:47:16 -04:00
Clark
2bef5653b4 Lower priority for apk update notification. 2023-06-06 12:47:16 -04:00
Clark
63d8549865 Light refactor to thread update. 2023-06-06 12:47:16 -04:00
Clark Chen
f6bac2f476 Fix trash can not appearing when editing group photo. 2023-06-06 12:47:16 -04:00
Alex Hart
0dd51856d3 CFV2 - Implement add to home screen. 2023-06-06 12:47:16 -04:00
Alex Hart
be01f2b511 CFV2 -- Add to Contacts / Mute Conversation. 2023-06-06 12:47:16 -04:00
Alex Hart
045d2cf42f Copy over several more action handlers for CFV2. 2023-06-06 12:47:16 -04:00
Cody Henthorne
64ddd982fe Add review banner to CFv2. 2023-06-06 12:47:16 -04:00
Alex Hart
b785b3f887 CFV2 Save to Disk / Copy Text Content. 2023-06-06 12:47:16 -04:00
Alex Hart
399421e20e CFV2 Implement delete, forward, view once handling. 2023-06-06 12:47:16 -04:00
Cody Henthorne
a656d65d1d Move render split to match CFv2 for fairer comparisons. 2023-06-06 12:47:16 -04:00
Nicholas Tinsley
291a5d57c4 Replace em dash in javadoc with ASCII-safe hyphen. 2023-06-06 12:47:16 -04:00
Nicholas Tinsley
a9a91e3162 Address a bunch of compiler warnings.
None of these should change any behavior, they're all annotations and stuff.
2023-06-06 12:47:16 -04:00
g1a55er
de4c6ab7b7 Adopt new APIs for network connectivity check.
Addresses #12941
2023-06-06 12:47:16 -04:00
Nicholas Tinsley
7ea9fc0c3b Update AlertDialogs to MaterialAlertDialogs.
Addresses #12949.
2023-06-06 12:47:16 -04:00
Greyson Parrelli
1965d5879f Log message procesing speed at 2 decimal places. 2023-06-06 12:47:16 -04:00
Greyson Parrelli
b2b907a86a Add additional validation for group messages. 2023-06-06 12:47:15 -04:00
Cody Henthorne
e565de0724 Add verified updates and unverified banner. 2023-06-06 12:47:15 -04:00
Greyson Parrelli
7318e676f7 Add an internal feature to search your contacts by ID/ACI/PNI. 2023-06-06 12:47:15 -04:00
Greyson Parrelli
7c28d8ad51 Fix possible NPE when opening a story. 2023-06-06 12:47:15 -04:00
Greyson Parrelli
3e21fb77c7 Skip sends to users with prekey failures. 2023-06-06 12:47:15 -04:00
Cody Henthorne
6b91e525db Add Reminders and Conversation Banner to CFv2. 2023-06-06 12:47:15 -04:00
Greyson Parrelli
0aca03a919 Add kyber support for change number. 2023-06-06 12:47:15 -04:00
Greyson Parrelli
e2ef8e2ef9 Add support for kyber prekeys. 2023-06-06 12:47:15 -04:00
Greyson Parrelli
15c248184f Add two-phase commit support for SVR2. 2023-06-06 12:47:15 -04:00
Alex Hart
a0c1b072b6 Bump version to 6.22.5 2023-06-01 17:16:39 -03:00
Alex Hart
fe806cc4eb Updated baseline profile. 2023-06-01 16:52:55 -03:00
Nicholas Tinsley
aacad78cdb Delete redundant Bluetooth voice note codepath.
Added some additional logging, as well.
2023-06-01 16:40:33 -03:00
Cody Henthorne
b89f2dd862 Fix message notifier plural. 2023-06-01 16:40:33 -03:00
Alex Hart
36d01477cc Fix incoming call notifications. 2023-06-01 16:40:33 -03:00
Nicholas
e4090d00c9 Increase logging around image compression failures. 2023-05-31 16:59:54 -04:00
Alex Hart
ff55bc8209 Bump version to 6.22.4 2023-05-31 16:47:39 -03:00
Alex Hart
b375c9efdc Updated baseline profile. 2023-05-31 16:47:27 -03:00
Alex Hart
67b4fde5c3 Updated language translations. 2023-05-31 16:42:28 -03:00
Clark
63c6581d14 Fix transaction issues with thread update. 2023-05-31 14:46:25 -04:00
Nicholas
3ddd01981d Prevent crashing when forwarding edited media. 2023-05-31 14:45:56 -04:00
Alex Hart
bbd845905a Bump version to 6.22.3 2023-05-30 17:27:36 -03:00
Alex Hart
3f53c37187 Updated baseline profile. 2023-05-30 17:26:36 -03:00
Clark
159c0d1104 Fix child transaction causing batch to be discarded. 2023-05-30 15:18:05 -04:00
Nicholas Tinsley
82db08b76f Catch native RuntimeExceptions in voice memo recording start. 2023-05-30 15:07:58 -04:00
Nicholas Tinsley
83e84228f5 Bolster Bluetooth headset detection for Android 11 and older. 2023-05-30 15:00:49 -04:00
Clark
05edc715ef Fix thread update race for draft update. 2023-05-30 10:44:52 -04:00
Alex Hart
c503df5eec Bump version to 6.22.2 2023-05-26 15:55:12 -03:00
Alex Hart
1f242473fe Updated baseline profile. 2023-05-26 15:51:55 -03:00
Alex Hart
f7db5f8ae0 Updated language translations. 2023-05-26 15:46:39 -03:00
Greyson Parrelli
3b88d7cf94 Update notifications after transaction completes. 2023-05-26 14:23:07 -04:00
Nicholas Tinsley
cac82f2eba Make license screen content static. 2023-05-26 11:56:27 -04:00
Greyson Parrelli
5811b469cf Observe empty state on main thread. 2023-05-26 09:35:07 -04:00
Nicholas Tinsley
af1175f32e Update CameraX. 2023-05-25 18:03:45 -04:00
Nicholas Tinsley
7a5ce5761f Add tap to send debug log to account locked screen.
Addresses #12950.
2023-05-25 18:02:25 -04:00
Nicholas
34104355cb Bump version to 6.22.1 2023-05-25 17:12:21 -04:00
Nicholas
143c1255d8 Updated baseline profile. 2023-05-25 17:11:52 -04:00
Nicholas
a9d0e5ac81 Updated language translations. 2023-05-25 17:09:14 -04:00
Greyson Parrelli
c8b3ee51ed Acquire group lock before processing a message batch. 2023-05-25 16:07:26 -04:00
Cody Henthorne
c964067139 Fix placeholder in username string. 2023-05-25 15:59:32 -04:00
Nicholas Tinsley
539f590c4c Disconnect Bluetooth SCO when user cancels recording. 2023-05-25 11:57:13 -04:00
Nicholas Tinsley
71dddd4a1b Add some more Bluetooth connection logging. 2023-05-25 11:55:42 -04:00
Alex Hart
792f5dd7b5 Always display bottom bar. 2023-05-25 12:49:36 -03:00
Nicholas
a3af49d92a Bump version to 6.22.0 2023-05-24 12:15:26 -04:00
Nicholas
4289b43a81 Updated baseline profile. 2023-05-24 12:14:47 -04:00
Nicholas
7f55623acf Updated language translations. 2023-05-24 12:11:15 -04:00
Nicholas Tinsley
52060b65be Disable all icons other than the active one. 2023-05-24 12:05:23 -04:00
Clark
fd826749e4 Edit message design tweaks. 2023-05-24 12:05:23 -04:00
Clark
627e15c3dd Add thumbnail for when editing message with media. 2023-05-24 12:05:23 -04:00
Clark
90f6890180 Enqueue thread update job after transaction completes. 2023-05-24 12:05:23 -04:00
Nicholas Tinsley
61eb397d2b Simplify notification for saving media.
Addresses #11759.
2023-05-24 12:05:23 -04:00
Greyson Parrelli
3a5e5364c7 Remove support for legacy gv1 sync messages. 2023-05-24 12:05:23 -04:00
Greyson Parrelli
25779d04a6 Regularly run account consistency checks. 2023-05-24 12:05:23 -04:00
Clark
242900e87f Dont requery attachments and add all jobs at once. 2023-05-24 12:05:23 -04:00
Nicholas Tinsley
05f07d1788 Handle SmsRetriever initialization cancellation. 2023-05-24 12:05:23 -04:00
Alex Hart
f961f4ccac Add initial CFV2 long press state implementation. 2023-05-24 12:05:23 -04:00
Nicholas Tinsley
145377b05f Add accessibility labels to navigate up button in ConversationFragment.
Addresses #12951.
2023-05-24 12:05:23 -04:00
Cody Henthorne
bc88887195 Animate CFv2 with keyboard opening or closing. 2023-05-24 12:05:23 -04:00
Nicholas Tinsley
5362b1c21c Prevent NPE when finishing voice memo recording. 2023-05-24 12:05:23 -04:00
Clark
0cfd3265ba Fix post transaction tasks not actually running. 2023-05-24 12:05:23 -04:00
Cody Henthorne
1099128513 Add rendering and handling for various disabled input states in CFv2. 2023-05-24 12:05:23 -04:00
Greyson Parrelli
ad50c81a6b Remove unnecessary validation check. 2023-05-24 12:05:23 -04:00
Clark
0817f113c6 Schedule media downloads after successful transaction. 2023-05-24 12:05:23 -04:00
Clark
4a9a07a9ef Run post transaction tasks only after root transaction ends. 2023-05-24 12:05:23 -04:00
Nicholas
61f50cfe60 Add license screen to settings page. 2023-05-24 12:05:23 -04:00
Nicholas
92888778c2 Restart websocket immediately upon network change. 2023-05-24 12:05:23 -04:00
Alex Hart
987f9b9dba Allow call links to exist in the calls tab. 2023-05-24 12:05:23 -04:00
Clark
97d95f37cc Rotate profile key when contact hidden. 2023-05-24 11:29:59 -04:00
Clark
836cd04564 Inline message processing when we can. 2023-05-24 11:29:59 -04:00
Clark
c26f54161d Use original message id for edit message history. 2023-05-24 11:29:59 -04:00
Clark
b540009ce6 Only call start foreground once from FCM. 2023-05-24 11:29:59 -04:00
Alex Hart
e58e209950 Remove ripple from tab buttons. 2023-05-24 11:29:58 -04:00
Alex Hart
4597a23104 Fix new call item margins. 2023-05-24 11:29:58 -04:00
Alex Hart
3aacf4bcd2 Add search highlight to call rows. 2023-05-24 11:29:58 -04:00
Alex Hart
6e6b663fac Reposition unread dots according to figma. 2023-05-24 11:29:58 -04:00
Alex Hart
6dad7eafcf Fix call tab color and spacing. 2023-05-24 11:29:58 -04:00
Alex Hart
5a38143987 Integrate call links create/update/read apis. 2023-05-24 11:29:58 -04:00
Greyson Parrelli
4d6d31d624 Make attachment count/size remote configurable. 2023-05-24 11:29:58 -04:00
Greyson Parrelli
938c82be3f Inline the calls tab feature flag. 2023-05-24 11:29:58 -04:00
Greyson Parrelli
dc2e249566 Add QR scanning to username link flow. 2023-05-24 11:29:58 -04:00
Greyson Parrelli
bb8fdcabcb Update to libsignal 0.25.0 2023-05-24 11:29:58 -04:00
Greyson Parrelli
6cf4dbc78c Add pre-alpha support for SVR2. 2023-05-24 11:29:58 -04:00
Jim Gustafson
8cd0ac5451 Update to RingRTC v2.27.0 2023-05-24 11:29:58 -04:00
Clark
33745f0b0c Fix edit message showing twice in notifications. 2023-05-24 11:29:58 -04:00
Nicholas
29ffed219f Bump version to 6.21.3 2023-05-24 10:52:40 -04:00
Nicholas
9629d0f715 Updated baseline profile. 2023-05-24 10:51:42 -04:00
Nicholas
01651984d7 Updated language translations. 2023-05-24 10:36:15 -04:00
Nicholas Tinsley
c7c15250ca Clarify app icon string descriptions. 2023-05-24 10:00:38 -04:00
Greyson Parrelli
36b96eafcc Bump version to 6.21.2 2023-05-19 16:37:26 -04:00
Greyson Parrelli
94f930ee22 Updated language translations. 2023-05-19 16:37:12 -04:00
Greyson Parrelli
3f740d2904 Tweak network timeout settings. 2023-05-19 16:30:19 -04:00
Nicholas Tinsley
f8529adfcf Design tweaks for app icon switching. 2023-05-19 16:30:19 -04:00
Alex Hart
77ccbdd322 Deduplicate in migration to prevent constraint breakage. 2023-05-19 16:30:19 -04:00
Cody Henthorne
f2846efd2c Fix split second spoiler reveal when quoting a message with a spoiler. 2023-05-19 16:30:19 -04:00
Nicholas Tinsley
131f9c4bc9 Move app icon composables outside of mutable Fragment class.
This way, the composables do not receive an implicit mutable parameter, which allows the compiler to mark them as skippable.
2023-05-19 16:30:19 -04:00
Greyson Parrelli
d7c06fff50 Bump version to 6.21.1 2023-05-18 20:35:52 -04:00
Greyson Parrelli
09a22d9dc4 Updated language translations. 2023-05-18 20:35:52 -04:00
Greyson Parrelli
0fbab04253 Animate transitions in icon selection. 2023-05-18 20:35:51 -04:00
Nicholas
c963e99dca Introduce the ability to change the app icon. 2023-05-18 20:35:51 -04:00
Alex Hart
7a555d127f Tighten migration and remove null peer events. 2023-05-18 20:35:51 -04:00
Cody Henthorne
866408f673 Limit body ranges processed on received messages. 2023-05-18 20:35:51 -04:00
Greyson Parrelli
a7e5ab1a6a Update inbound attachment processing. 2023-05-18 12:22:35 -04:00
Alex Hart
14d16d61e6 Only update text fields if contents changed. 2023-05-18 10:27:17 -03:00
Greyson Parrelli
b988e4a813 Disable foreign key constraints during backup restore updgrades. 2023-05-17 18:44:23 -04:00
Greyson Parrelli
ae33c8db1b Bump version to 6.21.0 2023-05-17 15:50:42 -04:00
Greyson Parrelli
9f1c5ac6bb Updated language translations. 2023-05-17 15:45:48 -04:00
Alex Hart
448e7d0739 Don't collapsed missed calls with outgoing or incoming. 2023-05-17 15:30:27 -04:00
Clark
8971ff9057 Fix for ForegroundServiceDidNotStartInTimeException for FcmFetchForegroundService. 2023-05-17 15:30:26 -04:00
Clark
2d6b16b2ce Introduce extra caching for group message processing. 2023-05-17 15:30:26 -04:00
Greyson Parrelli
44ab1643fa Fix group membership recipient remapping. 2023-05-17 15:30:26 -04:00
Cody Henthorne
a64bffd83a Complete text formatting. 2023-05-17 15:30:26 -04:00
Clark
534c5c3c64 Try not blocking main threads to start foreground service. 2023-05-17 15:30:26 -04:00
Cody Henthorne
99d3f9918f Fix crash and bug with ellipsizing 'About' in Settings screen.
Fixes #12895
Closes #12905
2023-05-17 15:30:26 -04:00
Ehren Kret
aaebf029db Remove unused capabilities. 2023-05-17 15:30:26 -04:00
Greyson Parrelli
e2c2ace0e3 Add initial storage interfaces for kyber prekeys. 2023-05-17 15:30:26 -04:00
Nicholas Tinsley
c76002663f Push bubbled conversation onto back stack. 2023-05-17 15:30:26 -04:00
Nicholas Tinsley
c5317370c8 Prevent duplicate reactions bottom sheet. 2023-05-17 15:30:26 -04:00
Cody Henthorne
4b09f4a654 Add basic attachment keyboard support to CFv2. 2023-05-17 15:30:26 -04:00
Alex Hart
0c57113d8e Add migration for call link recipient link. 2023-05-17 15:30:26 -04:00
Alex Hart
d7dd77a5af Add additional logging around thumbnail loading. 2023-05-17 15:30:26 -04:00
Greyson Parrelli
5c5b88ebcc Pluralize some strings. 2023-05-17 15:30:26 -04:00
Alex Hart
8df0248d4f Utilize receipts instead of messagerecord count for views. 2023-05-17 15:30:26 -04:00
Alex Hart
58e48fdf14 Fix switch toggling after first toggle. 2023-05-17 15:30:26 -04:00
Alex Hart
407fc56218 Utilize conversationRecipient for displaying whom a payment is to or from in conversation. 2023-05-17 15:30:26 -04:00
Alex Hart
398527d3f1 Ignore table update when remap already exists. 2023-05-17 15:30:26 -04:00
Greyson Parrelli
59745a695c Add context to some strings. 2023-05-17 15:30:26 -04:00
Cody Henthorne
2aaeda6ca8 Add initial send support to CFv2. 2023-05-17 15:30:26 -04:00
Alex Hart
5c78de2f46 Update nullability of CallLinkTable column. 2023-05-17 15:30:09 -04:00
Clark
93efc21452 Propogate read sync message to latest revision. 2023-05-17 15:30:09 -04:00
Clark
50ad005e7c Update edit message history dialog to match designs. 2023-05-17 15:30:09 -04:00
Clark
71c3bcdd29 Convert MSL delete entries for recipient to collection query. 2023-05-17 15:30:08 -04:00
Clark
e5bf04a407 Cleanup Recipient.externalPush to use RecipientId cache. 2023-05-17 15:30:08 -04:00
Clark
fe8b2cb761 Reduce db operations when updating thread snippet. 2023-05-17 15:30:08 -04:00
Clark
7c37f929a5 Go back to enqueuing thread update job. 2023-05-17 15:30:08 -04:00
Cody Henthorne
1b82d10b39 Squelch notifications in noisy groups and during large initial message processing. 2023-05-17 15:30:08 -04:00
Clark
6b6e2490e7 Add performance logging for message processing. 2023-05-17 15:30:08 -04:00
Greyson Parrelli
ebee54cf92 Add button to grow query area in Spinner. 2023-05-17 15:30:08 -04:00
Greyson Parrelli
b7acfb0dcc Add a better editor to Spinner. 2023-05-17 15:30:08 -04:00
Greyson Parrelli
65dab45582 Update string for sticker uninstall button label. 2023-05-17 15:30:08 -04:00
Greyson Parrelli
43086f9582 Fix more validation spots around unknown UUIDs.
This was a legacy path that got missed.
2023-05-17 15:30:08 -04:00
Clark
ed1aa74aff Reduce number of thread snippet updates from receipts. 2023-05-17 15:30:08 -04:00
Cody Henthorne
3ba128793a Display thread header in CFv2. 2023-05-17 15:30:08 -04:00
Clark
ffbbdc1576 Add PushProcessMessageJobV2 to reserved job queue. 2023-05-17 15:30:08 -04:00
Clark
6ff55cfff7 Reduce expensive group operations during message processing. 2023-05-17 15:29:31 -04:00
Clark
e9f1f781e1 Reduce number of db calls to getGroup. 2023-05-17 15:29:31 -04:00
Greyson Parrelli
6da36fe098 Deprecate the SyncMessage.pniIdentity field. 2023-05-17 15:29:31 -04:00
Greyson Parrelli
acb6510312 Switch to libsignal for PIN hashing. 2023-05-17 15:29:30 -04:00
Greyson Parrelli
13248506c5 Bump version to 6.20.5 2023-05-17 15:25:38 -04:00
Greyson Parrelli
fb9740f5c3 Updated language translations. 2023-05-17 15:25:16 -04:00
Nicholas Tinsley
61bf788f52 Tear down Bluetooth connection after voice memo recording. 2023-05-17 14:30:30 -04:00
Greyson Parrelli
fd116e0178 Bump version to 6.20.4 2023-05-15 21:29:08 -04:00
Greyson Parrelli
661d981231 Updated language translations. 2023-05-15 21:28:16 -04:00
Greyson Parrelli
6a701591af End transaction before handling db error. 2023-05-15 21:14:29 -04:00
Greyson Parrelli
02431c6ef4 Refactor array creation to a function. 2023-05-15 13:21:57 -04:00
Nicholas Tinsley
802b179880 Update license to AGPLv3. 2023-05-15 10:23:28 -04:00
Cody Henthorne
6c2104b84b Fix media not auto-downloading in groups bug. 2023-05-12 15:39:32 -04:00
Cody Henthorne
a3f432dc88 Bump version to 6.20.3 2023-05-12 14:14:43 -04:00
Cody Henthorne
7f4caedf40 Updated baseline profile. 2023-05-12 14:04:16 -04:00
Cody Henthorne
eebd06f0d8 Updated language translations. 2023-05-12 13:59:34 -04:00
Greyson Parrelli
0ea66b6bb0 Update apkdiff to ignore baseline profile. 2023-05-12 10:38:04 -04:00
Greyson Parrelli
084cdd7200 Fix de-duping migration when resolving date conflicts. 2023-05-12 09:45:02 -04:00
Greyson Parrelli
2eff9e0230 Update default conflict method to be 'ignore'. 2023-05-12 09:26:44 -04:00
Greyson Parrelli
6615bc4a2a Fix param on sync message. 2023-05-11 18:47:04 -04:00
Greyson Parrelli
387f18be98 Avoid some 401 errors during story sends. 2023-05-11 16:54:57 -04:00
Cody Henthorne
3b5a3eccfe Bump version to 6.20.2 2023-05-11 15:34:49 -04:00
Cody Henthorne
36083a8bd9 Updated baseline profile. 2023-05-11 15:28:56 -04:00
Cody Henthorne
7e16825bf4 Updated language translations. 2023-05-11 15:26:01 -04:00
Greyson Parrelli
9ba2724d0a Fix V190 database migration with a new migration. 2023-05-11 15:21:22 -04:00
Greyson Parrelli
7866851f5d Temporarily give up on the V190 migration. 2023-05-11 11:17:01 -04:00
Greyson Parrelli
c938035ec1 Improve error handling around unknown UUIDs. 2023-05-11 11:15:13 -04:00
Nicholas Tinsley
965fdc5e9b Fix linked devices reminder appearing all the time. 2023-05-11 11:08:06 -04:00
Nicholas Tinsley
eab932b4a0 Fix parsing for registration 502 errors. 2023-05-11 10:58:04 -04:00
Cody Henthorne
27a24262c8 Bump version to 6.20.1 2023-05-10 16:18:21 -04:00
Cody Henthorne
31584de225 Updated baseline profile. 2023-05-10 16:10:41 -04:00
Cody Henthorne
6b2f90019a Updated language translations. 2023-05-10 16:05:55 -04:00
Nicholas
208147db9e Make change number error notifications more prominent. 2023-05-10 15:59:26 -04:00
Nicholas
e4f70fa4fe Delegate to system to handle rotation in video call PiP. 2023-05-10 15:59:26 -04:00
Cody Henthorne
4d09abd0d3 Fix group state loss during concurrent updates.
Storage sync and a message process can both attempt to create a group at the same time. Message processing caches the local group state and performs networking which allows the cached state to become stale. The state was being used to decide to call create instead of update and the create would fail silently as the group record already exists. This would cause state to not be persisted and result in odd double events.
2023-05-10 15:59:26 -04:00
Greyson Parrelli
1304f4dc39 Update apkdiff.py to return non-zero exit codes on mismatch. 2023-05-10 15:59:26 -04:00
Nicholas Tinsley
ac027d9267 Store linked devices presence in separate key than reminder. 2023-05-10 15:59:26 -04:00
Cody Henthorne
0b6d343616 Fix tint on call rows in chat settings. 2023-05-10 15:59:26 -04:00
Nicholas
92e8f125f9 Improve nullability for setting communication devices. 2023-05-10 15:59:26 -04:00
Greyson Parrelli
bef15482af Add unique index on message (sentTimestamp, author, thread). 2023-05-10 15:59:26 -04:00
Greyson Parrelli
93d78b3b2e Improve conditional logic around prekey refresh schedule. 2023-05-09 15:35:48 -04:00
Cody Henthorne
d38b7deeeb Bump version to 6.20.0 2023-05-09 14:17:49 -04:00
Cody Henthorne
b656e1dd0a Updated baseline profile. 2023-05-09 13:53:50 -04:00
Cody Henthorne
f1cec895b9 Updated language translations. 2023-05-09 13:48:06 -04:00
Greyson Parrelli
9bf6922d97 Ensure users have a service identifier before sending receipts. 2023-05-09 13:41:28 -04:00
Greyson Parrelli
41fc4096e4 Fix migration crash if user is unregistered. 2023-05-09 13:41:28 -04:00
Nicholas
e46564cb7e Add jitter to backup scheduling. 2023-05-09 13:41:28 -04:00
Clark
77751c1d28 Add read through cache for thread id. 2023-05-09 13:41:28 -04:00
Clark
054a1e4017 Reduce number of db calls when processing data messages. 2023-05-09 13:41:28 -04:00
Greyson Parrelli
da27d74111 Improve rendering of nulls in Spinner results. 2023-05-09 13:41:28 -04:00
Greyson Parrelli
970278228d Replace 'audio call' with 'voice call'. 2023-05-09 13:41:28 -04:00
Greyson Parrelli
ee3f2d62cf Add extra guard against inserting unnecessary error messages. 2023-05-09 13:41:28 -04:00
Greyson Parrelli
ec6d5031cf Made table headers in Spinner sticky. 2023-05-09 13:41:28 -04:00
Greyson Parrelli
80f338c3af Increase Spinner query history to 25 items. 2023-05-09 13:41:28 -04:00
Cody Henthorne
268c9a1c26 Fix conversation list not updating with current state. 2023-05-09 13:41:27 -04:00
Jon Chambers
e8e01b5965 Remove vestiges of CDS "classic". 2023-05-09 13:41:27 -04:00
Alex Hart
23a042b667 Fix translatable value. 2023-05-09 13:41:27 -04:00
Clark
c2c1537858 Disable interactions while user is unregistered or expired. 2023-05-09 13:41:27 -04:00
Cody Henthorne
65d5f4c426 Add ConversationAdapterV2. 2023-05-09 13:41:27 -04:00
Alex Hart
a1eb33b1f6 CallLinkTable migration to add necessary columns for integration. 2023-05-09 13:41:27 -04:00
Alex Hart
9eadd92d05 Ensure websocket state changes are handled on main thread. 2023-05-05 13:55:45 -03:00
Nicholas
b0e1294584 Only show linked devices reminder if devices previously linked. 2023-05-05 12:49:18 -03:00
Nicholas
f1fd29a477 Use Bluetooth headset mic to record voice notes. 2023-05-05 12:49:18 -03:00
Cody Henthorne
fc9a6b98d1 Add sync group sent text message processing test. 2023-05-05 12:49:18 -03:00
Alex Hart
305f6c610c Fix bad formatting in CallTable. 2023-05-05 12:49:16 -03:00
Alex Hart
66f4732db5 Reimplement MessageRequestViewModel for CFV2. 2023-05-05 12:48:53 -03:00
Nicholas
ccdfa546b4 Prevent launching multiple audio device dialogs during call. 2023-05-05 12:48:53 -03:00
Greyson Parrelli
855e194baa Add initial username link screen + QR code generation. 2023-05-05 12:48:53 -03:00
Alex Hart
e0c06615fb Upgrade to libsignal 0.23.1 2023-05-05 12:48:53 -03:00
Alex Hart
03a4809866 Add local directory to .gitignore. 2023-05-05 12:48:53 -03:00
Clark
0aa7bd6a4e Reorder security provider initialization. 2023-05-05 12:48:53 -03:00
Greyson Parrelli
78b530f8b8 Show toast to internal users for invalid messages. 2023-05-05 12:48:53 -03:00
Nicholas Tinsley
ace47c61b1 Update top-level LICENSE file to AGPL 2023-05-05 12:48:53 -03:00
Clark
29796f51d7 Try calling startForeground in onCreate. 2023-05-05 12:48:53 -03:00
Alex Hart
4d2ce7a2be Batch call event syncs.
Co-authored-by: Greyson Parrelli <greyson@signal.org>
2023-05-05 12:48:53 -03:00
Alex Hart
8dc45263cd Add requestLayout when textfields are updated.
These fields appear to not resize themselves correctly and there appears
to be a possible race where they can end up wrongly ellipsizing
themselves.
2023-05-05 12:48:53 -03:00
Greyson Parrelli
d8d95d8efe Update SQLCipher to 4.5.4-S1 2023-05-05 12:48:53 -03:00
Greyson Parrelli
f081591354 Add Android 14 improvements for dynamic shortcuts.
Closes #12923
Co-authored-by: Yuichi Araki <yaraki@google.com>
2023-05-05 12:48:53 -03:00
Nicholas
35e96fecdb Launch the MediaPreviewV2Activity in its own stack from Bubbles. 2023-05-05 12:48:53 -03:00
Nicholas
841fbfa7ee Improve error handling for external failures during registration.
Addresses #10711 and #12927.
2023-05-05 12:48:53 -03:00
Nicholas
89397ae7cc Picture-in-Picture call improvements. 2023-05-05 12:48:53 -03:00
Alex Hart
6c57c2ac2a Fix crash from external share. 2023-05-05 12:48:53 -03:00
Jim Gustafson
40663eb52f Update to RingRTC v2.26.3 2023-05-05 12:48:53 -03:00
Greyson Parrelli
90c9cc17b9 Handle unregistered responses in more locations.
There were some send jobs where we knew users were unregistered, but we
weren't marking them as such in the DB.
2023-05-05 12:48:53 -03:00
Nicholas
125c4f43cf Make audio device button directly toggle when only two devices are present. 2023-05-05 12:48:53 -03:00
Greyson Parrelli
634e4abcc1 Use the word 'chat' instead of 'conversation'. 2023-05-05 12:48:53 -03:00
Greyson Parrelli
a5431330d1 Ensure user has a serviceId/e164 before attempting a read receipt. 2023-05-05 12:48:53 -03:00
Alex Hart
30fc6d94c5 Flesh out event listeners and add load sequencing to CFV2. 2023-05-05 12:48:53 -03:00
Alex Hart
694d8f1984 Add scroll buttons to CFV2. 2023-05-05 12:48:53 -03:00
Alex Hart
bff8fc8230 Add call link details screen scaffolding. 2023-05-05 12:48:53 -03:00
Alex Hart
5f7414e84c Bump version to 6.19.8 2023-05-05 12:47:12 -03:00
Greyson Parrelli
8c707555f2 Fix bad migration state that could happen during a device transfer. 2023-05-05 12:42:31 -03:00
Greyson Parrelli
63ce2de3bf Add some more missing indexes for foreign keys and create test. 2023-05-05 09:27:40 -04:00
Alex Hart
5e86cca277 Ensure call events are reverse-chron by timestamp. 2023-05-05 10:12:58 -03:00
Alex Hart
4bbed2601c Bump version to 6.19.7 2023-05-04 13:57:20 -03:00
Alex Hart
e0bda8cf53 Fix group call ring state for some calls. 2023-05-04 13:31:09 -03:00
Greyson Parrelli
a1c807d65b Force usage of best index for conversation query. 2023-05-04 09:35:45 -04:00
Greyson Parrelli
77f5c290cc Add 'if not exists' to index migration. 2023-05-04 09:33:29 -04:00
Alex Hart
4294b446f3 Hide call events from hidden and blocked contacts. 2023-05-04 10:02:15 -03:00
Alex Hart
664e8e5526 Bump version to 6.19.6 2023-05-03 13:23:13 -03:00
Alex Hart
264ade3db9 Updated baseline profile. 2023-05-03 13:16:07 -03:00
Alex Hart
1a6e5e9e2b Updated language translations. 2023-05-03 13:11:34 -03:00
Greyson Parrelli
6aa4bb549a Add database indices to improve message delete performance. 2023-05-03 09:59:47 -04:00
Alex Hart
dd76909f02 Bump version to 6.19.5 2023-05-02 14:51:17 -03:00
Alex Hart
a454adece8 Updated baseline profile. 2023-05-02 14:50:48 -03:00
Alex Hart
f2adc4d283 Updated language translations. 2023-05-02 14:46:00 -03:00
Clark
6af9835f74 Fix sync edit message group validation. 2023-05-02 12:41:33 -04:00
Alex Hart
b823baa387 Animate when entering and exiting call log multiselect. 2023-05-02 13:24:08 -03:00
Alex Hart
6791d2d46e Add animation when switching from chats to calls. 2023-05-02 13:10:39 -03:00
Alex Hart
3c343af562 Fix dancing call icon when a new message is recieved. 2023-05-02 13:10:23 -03:00
Greyson Parrelli
fe9ed4c5f7 Attempt to repair local recipient state in the V185 migration. 2023-05-02 10:04:57 -04:00
Alex Hart
7374e7ee23 Fix PiP crash on devices that lie about support.
Fixes #12924
2023-05-02 09:51:45 -03:00
Alex Hart
3c9c0e244a Bump version to 6.19.4 2023-05-01 15:31:50 -03:00
Alex Hart
64c219b02d Updated baseline profile. 2023-05-01 15:31:15 -03:00
Alex Hart
7e68f8faf2 Updated language translations. 2023-05-01 15:26:06 -03:00
Greyson Parrelli
c868098042 Fix 'Sent from' section in message details. 2023-05-01 13:13:13 -04:00
Greyson Parrelli
b3c0cda2be Fix rendering of some update message types. 2023-05-01 10:09:57 -04:00
Nicholas
2176d1f3df Bump version to 6.19.3 2023-04-25 16:19:26 -04:00
Nicholas
979b9859af Updated baseline profile. 2023-04-25 16:08:42 -04:00
Nicholas
a9ce4a1aed Updated language translations. 2023-04-25 16:03:22 -04:00
Greyson Parrelli
a01fb7ff1c Fix foreign key constraint issues with backup restore. 2023-04-25 15:52:09 -04:00
Alex Hart
0e631508b2 Fix call log multiselect deletions. 2023-04-25 16:42:33 -03:00
Greyson Parrelli
eb9915d445 Fix FTS rebuild retry. 2023-04-25 14:45:39 -04:00
Alex Hart
eedf7d4280 Update threads on call event message deletes. 2023-04-25 14:49:49 -03:00
Cody Henthorne
aaca487b8f Improve performance around marking messages read. 2023-04-25 11:29:45 -04:00
Alex Hart
a7d6c0f25c Prevent filtering when in multiselect mode. 2023-04-25 11:46:37 -03:00
Nicholas
158a250357 Force WiFi-to-cellular popup to use light mode text color. 2023-04-25 10:03:34 -04:00
Cody Henthorne
b9d7d19dea Fix multiple issues with rendering spoilers as story captions. 2023-04-25 09:51:11 -04:00
Alex Hart
a837f86999 Disable scrolling when context menu is open. 2023-04-25 10:36:27 -03:00
Cody Henthorne
a0e4b1aaf9 Fix weird highlight shown after revealing a spoiler. 2023-04-24 22:45:06 -04:00
Cody Henthorne
4d10be2aa5 Fix spoiler reveal in full screen media viewer. 2023-04-24 21:27:11 -04:00
Nicholas
8c74ae2fec Bump version to 6.19.2 2023-04-24 19:21:41 -04:00
Nicholas
7773341546 Updated baseline profile. 2023-04-24 19:20:42 -04:00
Nicholas
50747aa0d0 Updated language translations. 2023-04-24 19:17:10 -04:00
Greyson Parrelli
353434e1ef Possible fix for SQLite error. 2023-04-24 19:12:11 -04:00
Nicholas
d70213e031 Mute the camera if Signal loses focus.
Addresses #12754.
2023-04-24 19:12:10 -04:00
Cody Henthorne
763e14f25f Do not reveal spoilers in quotes. 2023-04-24 19:12:10 -04:00
Cody Henthorne
806e81743c Fix spoiler rendering in conversation list. 2023-04-24 19:12:10 -04:00
Clark
b0b2b02a49 Fix draft edit messages not being cleared. 2023-04-24 19:12:10 -04:00
Clark Chen
5acf442279 Exit edit message when quoting a message. 2023-04-24 19:12:10 -04:00
Clark
324e83489e Fix edit message with media. 2023-04-24 19:12:10 -04:00
Clark
8505530547 Fix link preview thumbnail not matching when receiving quote of edited message. 2023-04-24 09:43:41 -07:00
Alex Hart
43565d3414 Call icon and toast restructure. 2023-04-24 13:41:20 -03:00
Clark
9cb8fc8ef5 Fix edit message when editing message sent to self. 2023-04-24 08:19:07 -07:00
Greyson Parrelli
a11c34d1f6 Fix migration crash for unregistered users. 2023-04-24 10:14:43 -04:00
Alex Hart
03ef778dee Hide call button when entering action mode. 2023-04-24 10:40:48 -03:00
Alex Hart
fa61fa301c Fix incoming call notification on locked screen. 2023-04-24 10:16:03 -03:00
Cody Henthorne
b4f6177e87 Bump version to 6.19.1 2023-04-21 15:43:00 -04:00
Cody Henthorne
f5c16bf824 Updated baseline profile. 2023-04-21 15:33:37 -04:00
Cody Henthorne
c911dfa9f2 Updated language translations. 2023-04-21 15:30:24 -04:00
Clark Chen
297eb55c61 Correctly hide edit message from message selection menu. 2023-04-21 14:34:00 -04:00
Cody Henthorne
4fce7cc3cc Use distinct timestamp for sync message expire timer updates. 2023-04-21 13:44:34 -04:00
Cody Henthorne
1d793de213 Fix notification profile UI state bug and crash. 2023-04-21 13:37:03 -04:00
Alex Hart
211d79d14d Display caller avatar when showing CallStyle notification. 2023-04-21 14:34:47 -03:00
Cody Henthorne
f14bce9849 Fix unable to verify signed group change warning. 2023-04-21 12:37:28 -04:00
Cody Henthorne
8baf07a11c Refine link preview domain restrictions. 2023-04-21 11:40:16 -04:00
Alex Hart
a917dace6e Rev calls tab flag. 2023-04-20 17:47:12 -03:00
Clark
b3fef6c31e Stop dropping sync edit sent messages. 2023-04-20 15:41:50 -04:00
Cody Henthorne
05b4fea3fc Bump version to 6.19.0 2023-04-20 15:26:27 -04:00
Cody Henthorne
ab1ad28377 Updated baseline profile. 2023-04-20 15:01:17 -04:00
Cody Henthorne
5beb90dc19 Updated language translations. 2023-04-20 14:56:40 -04:00
Alex Hart
9e2e345a3e Add fix for toggle position on reglock. 2023-04-20 13:50:12 -04:00
Alex Hart
70f774dce9 Slight reorg of call links package. 2023-04-20 13:50:12 -04:00
Cody Henthorne
203b16e5a9 Update copy/dialogs for registration flow. 2023-04-20 13:50:12 -04:00
Clark
b3974d6e64 Resolve ANRs from job manager blocking incoming message observer. 2023-04-20 13:50:12 -04:00
Alex Hart
dc153ff4e6 Add support for jumping to quoted messages in CFV2. 2023-04-20 13:50:12 -04:00
Clark
5ddd7cdb9e Add sync message support for edit message. 2023-04-20 13:50:12 -04:00
Clark
85787ba1df Fix race for edit message notifying listeners. 2023-04-20 13:50:12 -04:00
Alex Hart
560b2f7d6f Utilize CallStyle for incoming and ongoing calls. 2023-04-20 13:50:12 -04:00
Alex Hart
8260be4bff Upgrade Kotlin to 1.8.10 and Core-KTX to 1.10.0 2023-04-20 13:50:12 -04:00
Alex Hart
65e0fae3f4 Add CFV2 Scroll-to-position wiring. 2023-04-20 13:50:12 -04:00
Alex Hart
e32b81dc2a Add new tab icons. 2023-04-20 13:50:12 -04:00
Alex Hart
10bcadb26a Allow two lines in contact refresh row item summary.
Fixes #12894
2023-04-20 13:50:12 -04:00
Alex Hart
93228f4be7 Prevent sending stickers to stories. 2023-04-20 13:50:12 -04:00
Alex Hart
e6a4067a35 Fix disposable leak in ShareActivity. 2023-04-20 13:50:12 -04:00
Alex Hart
baf9cd0909 Fix refresh of toggle state when dialog is dismissed in several cases. 2023-04-20 13:50:12 -04:00
Alex Hart
9081230286 Add ScrollToPositionDelegate and install in calls log fragment. 2023-04-20 13:50:12 -04:00
Greyson Parrelli
6db71f4a39 Improve performance of finding message positions in chats. 2023-04-20 13:50:12 -04:00
Iñaqui
d628921e48 Proceed with a stun server fallback 2023-04-20 13:50:12 -04:00
Alex Hart
240f27fbad Add correct id to barrier in call log row. 2023-04-20 13:50:12 -04:00
Clark
23a3d78deb Merge v186 and v185 migrations. 2023-04-20 13:50:12 -04:00
Alex Hart
47ebcc0f05 Upgrade ConstraintLayout to 2.1.4 2023-04-20 13:50:12 -04:00
Greyson Parrelli
3264a0a795 Fix receipts for stories. 2023-04-20 13:50:12 -04:00
Greyson Parrelli
deeaf2ba2e Put guard against self-foreign-keys causing infinite recursion. 2023-04-20 13:50:12 -04:00
Greyson Parrelli
646f79be7d Fix DatabaseConsistencyTest. 2023-04-20 13:50:12 -04:00
Clark
07f6baf7c1 Add message editing feature. 2023-04-20 13:50:12 -04:00
Cody Henthorne
4f06a0d27c Improve performance from thread being updated to data available to render. 2023-04-20 13:50:12 -04:00
Alex Hart
9d17bf473c Add several callbacks to v2 convo fragment. 2023-04-20 13:50:11 -04:00
Greyson Parrelli
279ad7945e Move to defined from_recipient_id and to_recipient_id columns on message table. 2023-04-20 13:50:11 -04:00
Alex Hart
d079f85eca Add GiphyMp4 controller to V2 Conversation Fragment. 2023-04-20 13:50:11 -04:00
Greyson Parrelli
bd8060e533 Updated error string. 2023-04-20 13:50:11 -04:00
Alex Hart
0b21481539 Add toast when long pressing camera button and video is not supported. 2023-04-20 13:50:11 -04:00
Alex Hart
3090a8521c Merge V2 Conversation Fragment behind an internal setting. 2023-04-20 13:50:11 -04:00
Alex Hart
5959545ae9 Add call log search support for group names. 2023-04-20 13:50:11 -04:00
Clark
e7f8d36199 Fix multidevice contact sync update job reporting wrong content length. 2023-04-20 13:50:11 -04:00
Alex Hart
09cf8074aa Allow users to select a compact tab bar. 2023-04-20 13:50:11 -04:00
Cody Henthorne
06f19aa6cd Bump version to 6.18.4 2023-04-20 13:49:24 -04:00
Cody Henthorne
7a4f522144 Updated baseline profile. 2023-04-20 13:48:22 -04:00
Cody Henthorne
a2985830c5 Updated language translations. 2023-04-20 13:29:59 -04:00
Alex Hart
358da730cd Add e164 in service address in SyncMessageProcessor. 2023-04-20 11:01:33 -03:00
Cody Henthorne
2c25a0494b Bump version to 6.18.3 2023-04-17 14:09:43 -04:00
Cody Henthorne
16b384925e Updated baseline profile. 2023-04-17 14:05:11 -04:00
Cody Henthorne
fd03cc6319 Updated language translations. 2023-04-17 13:59:34 -04:00
Alex Hart
a9fc5c6331 Remove Google Pay check at launch. 2023-04-17 14:24:13 -03:00
Cody Henthorne
dad9d0b708 Fix missing e164 in message processing. 2023-04-17 13:02:37 -04:00
Cody Henthorne
e2ade166ec Fix bugs with switching and render wired headset state in calls. 2023-04-17 12:20:48 -04:00
Greyson Parrelli
e33a68b203 Bump version to 6.18.2 2023-04-14 16:39:30 -04:00
Greyson Parrelli
ad52062195 Updated language translations. 2023-04-14 16:39:30 -04:00
Greyson Parrelli
236e0faace Retry FTS rebuild 3 times.
Looks like we still have the old connection pull after a backup restore.
This gives it 3 chances.

Fixes #12902
2023-04-14 16:39:28 -04:00
Alex Hart
7b4d2661ad Add state check to onDismiss to prevent crash. 2023-04-14 16:30:07 -04:00
Alex Hart
03b5170b97 Add logging around in-thread image sizing. 2023-04-14 16:30:07 -04:00
Alex Hart
ea6d1a9381 Fix case where device lied about pip mode support. 2023-04-14 16:30:07 -04:00
Greyson Parrelli
aff5c2aa16 Bump version to 6.18.1 2023-04-13 17:35:13 -04:00
Greyson Parrelli
829abf169a Updated language translations. 2023-04-13 17:34:44 -04:00
Greyson Parrelli
56e008ea4f Add database consistency test, fix calling migration. 2023-04-13 17:26:26 -04:00
Alex Hart
2a16d8baed Mark all call events as read whenever we enter the calls tab. 2023-04-13 17:26:26 -04:00
Alex Hart
ee89629738 Fix missed group call label. 2023-04-13 17:26:26 -04:00
Alex Hart
3e63ac46b4 Ensure message deletion marks event deleted. 2023-04-13 17:26:26 -04:00
Alex Hart
c881c67f5e Fix child filtering. 2023-04-13 17:26:26 -04:00
Alex Hart
a3574292c6 Fix Call Log snap and ordering. 2023-04-13 17:18:59 -04:00
Alex Hart
94b308cecb Add logging around next/previous moves for story viewer. 2023-04-13 17:18:59 -04:00
Alex Hart
d3e83b12d9 Utilize SERIAL instead of BOUNDED executor when marking stories as viewed. 2023-04-13 17:18:59 -04:00
Alex Hart
d77555266b Prevent deleting call events with DELETION_TIMESTAMP set to 0. 2023-04-13 17:18:59 -04:00
Alex Hart
3451ac4504 Move LifecycleDisposable to core-util. 2023-04-13 17:18:59 -04:00
Greyson Parrelli
b51973f1d5 Bump version to 6.18.0 2023-04-12 16:59:33 -04:00
Greyson Parrelli
f568002e5c Updated language translations. 2023-04-12 16:58:53 -04:00
Greyson Parrelli
321cced323 Ignore broken story unit tests. 2023-04-12 16:58:42 -04:00
Alex Hart
4359336fd5 Move load-state into its own data-store. 2023-04-12 16:31:35 -04:00
Alex Hart
e8570c3680 Add call tab event grouping. 2023-04-12 16:31:35 -04:00
Alex Hart
fd1ff5e438 Extract post mark as read request body. 2023-04-12 16:31:35 -04:00
Clark
026d029614 Fix tapping too fast breaking my stories viewer. 2023-04-12 16:31:35 -04:00
Clark Chen
ef058a1644 Inline export account data feature flag. 2023-04-12 16:31:27 -04:00
Cody Henthorne
a35a167e7a Fix spoiler animation running after view is returned to cache. 2023-04-12 16:31:27 -04:00
Cody Henthorne
36ef36be61 Cleanup MessageContentProcessorTestV2. 2023-04-12 16:31:27 -04:00
Bernie Dolan
a874efee4e Update payments to 4.1.0 2023-04-12 16:31:27 -04:00
Cody Henthorne
07c32e2a35 Fallback back to Telephony create thread in MMS export on failure. 2023-04-12 16:31:27 -04:00
Cody Henthorne
055f4b09ee Add additional backup folder failure debug info. 2023-04-12 16:31:27 -04:00
Cody Henthorne
737b1c962a Retry backup verify on security exception. 2023-04-12 16:31:27 -04:00
Cody Henthorne
99ac2cb333 Allow spoiler paint to be tinted independently per renderer. 2023-04-12 16:31:27 -04:00
Alex Hart
a183057b32 Update call state icons and text. 2023-04-12 16:31:27 -04:00
Alex Hart
2883c16560 Extract 'invite to signal' into an InviteActions object. 2023-04-12 16:31:27 -04:00
Greyson Parrelli
d88534e71f Improve spoiler drawing performance. 2023-04-12 16:31:27 -04:00
Greyson Parrelli
a56e9e502e Move LeakCanary into its own variant. 2023-04-12 16:31:27 -04:00
Alex Hart
433e8266c9 Add stricter call row identification. 2023-04-12 16:31:27 -04:00
Alex Hart
490feb358c Update ConversationOptionsMenuProvider to utilize snapshot data class. 2023-04-12 16:31:19 -04:00
Clark
27e3c883c3 Update notification on profile name fetch or change. 2023-04-12 16:31:19 -04:00
Greyson Parrelli
71e2b8225a Debounce thread updates for incoming messages. 2023-04-12 16:31:19 -04:00
Greyson Parrelli
6d4906dfa8 Add microbenchmarks for message decryption. 2023-04-12 16:31:19 -04:00
Aaron Labiaga
0156e74f5a Improve transition to PiP mode.
Use setAutoEnterEnable to true for smooth transition to
Picture-in-Picture when in gestural navigation mode.

Closes #12878
2023-04-12 16:29:48 -04:00
Clark
c834cb6ff7 Fix design assumption invalidated crash in MediaPreviewAdapter. 2023-04-11 10:34:18 -04:00
Clark
48360d08d4 Integrate contact hiding with message requests. 2023-04-11 10:34:18 -04:00
Greyson Parrelli
74877b839e Bump version to 6.17.3 2023-04-11 10:33:28 -04:00
Greyson Parrelli
39e04bef17 Updated language translations. 2023-04-11 10:32:42 -04:00
Greyson Parrelli
c5af204de3 Prevent FK violation from bad decryption insert.
Fixes #12880
2023-04-11 10:02:00 -04:00
Greyson Parrelli
ca0dd03042 Allow websocket retries when proxy is set. 2023-04-11 10:01:37 -04:00
Greyson Parrelli
f8f70ed3e1 Bump version to 6.17.2 2023-04-10 11:40:29 -04:00
Greyson Parrelli
0510588a09 Updated language translations. 2023-04-10 11:35:03 -04:00
Alex Hart
1d8fc4b7fd Fix mic tinting in small call button resource. 2023-04-10 12:01:13 -03:00
Greyson Parrelli
b7f5333b39 Reduce message observer max background time to 2 minutes.
Seeing some increased battery usage issues. 5 minutes was probably too
high.
2023-04-10 09:45:57 -04:00
Greyson Parrelli
544121d035 Minimize lock window in IncomingMessageObserver.
In particular, this was done to avoid a possible deadlock that could
occur between the IncomingMessageObserver lock and the JobManager lock.
2023-04-10 09:42:33 -04:00
Greyson Parrelli
4da4de3b99 Hopeful fix for bluetooth selection issues. 2023-04-07 09:08:41 -04:00
Alex Hart
0b62c0346b Bump version to 6.17.1 2023-04-06 17:12:11 -03:00
Alex Hart
292956b18c Updated baseline profile. 2023-04-06 17:12:00 -03:00
Alex Hart
925f347050 Updated language translations. 2023-04-06 17:09:41 -03:00
Greyson Parrelli
ea150939cb Fix issue where audio selections weren't persisting in UI. 2023-04-06 14:35:46 -04:00
Clark
57f490e5db Fix link previews not being treated as media message. 2023-04-06 13:48:11 -04:00
Alex Hart
a141fdaf7d Move state check to audio handler thread. 2023-04-06 14:16:55 -03:00
Clark
16f1fbf583 Only trigger image edit drag after user moves at least 3 pixels. 2023-04-06 11:52:06 -04:00
Alex Hart
9d4e13cd08 Wrap hidePicker dismiss call in ISE catch. 2023-04-06 10:46:11 -03:00
Alex Hart
73a7063867 Fix toolbar state management and pip. 2023-04-06 10:42:02 -03:00
Alex Hart
cd5c253a78 Clarify logging around group ring state. 2023-04-06 10:31:37 -03:00
Alex Hart
372c6f6ba3 Bump version to 6.17.0 2023-04-05 16:47:03 -03:00
Alex Hart
d6f4a89326 Updated baseline profile. 2023-04-05 16:46:56 -03:00
Alex Hart
60905c7409 Updated language translations. 2023-04-05 16:44:15 -03:00
Clark Chen
f3490d07bf Fix settings icons for older API levels. 2023-04-05 16:40:23 -03:00
Alex Hart
b99855afbe Fix avatar photo editing. 2023-04-05 16:40:23 -03:00
Alex Hart
921c903190 Allow forwarding of contacts. 2023-04-05 16:40:23 -03:00
Alex Hart
9771b53c79 Add logging to where we mark StoryContent as Ready. 2023-04-05 16:40:23 -03:00
Nicholas
0ab5bbb240 Detect and recover from SCO interruptions. 2023-04-05 16:40:23 -03:00
Alex Hart
fef533f101 Upgrade CameraX to 1.2.2 2023-04-05 16:40:23 -03:00
Alex Hart
3399af5a96 Fix mic toggle state. 2023-04-05 16:40:23 -03:00
Alex Hart
022195508a Swap AlertDialog.Builder for MaterialAlertDialogBuilder. 2023-04-05 16:40:23 -03:00
Alex Hart
d3daaff6a4 Update collapsed toolbar state for group calling. 2023-04-05 16:40:23 -03:00
Nicholas
89a3c62637 Only deauthorize on identified connection. 2023-04-05 16:40:23 -03:00
Alex Hart
6da9db6d86 Allow MediaSelectionActivity to ignore uiMode configuration changes. 2023-04-05 16:40:23 -03:00
Alex Hart
c254b08e33 Add the join / return button to call log items. 2023-04-05 16:40:23 -03:00
Alex Hart
9d575650d1 Add create call link sheet. 2023-04-05 16:40:23 -03:00
Greyson Parrelli
d8ac5a390a Write to MSL before sending a sync message. 2023-04-05 16:40:23 -03:00
Alex Hart
60842a10ff Align donate for a friend duration with data from gift badge. 2023-04-05 16:40:23 -03:00
Alex Hart
1cab6f87a0 Remove extra rows from PhoneNumberPrivacySettingsFragment. 2023-04-05 16:40:23 -03:00
Nicholas
a0aeac767d New Android 12+ audio route picker for calls. 2023-04-05 16:40:23 -03:00
Greyson Parrelli
99bd8e82ca Do not process messages while change number is happening. 2023-04-05 16:40:23 -03:00
Greyson Parrelli
bbdf54097e Prevent certain types of circular job dependencies. 2023-04-05 16:40:23 -03:00
Greyson Parrelli
2a9576baf5 Convert FastJobStorage to kotlin. 2023-04-05 16:40:23 -03:00
Clark Chen
2ca4c2d1c1 Fix gradle verification for Mac/Windows. 2023-04-05 16:40:23 -03:00
Greyson Parrelli
3231f8302c Do not add dependencies on previous message sends if you have no media.
This was an accidental carry-over from the PushMediaSendJob ->
IndividualSendJob rename.

Previously, PushMediaSendJob basically always had media, so this check
wasn't needed. Now it rarely has media, so we have to add it.
2023-04-05 16:40:23 -03:00
Greyson Parrelli
e0be9b4ef5 Fix resend operation for sync messages.
We shouldn't be using sealed sender for any sync messages.
2023-04-05 16:40:23 -03:00
Clark
83b0963533 Add fix for m4a mapping to audio/mpeg. 2023-04-05 16:40:23 -03:00
Alex Hart
f9548dcffe Add support for group call disposition.
Co-authored-by: Cody Henthorne <cody@signal.org>
2023-04-05 16:40:23 -03:00
Greyson Parrelli
e94a84d4ec Fixed MessageProcessingPerformanceTest. 2023-04-05 16:40:23 -03:00
Greyson Parrelli
db5f8707ec Remove TracingExecutors. 2023-04-05 16:40:23 -03:00
Greyson Parrelli
0f15562a28 Rename ComposeFragment.SheetContent -> FragmentContent. 2023-04-05 16:40:23 -03:00
Greyson Parrelli
b300f223ba Update AGP to 7.4.2 2023-04-05 16:40:23 -03:00
Clark
ad9337021c Streamline export account data to not save to disk. 2023-04-04 12:16:45 -03:00
Greyson Parrelli
5e94c350ed Add dependency for kotlinx immutable collections. 2023-04-04 12:16:45 -03:00
Clark
666020c3dc Add learn more link to export account data. 2023-04-04 12:16:45 -03:00
Alex Hart
f249a6edd5 Add StatusBarColorNestedScrollConnection. 2023-04-04 12:16:45 -03:00
Cody Henthorne
2e45bd719a Add kotlin/proto level message processing. 2023-04-04 12:16:45 -03:00
Clark
28f27915c5 Add support for time stickers in image editor. 2023-04-04 12:16:45 -03:00
Alex Hart
08ebca501b Bump version to 6.16.2 2023-04-04 11:07:18 -03:00
Alex Hart
417cda1d38 Updated baseline profile. 2023-04-04 11:06:48 -03:00
Alex Hart
dd730f5fbf Updated language translations. 2023-04-04 11:01:17 -03:00
Nicholas Tinsley
77bb3702a9 Add more detail to 502 errors during registration. 2023-04-03 14:43:26 -04:00
Nicholas Tinsley
5046f58c6f Constrain end of code entry subheader. 2023-04-03 14:32:17 -04:00
Greyson Parrelli
d02f605874 De-pluralize some strings. 2023-03-31 15:01:11 -04:00
Nicholas
36a8c4d8ba Bump version to 6.16.1 2023-03-30 18:33:53 -04:00
Nicholas
25f0427585 Updated baseline profile. 2023-03-30 18:33:00 -04:00
Nicholas
5a501f4815 Updated language translations. 2023-03-30 18:27:30 -04:00
Greyson Parrelli
de0a37d356 Fix possible dangling thread records. 2023-03-30 17:17:10 -04:00
Clark
5c65d5435c Fix rtl hiding conversation title. 2023-03-30 16:29:08 -04:00
Greyson Parrelli
8d6a4c2888 Fix SKDM processing. 2023-03-30 15:48:53 -04:00
Nicholas
b4a7ffdc12 Bump version to 6.16.0 2023-03-29 14:18:55 -04:00
Nicholas
5dd10f6fcc Updated baseline profile. 2023-03-29 14:16:21 -04:00
Nicholas
e76b5007e0 Updated language translations. 2023-03-29 14:13:51 -04:00
Nicholas Tinsley
16e8f9633e fixup! Add benchmark for conversation open. 2023-03-29 12:44:27 -04:00
Clark Chen
cb4a45fea3 Fix empty name crash when fetching first alpha recipient row. 2023-03-29 10:55:00 -04:00
Cody Henthorne
0017b7af26 Ignore contact joined message when determining if we should apply universal disappearing messages. 2023-03-28 16:06:23 -04:00
Greyson Parrelli
5f645193e4 Create Buttons.ActionButton component. 2023-03-28 16:02:45 -04:00
Cody Henthorne
607a06d379 Enable scheduled backups regardless of API version. 2023-03-28 09:24:11 -04:00
Cody Henthorne
149955e07a Fix crash when searching and lowercase snippet differs from input snippet. 2023-03-27 21:16:36 -04:00
Greyson Parrelli
80b9e4e7ae Updated username education icons. 2023-03-27 13:05:38 -04:00
Greyson Parrelli
f02ac86e45 Updated strings for username education fragment. 2023-03-27 11:07:43 -04:00
elena
45e96f0efe Show character count when writing a payment note.
Close #12616
2023-03-27 09:47:30 -04:00
Cody Henthorne
06894d6a7e Fix formatting on long text messages. 2023-03-24 17:15:02 -04:00
Greyson Parrelli
b67dfe10d4 Add some accessibility labels for the camera screen. 2023-03-24 16:44:05 -04:00
Greyson Parrelli
b9b6a57e2c Fix possible username conflict in storage update. 2023-03-24 16:32:38 -04:00
Cody Henthorne
ba2d005b2a Fix search result styling for formatting and query highlighting. 2023-03-24 15:49:27 -04:00
Cody Henthorne
f53679f24a Fix spoiler rendering in quotes. 2023-03-24 15:49:27 -04:00
Cody Henthorne
7eb00e41a2 Fix rendering of links and mentions covered by spoilers. 2023-03-24 15:49:26 -04:00
Jim Gustafson
168e37c3fc Update to RingRTC v2.26.1 2023-03-24 15:49:26 -04:00
Clark
98438ff8e4 Update async layout inflater to fix AppCompat views. 2023-03-24 15:49:26 -04:00
Clark
d6a9ed1a8d Add setting for requesting user account data. 2023-03-24 15:49:26 -04:00
Jim Gustafson
b194c0e84b Update to RingRTC v2.26.0 2023-03-24 15:49:26 -04:00
Nicholas
ed67e7ac04 Ignore smartwatch as possible headset. 2023-03-24 15:49:26 -04:00
Cody Henthorne
43cd647036 Add spoiler format style. 2023-03-24 15:49:26 -04:00
Greyson Parrelli
5d6889786c Bump version to 6.15.3 2023-03-24 14:39:52 -04:00
Greyson Parrelli
53d4e5c4d1 Updated language translations. 2023-03-24 14:39:52 -04:00
Greyson Parrelli
87918da943 Shorten lifespan of buffered store. 2023-03-24 14:39:51 -04:00
Greyson Parrelli
5914a4d1cf Improved logging around backup export. 2023-03-24 14:39:51 -04:00
Alex Hart
351baa4135 Update content descriptions for call toggles. 2023-03-24 14:39:51 -04:00
Alex Hart
1a71e1a5ae Fix cases where first letter is not an integer or character. 2023-03-24 14:39:51 -04:00
Alex Hart
3ce68a7df8 Allow toggle to be manually slid on preference screens. 2023-03-24 14:39:51 -04:00
Alex Hart
e83c2f1e05 Fix bottom row overlap in group creation / add activities. 2023-03-24 09:47:54 -03:00
Alex Hart
684e53402e Close keyboard after successful profile save. 2023-03-24 09:40:59 -03:00
Greyson Parrelli
db1853f775 Fix timestamp logs on call messages. 2023-03-23 18:58:49 -04:00
Greyson Parrelli
aad835323b Bump version to 6.15.2 2023-03-23 18:22:09 -04:00
Greyson Parrelli
d6f6633c73 Updated language translations. 2023-03-23 18:22:09 -04:00
Greyson Parrelli
76984ab042 Improve logging around message sending.
There were some message types where we didn't log the timestamp.
The timestamp is important for debugging issues (let's us locate an
error more precisely in the logs).
2023-03-23 18:08:39 -04:00
Greyson Parrelli
d58c4ef439 Unify locks in protocol stores. 2023-03-23 15:37:45 -04:00
Alex Hart
2763cfe6f4 Reintroduce labels for incoming call screen. 2023-03-23 16:10:25 -03:00
Greyson Parrelli
454e9a99fc Fix possible NPE in ConversationListFragment. 2023-03-23 14:41:40 -04:00
Alex Hart
aeb250cae1 Fix ISK minimum currency precision. 2023-03-23 14:41:25 -03:00
Greyson Parrelli
34367b4e70 Bump version to 6.15.1 2023-03-23 13:34:32 -04:00
Greyson Parrelli
451537d320 Updated language translations. 2023-03-23 13:34:32 -04:00
Greyson Parrelli
53d4825e12 Fully rebuild FTS after a backup restore. 2023-03-23 13:34:32 -04:00
Greyson Parrelli
24ee4a869f Fix empty state incorrectly showing in search. 2023-03-23 13:32:51 -04:00
Alex Hart
6ae3fb49e0 Add transitions to call state pill. 2023-03-23 13:32:51 -04:00
Alex Hart
8f9713a2c0 Add new call toast and remove call button labels. 2023-03-23 13:32:51 -04:00
Alex Hart
7a2ad37333 Add proper tinting to new chat icons. 2023-03-23 13:32:51 -04:00
Alex Hart
2509d1be73 Fix text alignment and padding on onboarding cards. 2023-03-23 13:32:51 -04:00
Alex Hart
19f4073068 Fix listener behavior for manual directory refresh invocation. 2023-03-23 13:32:51 -04:00
Alex Hart
fd612525a1 Fix touch interactions with MaterialSwitch in preferences. 2023-03-23 13:32:39 -04:00
Greyson Parrelli
631b428a84 Fix log statement.
We were printing out the envelope deviceId, which isn't populated with
sealed sender and was always 0. Should have used the one from the
decryption result.
2023-03-23 13:26:06 -04:00
Greyson Parrelli
09cd581cf4 Bump version to 6.15.0 2023-03-22 14:30:28 -04:00
Greyson Parrelli
fc1ea458f7 Updated language translations. 2023-03-22 14:28:36 -04:00
Clark
247edce7b0 Catch exceptions in share repository for blob provider IO exceptions. 2023-03-22 14:28:36 -04:00
Alex Hart
57a2a32c71 Update call button iconography and colours. 2023-03-22 14:28:36 -04:00
Clark
d9c1ecab9b Fix precaching of conversation list items. 2023-03-22 14:28:36 -04:00
Alex Hart
c70f1f5d75 Fix call screen transition. 2023-03-22 14:28:36 -04:00
Alex Hart
c26cc56f20 Fix bottom bar state handling and active state when menu is open. 2023-03-22 14:28:36 -04:00
Nicholas Tinsley
ca21ab667a Force LTR layout direction on NumericKeyboardView. 2023-03-22 14:28:36 -04:00
Clark
e2ae0063a5 Fix send button disappearing for voice drafts. 2023-03-22 14:28:36 -04:00
Alex Hart
eb150d9a15 Update SwitchMaterial to the new MaterialSwitch. 2023-03-22 14:28:36 -04:00
Cody Henthorne
ee48e6c347 Add sync message handling and stop formatting behavior. 2023-03-22 14:28:36 -04:00
Nicholas
cedf512726 Fix PanicKit for PIN lock.
Fixes #12816.
2023-03-22 14:28:10 -04:00
Clark
2256c8591a Add special audio recording sample rate for Xiaomi Mi 9T. 2023-03-22 14:28:10 -04:00
Alex Hart
1056adb591 Move distribution type operation into ConversationViewModel. 2023-03-22 14:28:10 -04:00
Alex Hart
53716019b6 Remove QuoteRestorationTask in favour of using DraftViewModel to resolve it. 2023-03-22 14:28:10 -04:00
Alex Hart
30f6faf3d7 Move mute handling into ConversationViewModel. 2023-03-22 14:28:10 -04:00
Alex Hart
2a43ffad4f Extract ConversationParentFragment Options Menu into a MenuProvider. 2023-03-22 14:28:10 -04:00
Alex Hart
f9ed5c4d03 Correct some icon tinting. 2023-03-22 14:28:10 -04:00
Cody Henthorne
25028e0e6f Add additional text formatting support. 2023-03-22 14:28:10 -04:00
Alex Hart
1c3636eedd Add undo-ability to call tab deletion. 2023-03-22 14:28:10 -04:00
Alex Hart
4d735d23b6 Remove unnecessary method calls in options menu code. 2023-03-22 14:28:10 -04:00
Greyson Parrelli
834d0a1cee Trigger an automatic session reset after failing to send a retry receipt. 2023-03-22 14:28:09 -04:00
Alex Hart
166e555d32 Kill two unused classes. 2023-03-22 14:28:09 -04:00
Greyson Parrelli
7f963d7628 Keep protocol error logs longer. 2023-03-22 14:28:09 -04:00
Alex Hart
cebe600014 Update bottom bar to support just calls and chats. 2023-03-22 14:28:09 -04:00
Alex Hart
5c688289a5 Ensure we do not stage shared element transition view when opening media from a bubble. 2023-03-22 14:28:09 -04:00
Greyson Parrelli
bf611f3a56 Fix potential NPE in SNC dialog. 2023-03-22 14:28:09 -04:00
Clark
150c42c590 Add notification for failed story messages. 2023-03-22 14:28:09 -04:00
Clark
069b707d9d Add dark mode for location picker. 2023-03-22 14:28:09 -04:00
Alex Hart
8c0d979abd Add call tab bottom bar. 2023-03-22 14:28:09 -04:00
Alex Hart
545f1fa5a4 Add call tab info screen. 2023-03-22 14:28:09 -04:00
Greyson Parrelli
49a814abef Show blocked users as 'skipped' when sending to curated story list. 2023-03-22 14:28:09 -04:00
Clark
17fc0dc0a1 Add indicator and story ring for stories in chat selection. 2023-03-22 14:28:09 -04:00
Greyson Parrelli
7c8de901f1 Store Job data as bytes. 2023-03-22 14:28:09 -04:00
Alex Hart
b5af581205 Set proper filter labeling on call tab. 2023-03-22 14:28:09 -04:00
Alex Hart
de73744432 Add new symbols for call tab. 2023-03-22 14:28:09 -04:00
Alex Hart
ce3770a0fb Add new call screen for calls tab. 2023-03-22 14:28:09 -04:00
Greyson Parrelli
1210b2af0f Some additional decryption perf improvements. 2023-03-22 14:28:09 -04:00
Greyson Parrelli
c6861f1778 Add support for the ManifestRecord.sourceDevice field. 2023-03-22 14:28:09 -04:00
Clark
906dd5cb40 Drop link preview thumbnail from forward if URI isn't present. 2023-03-22 14:27:59 -04:00
Clark
97b349b0de Add benchmark for conversation open. 2023-03-20 17:39:09 -04:00
Clark
f3b830ae20 Fix dark mode for compose bottom sheets. 2023-03-20 17:39:09 -04:00
Greyson Parrelli
7d7e6e5013 Update SQLCipher to 4.5.3-FTS-S3 2023-03-20 17:39:09 -04:00
Alex Hart
8ca596580c Add info action wiring in calls tab. 2023-03-20 17:39:09 -04:00
Alex Hart
7521520b26 Ensure scrolling properly highlights action bar in calls tab. 2023-03-20 17:39:09 -04:00
Alex Hart
18554170f2 Update call tab to display unread missed call count. 2023-03-20 17:39:09 -04:00
Alex Hart
cd5a3768eb Fix back handling between tabs. 2023-03-20 17:39:09 -04:00
Greyson Parrelli
cf64f06c36 Add a new test case for recipient merging. 2023-03-20 17:39:09 -04:00
Alex Hart
88de0f21e7 Add initial implementation of calls tab behind a feature flag. 2023-03-20 17:39:09 -04:00
Greyson Parrelli
d1373d2767 Remove queue drained constraint from receipt jobs. 2023-03-20 17:39:09 -04:00
Greyson Parrelli
baece9823b Remove log when enqueuing job within a transaction.
Found the bug I put the logging in for, and now this log happens way to
much after the decryption batching.
2023-03-20 17:39:09 -04:00
Greyson Parrelli
e18b2d263c Fix rendering of story replies in quote thread view. 2023-03-20 17:39:09 -04:00
Greyson Parrelli
d12830cb66 Add language support for Uyghur. 2023-03-20 17:39:09 -04:00
Cody Henthorne
59141bc6a4 Improve delete thread performance. 2023-03-20 17:39:09 -04:00
Greyson Parrelli
431e366e76 Add in possible recovery for DB error handler.
A bad FTS index can result in the corruption handler being triggered.
We can attempt to rebuild it to see if that helps.
2023-03-20 17:39:09 -04:00
Nicholas
66cb2a04c3 Rename properties of AccountAttributes. 2023-03-20 17:39:09 -04:00
Greyson Parrelli
90cc672c37 Convert MessageTable to kotlin. 2023-03-20 17:39:09 -04:00
Clark
c2a76c4313 Convert ConversationTitleView to a ConstraintLayout. 2023-03-20 17:39:09 -04:00
Greyson Parrelli
ee685936c5 Updated MessageProcessingPerformanceTest to use websocket injection. 2023-03-20 17:39:09 -04:00
Alex Hart
a7bca89889 Perform username deletion if no local name is set. 2023-03-20 17:39:09 -04:00
Clark
39f5aebbec Add support for scheduling media to multiple contacts. 2023-03-20 17:39:09 -04:00
Greyson Parrelli
35571e7ab2 Added another RecipientTable.getAndPossiblyMerge test case. 2023-03-20 17:39:09 -04:00
Clark
ed2d6ea903 Only setup mock data once for baseline profiles and benchmarks. 2023-03-20 17:39:09 -04:00
Alex Hart
e1e117ce73 Increase logging around username synchronization. 2023-03-20 17:39:09 -04:00
Greyson Parrelli
894095414a Perform message decryptions in batches. 2023-03-20 17:39:09 -04:00
Clark
04baa7925f Add support for baseline profiles. 2023-03-20 17:39:08 -04:00
Clark
79a062c838 Introduce thread priorities for threads and handlerthreads. 2023-03-20 17:39:08 -04:00
Greyson Parrelli
2cef06cd6e Bump version to 6.14.5 2023-03-20 17:37:51 -04:00
Greyson Parrelli
af4b98f424 Updated language translations. 2023-03-20 17:37:51 -04:00
Greyson Parrelli
cd66ba60e3 Disable view precaching of chat list to fix selection checkmark bug. 2023-03-20 17:37:49 -04:00
Greyson Parrelli
2d2a1049a4 Make onboarding card close button background borderless. 2023-03-20 17:37:24 -04:00
Greyson Parrelli
03aa6a1d61 Fix potential crash when starting IncomingMessageObserver service. 2023-03-20 17:37:24 -04:00
Greyson Parrelli
6c6d4e801f Fix crash when starting multiple audio records. 2023-03-20 17:37:24 -04:00
Cody Henthorne
a6d7b0c7bf Fix crash in multishare flow. 2023-03-20 17:37:24 -04:00
Cody Henthorne
f3c6f2e3c5 Bump version to 6.14.4 2023-03-15 19:55:20 -04:00
Cody Henthorne
4dc5ada717 Updated language translations. 2023-03-15 19:37:09 -04:00
Nicholas Tinsley
c01d542ec2 Better handling of push timeouts during registration. 2023-03-15 17:34:40 -04:00
Cody Henthorne
af7987d743 Bump version to 6.14.3 2023-03-14 16:42:02 -04:00
Cody Henthorne
37a7516b7e Updated language translations. 2023-03-14 15:08:53 -04:00
Cody Henthorne
dae0559568 Do not crash when backup process encounters an unexpected security exception. 2023-03-14 11:13:17 -04:00
Cody Henthorne
904817b498 Fix payment reaction notification. 2023-03-13 10:08:39 -04:00
Nicholas
9087f427a5 If push challenge times out, don't try again. 2023-03-13 09:50:54 -04:00
Cody Henthorne
f24d82bf04 Attempt to fix scheduled backups again. 2023-03-13 09:19:54 -04:00
Alex Hart
10e55765c1 Bump version to 6.14.2 2023-03-10 15:21:32 -04:00
Alex Hart
f205fece67 Updated language translations. 2023-03-10 15:17:36 -04:00
Nicholas Tinsley
6fb3167157 Don't reset session on return from captcha. 2023-03-10 13:49:04 -05:00
Nicholas
f22daccde6 Support pasting in verification code view. 2023-03-10 13:00:34 -05:00
Alex Hart
643d96a896 Bump version to 6.14.1 2023-03-08 15:53:23 -04:00
Alex Hart
04664d34e4 Updated language translations. 2023-03-08 15:51:32 -04:00
Alex Hart
7fd5b72204 Allow nullability of Intent parameter in converted services. 2023-03-08 15:44:47 -04:00
Alex Hart
fd3a509231 Bump version to 6.14.0 2023-03-08 15:16:41 -04:00
Alex Hart
9b672a520a Updated language translations. 2023-03-08 15:12:21 -04:00
Alex Hart
2d3e8ef31c Replace CardView usages with MaterialCardView. 2023-03-08 15:06:50 -04:00
Cody Henthorne
f1c2ee9b32 Stabilize message processing tests and add inline decryption timings. 2023-03-08 15:06:50 -04:00
Alex Hart
68a0cb40a6 Update several AndroidX libraries.
activity -> 1.6.1
appcompat -> 1.6.1
fragment -> 1.5.5
navigation -> 2.5.3
core-ktx -> 1.9.0
safe-args -> 2.5.3
2023-03-08 15:06:50 -04:00
Alex Hart
68a50798f2 Update phone number listing rules. 2023-03-08 15:06:50 -04:00
Alex Hart
73151e8ff6 Upgrade core-ktx to 1.8.0 2023-03-08 15:06:50 -04:00
Alex Hart
b7da4b93db Upgrade CameraX to 1.2.1 2023-03-08 15:06:50 -04:00
Nicholas
bd373a3045 Improve registration network reliability. 2023-03-08 15:06:50 -04:00
Alex Hart
7c94d570cb Update copy of GiftMessageView. 2023-03-08 15:06:50 -04:00
Greyson Parrelli
8d8f5fb9e4 Update icons for nightly and nightlyStaging. 2023-03-08 15:06:50 -04:00
Greyson Parrelli
1b2cb2637f Perform decryptions inline. 2023-03-08 15:06:50 -04:00
Alex Hart
e222f96310 Add username sync job to be run after new registrations. 2023-03-08 15:06:50 -04:00
Nicholas
877a62b809 Convert VersionTracker to Kotlin and add RefreshAttributesJob. 2023-03-08 15:06:50 -04:00
Greyson Parrelli
81fc99724d Move the Job#onSubmit call to be outside of the JobController lock. 2023-03-06 10:48:18 -05:00
Cody Henthorne
6e8f3d1e71 Fix scheduled message changing disappearing messages bug. 2023-03-06 09:47:12 -05:00
Alex Hart
33ab25a557 Add global formatter to gradle build files. 2023-03-06 10:46:51 -04:00
Cody Henthorne
c30e3664b8 Improve performance of message processing.
Rearranging code allows us to skip expensive calls or duplicating work
already spent to resolve a recipient.
2023-03-04 10:52:21 -05:00
Nicholas
bb8c7bab20 Finish registration activity upon phone number entry cancellation. 2023-03-04 10:52:21 -05:00
Nicholas Tinsley
194681abb7 Change reply icon color. 2023-03-04 10:52:21 -05:00
Alex Hart
c56564014b Add ktlint checking to :build-logic:plugins and split buildQa out into its own task for readability. 2023-03-04 10:52:21 -05:00
Cody Henthorne
c0aff46e31 Add message processing performance test. 2023-03-04 10:52:21 -05:00
Jim Gustafson
f719dcca6d Update to RingRTC v2.25.1 2023-03-04 10:52:21 -05:00
Alex Hart
abd1582422 Fix kdoc in MediaPreviewRepository. 2023-03-04 10:52:21 -05:00
Greyson Parrelli
ec2565263e Initial refactor of the message decryption flow. 2023-03-04 10:52:21 -05:00
Alex Hart
c1a94be9cd Set media preview background to correct color. 2023-03-04 10:52:21 -05:00
Cody Henthorne
e303e80f17 Fix instrumentation tests and group member regression. 2023-03-04 10:52:21 -05:00
Alex Hart
018f6ac7aa Update compose BOM to 2023.01.00 2023-03-04 10:52:21 -05:00
Alex Hart
6f9d3f02f1 Privatize Scaffold preview. 2023-03-04 10:52:21 -05:00
Alex Hart
9b2ccd43c8 Make radio-row preview interactive. 2023-03-04 10:52:21 -05:00
Alex Hart
bd078274b5 Fix RadioRow vertical alignment. 2023-03-04 10:52:21 -05:00
Ehren Kret
9cfb95fee7 Update Github CI to build with more cores and limit metaspace size. 2023-03-04 10:52:03 -05:00
Alex Hart
0f18fa329d Fix crash when inserting group member remaps. 2023-03-04 10:52:03 -05:00
Nicholas
428ef554a3 Add bottom sheet reminder for linked devices on re-register. 2023-03-04 10:52:03 -05:00
Alex Hart
8ca8e5d8f9 Add scaffold preview. 2023-03-04 10:52:03 -05:00
bijaykumarpun
5634e9834d Fix incorrect underlines rendering for empty lines in image editor.
Fixes #12807
Closes #12808
2023-03-04 10:52:03 -05:00
David
b437cb0344 Fix parameter order in getAccessMapFor.
Closes #12812
2023-03-04 10:52:03 -05:00
Alex Hart
3695d7a5f1 Fix marquee scroll behavior in VoiceNotePlayerView. 2023-03-04 10:52:03 -05:00
Alex Hart
7010b19fea Remove username creation state from passphrase required activity. 2023-03-04 10:52:03 -05:00
Alex Hart
3f62221182 Add unit test for build-logic static ip tool. 2023-03-04 10:52:03 -05:00
Alex Hart
43aad90ee4 Fix code formatting. 2023-03-04 10:51:41 -05:00
Alex Hart
aa28668315 Cannot add build-logic tasks to QA. 2023-03-04 10:51:41 -05:00
Alex Hart
9ea392fb4e Utilize non-default arg. 2023-03-04 10:51:41 -05:00
Alex Hart
d0c858221e Add a couple unit tests for StaticIpResolver and string into qa. 2023-03-04 10:51:41 -05:00
Alex Hart
a95e695a97 Start StaticIpResolver testing. 2023-03-04 10:51:41 -05:00
Nicholas Tinsley
8910eac6e0 Fix crash in RegistrationCompleteFragment. 2023-03-04 10:51:41 -05:00
Cody Henthorne
e635c3030e Fix scheduled backup jobs cancelling itself. 2023-03-04 10:51:41 -05:00
Nicholas Tinsley
8cbad2c3a6 Update SMS export string. 2023-03-04 10:51:41 -05:00
Alex Hart
45a04423b0 Add PNP settings. 2023-03-04 10:51:41 -05:00
Clark
f3693c966a Improve conversation list cold start performance. 2023-03-04 10:51:41 -05:00
Cody Henthorne
10e8c6d795 Skip attachments with unrecoverable errors during sms export. 2023-03-04 10:51:41 -05:00
Greyson Parrelli
57e8684bb3 Always use a foreground service when processing high-priority FCM pushes. 2023-03-04 10:51:41 -05:00
Greyson Parrelli
f91c400f6c Convert build-logic build.gradle to kotlin. 2023-03-04 10:51:41 -05:00
Ehren Kret
40f86ed2be Enable gradle caching on the Github CI system. 2023-03-04 10:51:20 -05:00
Alex Hart
ca8add87c6 Update design for who can find me fragment. 2023-03-03 10:40:55 -05:00
Alex Hart
a9c4fcf894 Refresh onboarding cards. 2023-03-03 10:40:55 -05:00
Nicholas
6bc5b19b1e Convert RegistrationCompleteFragment to Kotlin. 2023-03-03 10:40:55 -05:00
Nicholas
4990243a91 Ask for profile name on re-register if none present for number. 2023-03-03 10:40:55 -05:00
Greyson Parrelli
6922886395 Fix a bunch of random lint warnings. 2023-03-03 10:40:55 -05:00
Greyson Parrelli
ce4b7c2d7f Convert StaticIpResolver to kotlin. 2023-03-03 10:40:55 -05:00
Greyson Parrelli
21deb6803c Delete the buildSrc directory.
Moved all of the stuff we were using it for into build-logic.
2023-03-03 10:40:55 -05:00
Greyson Parrelli
873552436a Remove some unused RecipientTable code. 2023-03-03 10:40:55 -05:00
Greyson Parrelli
8334db5273 Validate E164's in ContactRecords. 2023-03-03 10:40:55 -05:00
Greyson Parrelli
33828439fb Use the websocket for FCM fetches. 2023-03-03 10:40:55 -05:00
Clark
4f31dc36ba Improve cold start by postponing voice note service creation. 2023-03-03 10:40:55 -05:00
Cody Henthorne
0a971569d9 Bump version to 6.13.6 2023-03-03 10:28:27 -05:00
Cody Henthorne
06476c80f8 Updated language translations. 2023-03-03 10:23:22 -05:00
Nicholas
d1d73fef30 Support multiple sequential captcha challenges. 2023-03-03 09:51:27 -05:00
Nicholas
89ad213994 Bump version to 6.13.5 2023-03-01 10:47:05 -05:00
Nicholas
6969c6d6ee Updated language translations. 2023-03-01 10:45:56 -05:00
Cody Henthorne
b82f6f83ec Fix network on main thread crash. 2023-03-01 10:17:12 -05:00
Nicholas
d3572f92f5 Bump version to 6.13.4 2023-02-28 15:44:20 -05:00
Nicholas
41126ba913 Updated language translations. 2023-02-28 15:40:43 -05:00
Nicholas Tinsley
0a00413228 Clear unauthorized banner on registration. 2023-02-28 11:42:28 -05:00
Nicholas Tinsley
e810eeec58 Add debugging logs for PIN re-register flow. 2023-02-28 11:24:06 -05:00
Nicholas
5f0035b2d0 Sort country codes as strings rather than ints. 2023-02-28 09:07:20 -05:00
Cody Henthorne
a20d5fd6cf Fix crash when trying to cancel alarm without permission. 2023-02-27 16:57:51 -05:00
Cody Henthorne
191b2076c3 Bump version to 6.13.3 2023-02-24 19:14:41 -05:00
Cody Henthorne
35779e8df3 Updated language translations. 2023-02-24 19:03:50 -05:00
Cody Henthorne
06bec76371 Use recovery flow for change number when possible. 2023-02-24 16:22:43 -05:00
Cody Henthorne
ff76c4cdef Fix keyboard not always auto-showing in registration screens. 2023-02-24 15:59:58 -05:00
Cody Henthorne
42da07b763 Fix incorrect fcm status when reregistering with recovery. 2023-02-24 15:51:30 -05:00
Cody Henthorne
3e69ef8acc Attempt to auto-resolve after being locked out if local data is available. 2023-02-24 11:24:34 -05:00
Nicholas Tinsley
ab48aa5766 Do not force +1 country code when restoring registration state. 2023-02-24 11:05:45 -05:00
Cody Henthorne
e37d3be73a Bump version to 6.13.2 2023-02-23 20:20:50 -05:00
Cody Henthorne
16c2609dab Updated language translations. 2023-02-23 20:01:05 -05:00
Nicholas
e4d4a5d9e0 Adapt change number flow to use V2 API. 2023-02-23 19:56:32 -05:00
Greyson Parrelli
a552a5a5bc Updated in-app language selector to always use native language name. 2023-02-23 19:11:46 -05:00
Greyson Parrelli
13d48b880b Add language support for Cantonese. 2023-02-23 18:58:39 -05:00
Cody Henthorne
6cb8c7a8a9 Ensure attributes are updated with latest properties. 2023-02-23 16:51:25 -05:00
Chris Eager
ae3ff21689 Update spam reporting token JSON field name. 2023-02-23 16:27:14 -05:00
Cody Henthorne
d3c3986100 Bump version to 6.13.1 2023-02-23 12:38:57 -05:00
Cody Henthorne
6f3c095a95 Updated language translations. 2023-02-23 12:30:21 -05:00
Cody Henthorne
6ee04f6574 Fix crash after entering incorrect pin for registration lock. 2023-02-23 12:24:39 -05:00
Nicholas
f3922c4156 Fix bottom sheet behavior and design. 2023-02-23 12:24:39 -05:00
Cody Henthorne
2ffc576387 Fix verification file for windows builders. 2023-02-23 12:24:39 -05:00
Cody Henthorne
583f7db554 Clear old group rings on startup. 2023-02-23 12:24:39 -05:00
Cody Henthorne
1cffd88af2 Fix crashes during skip SMS flow. 2023-02-23 12:24:39 -05:00
Cody Henthorne
01351125f1 Fix reporting token data bug. 2023-02-23 08:08:21 -05:00
Greyson Parrelli
19d67d1111 Bump version to 6.13.0 2023-02-22 22:35:00 -05:00
Greyson Parrelli
8cec6a8b0c Updated language translations. 2023-02-22 22:34:30 -05:00
Greyson Parrelli
e8b3d2c7aa Improve logging around message processing. 2023-02-22 22:26:14 -05:00
Cody Henthorne
62414e72b5 Add support for pin entry sad paths. 2023-02-22 22:26:14 -05:00
Nicholas
afb9b76208 Bug fixes for the new registration flow. 2023-02-22 22:26:14 -05:00
Cody Henthorne
4f458a022f Add skip SMS flow. 2023-02-22 22:26:14 -05:00
Nicholas
a47e3900c1 Implement session-based account registration API. 2023-02-22 22:11:58 -05:00
Jim Gustafson
3de17fa2d0 Update to RingRTC v2.25.0 2023-02-22 19:09:55 -05:00
Clark
7abf358ac4 Pre-cache conversation_list_item_view to speed up cold start. 2023-02-22 16:50:08 -05:00
Greyson Parrelli
64d5cbce3d Fix bug where you could choose to add someone already in a group. 2023-02-22 16:08:39 -05:00
Greyson Parrelli
691ab353da Fix possible dlist sync crash, improved debugging.
Fixes #12795
2023-02-22 14:18:41 -05:00
Greyson Parrelli
b689ea62a6 Fix using system emoji in condensed message mode. 2023-02-22 13:14:53 -05:00
Greyson Parrelli
17aa0365d6 Handle keepMutedChatsArchived more generically, also apply it to sends.
Fixes #12788
2023-02-22 13:14:44 -05:00
Greyson Parrelli
3f93d4b9fc Change ReportSpamJob lifespan to 1 day. 2023-02-22 10:59:09 -05:00
Greyson Parrelli
263fb9fc04 Use null when submitting empty reporting tokens.
Cleaned up a few things too, just spacing and stuff.
2023-02-22 10:59:04 -05:00
Greyson Parrelli
3ebafca297 Validate that reporting token is non-null and non-empty. 2023-02-22 10:33:56 -05:00
Greyson Parrelli
316df00287 Save username when applying update to AccountRecord. 2023-02-22 09:23:49 -05:00
Greyson Parrelli
b92346d4ae Make our FABs rounded rects again. 2023-02-21 14:42:04 -05:00
Greyson Parrelli
7bdb5fd76c Update material library to 1.8.0
Fixes #12792
2023-02-21 11:32:24 -05:00
Greyson Parrelli
dad9980a80 Add Observable for LiveRecipient. 2023-02-21 11:32:24 -05:00
Greyson Parrelli
21df032b04 Mark Recipient.self() as needing sync after change PNP setting. 2023-02-21 11:32:24 -05:00
Alex Hart
7edebe9fa1 Clean up a couple warnings in MediaPreviewV2Fragment. 2023-02-21 11:32:24 -05:00
Alex Hart
a398745740 Implement username is out of sync banner. 2023-02-21 11:32:24 -05:00
Greyson Parrelli
4954be109c Bump version to 6.12.5 2023-02-21 09:59:42 -05:00
Greyson Parrelli
7380d4b11e Updated language translations. 2023-02-21 09:59:19 -05:00
Greyson Parrelli
d8a6f9c324 Fix possible crash when finishing animation. 2023-02-21 09:45:55 -05:00
Greyson Parrelli
ab9057cb25 Fix possible crash during backup restore. 2023-02-21 09:45:44 -05:00
Greyson Parrelli
ba8ea3b54b Bump version to 6.12.4 2023-02-17 15:33:51 -05:00
Greyson Parrelli
d9aca34eee Updated language translations. 2023-02-17 15:33:09 -05:00
Greyson Parrelli
05d232beec Fix possible crash in conversation fragment startup. 2023-02-17 15:25:45 -05:00
Greyson Parrelli
fde0726500 Fix bug where you couldn't add new raw phone numbers to groups. 2023-02-17 15:17:18 -05:00
Greyson Parrelli
bb8b987833 Ignore lastProfileFetchTime when determining Recipient equality.
Was resulting in some unnecessary Recipient re-renders during
conversation open.
2023-02-17 10:12:42 -05:00
Alex Hart
f066fb8ea2 Tweak media transition fade. 2023-02-16 17:26:53 -04:00
Greyson Parrelli
7738c286c2 Bump version to 6.12.3 2023-02-16 16:08:47 -05:00
Greyson Parrelli
697670b334 Updated language translations. 2023-02-16 16:08:47 -05:00
Greyson Parrelli
4cfba86cb1 Fix bug where username wasn't synced to ContactRecord. 2023-02-16 16:08:47 -05:00
Greyson Parrelli
ce4e84aadc Remove crash for group remap updates. 2023-02-16 16:08:47 -05:00
Cody Henthorne
23d0152767 Use newer APIs for wave form generation. 2023-02-16 16:08:47 -05:00
Greyson Parrelli
d714590d3f Address bioauth issues on API 28. 2023-02-16 16:08:47 -05:00
Alex Hart
65bc1263f3 Add final copy for username deletion snackbar. 2023-02-16 16:08:47 -05:00
Alex Hart
730065fc76 Add support URL for usernames. 2023-02-16 16:08:47 -05:00
Alex Hart
1e10b82769 Fix gif sizing in conversation. 2023-02-16 16:08:47 -05:00
Cody Henthorne
01f477a587 Fix voice note UX issues. 2023-02-16 16:08:47 -05:00
Alex Hart
76383fe1bc Fix RTL corners in AlbumThumbnailView. 2023-02-16 16:08:47 -05:00
Greyson Parrelli
c61f45b88b Remove old private certificate authority. 2023-02-16 16:08:47 -05:00
Greyson Parrelli
e559198495 Fix crash when creating backup. 2023-02-16 16:08:47 -05:00
Greyson Parrelli
8676cb27ae Don't show add contact button if the user has no e164.
Fixes #12570
2023-02-16 16:08:46 -05:00
Alex Hart
7215ca6a28 Fix issue where view padding would not properly update on rotation. 2023-02-16 16:08:46 -05:00
Alex Hart
ef11a8d98d Fix navbar flashing on transform. 2023-02-16 16:08:46 -05:00
Greyson Parrelli
6efd501f1c Don't send MR accept for unblocking.
That's handled by storage service now.
2023-02-15 18:07:40 -05:00
Greyson Parrelli
66c650e859 Bump version to 6.12.2 2023-02-15 17:35:23 -05:00
Greyson Parrelli
8141f7a5cf Updated language translations. 2023-02-15 17:35:23 -05:00
Greyson Parrelli
0fcdf61e76 Revert "Don't run FTS optimize job (for now)."
This reverts commit f26b2c0b2a.
2023-02-15 17:35:23 -05:00
Greyson Parrelli
fa571f14e6 Update SQLCipher to 4.5.3-FTS-S2 2023-02-15 17:35:23 -05:00
Cody Henthorne
583860053b Cancel scheduled message alarm if no messages are scheduled. 2023-02-15 17:35:23 -05:00
Alex Hart
ad70baf557 Add documentation to DisplaySecondaryInformation. 2023-02-15 17:35:23 -05:00
Cody Henthorne
2b0e9783a7 Fix invalid attachment data during sms export. 2023-02-15 17:35:23 -05:00
Alex Hart
c75a9b577d Prefer about over phone number. 2023-02-15 17:35:23 -05:00
Cody Henthorne
e8ff1a04ed Fix scrolling issue in transfer lock dialog for small displays. 2023-02-15 17:35:22 -05:00
Cody Henthorne
a22a696722 Fix missing padding in schedule message time picker. 2023-02-15 17:35:22 -05:00
Alex Hart
66494fa418 Fix transition for very tall images. 2023-02-15 17:35:22 -05:00
Greyson Parrelli
9fd763fe83 Bump version to 6.12.1 2023-02-15 13:32:37 -05:00
Greyson Parrelli
1d508ad5cc Updated language translations. 2023-02-15 13:31:58 -05:00
Alex Hart
c1c7f57ec0 Fix sticker scaling. 2023-02-15 13:31:58 -05:00
Cody Henthorne
6100160e18 Address various issues with dark theme registration flow. 2023-02-15 13:24:15 -05:00
Alex Hart
e2c3db3eda Fix miscalculation of groups in common. 2023-02-15 13:24:15 -05:00
Alex Hart
6759b59507 Fix toolbar height and fade in. 2023-02-15 13:24:15 -05:00
Greyson Parrelli
e36844fe78 Improve logging around unblocking. 2023-02-15 13:24:15 -05:00
Alex Hart
5cf937215a Fix stub of TransferControlsView. 2023-02-15 13:24:15 -05:00
Alex Hart
1b49b9bffb Hide system UI until the shared element transition completes. 2023-02-15 13:24:15 -05:00
Alex Hart
a3a29d5cb2 Prevent shared element animation when we're not on the initial media. 2023-02-15 13:24:15 -05:00
Nicholas
6fbfb87bd6 Restore entered phone number post-captcha. 2023-02-15 13:24:15 -05:00
Alex Hart
2bff2d3a30 Disable shared element transitions from bubble. 2023-02-15 13:24:15 -05:00
Cody Henthorne
a88410faaf Fix incorrect quick react emojis for story replies. 2023-02-15 13:24:15 -05:00
Greyson Parrelli
f26b2c0b2a Don't run FTS optimize job (for now). 2023-02-15 13:24:15 -05:00
Alex Hart
6f1b03eac6 Utilize fade instead of just setting alpha to 0. 2023-02-15 10:57:54 -04:00
Cody Henthorne
9610339f38 Improve UX around seeing audio wave forms.
- Attempts to generate the wave form on download instead on display
- Allows multi-threaded generation of wave forms instead of serial
  executor
2023-02-15 09:43:16 -05:00
Alex Hart
d4ce8458a4 Ensure e164s are pretty-printed. 2023-02-15 10:10:25 -04:00
Greyson Parrelli
384cdf8610 Bump version to 6.12.0 2023-02-14 22:47:48 -05:00
Greyson Parrelli
3ee30808de Updated language translations. 2023-02-14 22:47:20 -05:00
Greyson Parrelli
78c64880f7 Fix instance where PNI may be accessed too early. 2023-02-14 14:51:29 -05:00
Greyson Parrelli
b99ce9cc1d Fix typo in string. 2023-02-14 14:30:25 -05:00
Greyson Parrelli
41f796d809 Update CDSI_MRENCLAVE. 2023-02-14 14:28:22 -05:00
Alex Hart
ec504af593 Improve conversation open speed. 2023-02-14 15:10:08 -04:00
Greyson Parrelli
60874ba57b Fix contact name syncing to storage service. 2023-02-14 14:03:09 -05:00
Greyson Parrelli
4397b5af25 Add support for storing systemNickname from storage service. 2023-02-14 14:03:09 -05:00
Rashad Sookram
07234443c6 Update verification metadata for MacOS. 2023-02-14 14:03:09 -05:00
Alex Hart
c027203e8c Polish thumbnail animation. 2023-02-14 14:03:09 -05:00
Alex Hart
417db2341b Utilize drawable instead of bitmap for transition. 2023-02-14 14:03:09 -05:00
Bernie Dolan
6aa4ef95b5 Update payments to 4.0.0.1 2023-02-14 14:03:09 -05:00
Greyson Parrelli
6145fa213e Move common gradle config into convention plugins. 2023-02-14 14:03:09 -05:00
Greyson Parrelli
9fa4741e49 Update ContactRecord.hidden field to value 20. 2023-02-14 14:03:09 -05:00
Greyson Parrelli
b9d5fb54c3 Allow using the location picker with approximate location. 2023-02-14 14:03:09 -05:00
Greyson Parrelli
c0fe156897 Do not fail message inserts on bad quote attachments.
Fixes #12721
2023-02-14 14:03:09 -05:00
Alex Hart
22cad64089 Clean up ThumbnailView warnings. 2023-02-14 14:03:09 -05:00
Alex Hart
702cf6ef71 Remove unused layout class. 2023-02-14 14:03:09 -05:00
Alex Hart
d7c3112602 Speed up thumbnail transition. 2023-02-14 14:03:09 -05:00
Greyson Parrelli
d9c31a6cd6 Update AGP to 7.4.0 2023-02-14 14:03:09 -05:00
Greyson Parrelli
408c288936 Convert MediaTable to kotlin. 2023-02-14 14:03:09 -05:00
Greyson Parrelli
af6f16bdb6 Move Backups.proto to Wire. 2023-02-14 14:03:09 -05:00
Cody Henthorne
055ceba398 Add 'AnyAddressPorts' calling field trial flag. 2023-02-14 14:03:08 -05:00
Greyson Parrelli
3f81a94176 Fix case where we were performing remote inserts. 2023-02-14 14:02:23 -05:00
Greyson Parrelli
a02d2e467b Revert "Remove the unknown insert validation."
This reverts commit 320669c54e.
2023-02-14 14:02:23 -05:00
Greyson Parrelli
414550861e Prevent recursive early content processing. 2023-02-14 14:02:23 -05:00
Greyson Parrelli
afbce6f800 Re-enable FTS optimization after deletes. 2023-02-14 14:02:23 -05:00
Alex Hart
dda5037429 Add stubbing to ConversationThumbnailView and caching to a typeface. 2023-02-14 14:02:23 -05:00
Greyson Parrelli
ffbebe0670 Update SQLCipher to 4.5.3-FTS-S1 2023-02-14 14:02:23 -05:00
Alex Hart
cf250b4b32 Add catch for candidate generation error to be treated the same as username unavailable. 2023-02-14 14:02:23 -05:00
Nicholas Tinsley
b14aea0922 Support dark mode in verification code keyboard. 2023-02-14 14:02:23 -05:00
Alex Hart
d0de43a6b2 Add thumbnail shared element animation. 2023-02-14 14:02:23 -05:00
Alex Hart
2c48d40375 Update API endpoints and integration for usernames. 2023-02-14 14:02:23 -05:00
Greyson Parrelli
803154c544 Add a new PNP build flavor. 2023-02-14 14:02:23 -05:00
Greyson Parrelli
684150dc1e Handle split contacts in storage service when in PNP mode. 2023-02-14 14:02:22 -05:00
Greyson Parrelli
fdcf0a76e8 Split unregistered contacts when in PNP mode. 2023-02-14 14:02:22 -05:00
Greyson Parrelli
9e056e5dd0 Add support for rendering session switchover events. 2023-02-14 14:02:22 -05:00
Cody Henthorne
03c68375db Fix bad group state when requesting to rejoin a group. 2023-02-14 14:02:22 -05:00
Alex Hart
5d328857aa Upgrade libsignal to 0.22.0 2023-02-14 14:02:22 -05:00
Cody Henthorne
3a0dbe6e67 Use alarm clock for scheduling message sends. 2023-02-14 14:02:22 -05:00
Cody Henthorne
56b35f3767 Fix quoted links from rendering as clickable. 2023-02-14 14:02:22 -05:00
Greyson Parrelli
7f0221c5c6 Prefer MessageRecord mismatches when updating SN's. 2023-02-14 14:02:22 -05:00
Cody Henthorne
23050152de Replace time duration picker dialog for screen lock timeout. 2023-02-14 14:02:22 -05:00
Alex Hart
db65edb7df Mark DSL api discouraged. 2023-02-14 14:02:22 -05:00
Alex Hart
605289aca4 Upgrade ktlint and add twitter compose rules. 2023-02-14 14:02:22 -05:00
Jim Gustafson
52e9b31554 Update to RingRTC v2.24.0 2023-02-14 14:02:22 -05:00
Alex Hart
c8e6ccc0c0 Add extended colors to SignalTheme. 2023-02-14 14:02:22 -05:00
Alex Hart
f20d929292 Add Buttons object for properly themed compose buttons. 2023-02-14 14:02:22 -05:00
Greyson Parrelli
a9accfb074 Bump version to 6.11.7 2023-02-14 14:01:40 -05:00
Greyson Parrelli
8f2d1a2d12 Updated language translations. 2023-02-14 14:01:40 -05:00
Greyson Parrelli
ca8755c6ad Inline the scheduled message feature flag. 2023-02-14 14:01:40 -05:00
Cody Henthorne
dc4eb7911d Bump version to 6.11.6 2023-02-13 13:43:23 -05:00
Cody Henthorne
eb2e0205ae Updated language translations. 2023-02-13 13:31:23 -05:00
Cody Henthorne
7a72a9a0d7 Fix memory leak in conversation fragment. 2023-02-13 13:07:32 -05:00
Alex Hart
805ccc4f7a Bump version to 6.11.5 2023-02-10 16:13:23 -04:00
Alex Hart
499b186b68 Updated language translations. 2023-02-10 16:12:21 -04:00
Cody Henthorne
c741e32824 Fix stale thread id when a conversation is deleted. 2023-02-10 13:08:51 -05:00
Alex Hart
fba4c882cb Bump version to 6.11.4 2023-02-09 16:16:41 -04:00
Alex Hart
24ef853f24 Updated language translations. 2023-02-09 16:16:04 -04:00
Nicholas
9f22ba68ea Set PIN entry text to use dynamic theme colors. 2023-02-09 13:45:26 -05:00
Greyson Parrelli
d8eac87219 Cleanup dangling MSL rows. 2023-02-09 13:41:32 -05:00
Greyson Parrelli
cf71e2cfa8 Bump version to 6.11.3 2023-02-08 21:21:36 -05:00
Greyson Parrelli
7f16d0653c Updated language translations. 2023-02-08 21:20:43 -05:00
Greyson Parrelli
61e127fabf Fix method to find MMS group. 2023-02-08 21:13:14 -05:00
Alex Hart
7ffdf91ce5 Bump version to 6.11.2 2023-02-07 15:06:25 -04:00
Alex Hart
4c26fe432e Updated language translations. 2023-02-07 15:06:25 -04:00
Cody Henthorne
6c78a405bb Fix backup scheduling looping bug. 2023-02-07 15:06:25 -04:00
Cody Henthorne
89b0167fd2 Ensure backup job verification can be cancelled. 2023-02-07 11:19:26 -05:00
Alex Hart
e25133fa29 Bump version to 6.11.1 2023-02-06 17:15:26 -04:00
Alex Hart
4ba77c0f9f Updated language translations. 2023-02-06 17:09:45 -04:00
Nicholas Tinsley
10f376e402 Catch new audio recording error states. 2023-02-06 16:53:08 -04:00
Cody Henthorne
7bae8b6e1b Fix scheduled message sends changing thread disappearing message timer. 2023-02-06 16:53:08 -04:00
Cody Henthorne
67fb9d09d4 Fix scheduled send in note to self with no linked devices. 2023-02-06 16:53:08 -04:00
Nicholas
3b40b10a77 Try to check group mute status for keeping archived. 2023-02-06 16:53:08 -04:00
Cody Henthorne
418b486776 Fix crash when scheduling a message in an empty thread. 2023-02-06 16:53:08 -04:00
Cody Henthorne
1f31f4a50a Adjust SMS phases and show Phase 3 start date. 2023-02-06 16:53:08 -04:00
Cody Henthorne
9b08ebcc1d Update QR code and send symbols. 2023-02-06 16:53:08 -04:00
Nicholas
aec4944c56 Allow V1 groups to be deleted by clearing app data. 2023-02-06 16:53:08 -04:00
Nicholas Tinsley
9a1f8af703 Add Material3 SVG assets to Registration. 2023-02-06 16:53:08 -04:00
Greyson Parrelli
268b11c4e1 Fix bug in MSL table definition. 2023-02-06 16:53:08 -04:00
Nicholas Tinsley
2e3d73f44b Media preview design tweaks. 2023-02-06 16:53:08 -04:00
Alex Hart
f477a4dae9 Add compose bottom-sheet handle. 2023-02-06 16:53:08 -04:00
Greyson Parrelli
a41aed20e1 Fix issue where group stories weren't syncing to linked devices. 2023-02-03 12:08:21 -05:00
Alex Hart
1ed3dbb147 Add svg asset for username megaphone. 2023-02-03 09:46:42 -04:00
Alex Hart
fcfb9fad01 Add svg assets to username education screen. 2023-02-03 09:45:13 -04:00
Alex Hart
25c96a6be6 Resolve crashing when trying to get the header letters for the contacts section of search. 2023-02-03 09:33:18 -04:00
Nicholas Tinsley
90695182f3 Bump version to 6.11.0 2023-02-02 17:55:33 -05:00
Nicholas Tinsley
1c38ab18b8 Updated language translations. 2023-02-02 17:55:12 -05:00
Alex Hart
7c716e5525 Fix slow kotlin build. 2023-02-02 17:22:40 -05:00
Cody Henthorne
56a44ae65c Enforce expected ordering when scheduling text and media messages. 2023-02-02 17:22:40 -05:00
Nicholas
d33aa247db Fix composer voice memo cancellation due to focus loss. 2023-02-02 17:22:40 -05:00
Alex Hart
63a153571d Add generic Compose fragment. 2023-02-02 17:22:40 -05:00
Alex Hart
fb07e897d0 Mark username megaphone completion after hitting continue. 2023-02-02 17:22:40 -05:00
Alex Hart
93387ec79a Add deletion snackbar for usernames with temporary copy. 2023-02-02 17:22:40 -05:00
Alex Hart
cd79dbbb82 Add Username UI updates. 2023-02-02 17:22:40 -05:00
Alex Hart
7fbfc09a89 Refactor ContactSelectionListFragment to use ContactSearch infrastructure. 2023-02-02 17:22:40 -05:00
Alex Hart
0f6bc0471c Add core-ui module and Jetpack Compose. 2023-02-02 17:22:40 -05:00
Alex Hart
ba919d4ecc Add proper styling for text inputs. 2023-02-02 17:22:40 -05:00
Alex Hart
73722297cf Fix membership query to account for active state and mms state. 2023-02-02 17:22:40 -05:00
Greyson Parrelli
6050a9f585 Update feature flag constant. 2023-02-02 17:22:40 -05:00
Cody Henthorne
b243eee4ce Fix incorrect unread count after sending scheduled messages. 2023-02-02 17:22:40 -05:00
Greyson Parrelli
a91a13cead Introduce Wire for proto codegen. 2023-02-02 17:22:40 -05:00
Nicholas
72449fd73e Store & submit spam reporting token from server. 2023-02-02 17:22:40 -05:00
Cody Henthorne
6a8e82ef91 Prevent scheduling of sends when alarm permission is denied. 2023-02-02 17:22:40 -05:00
Greyson Parrelli
987fafff92 Remove is_mms field from MSL tables. 2023-02-02 17:22:40 -05:00
Cody Henthorne
35ff977df9 Fix position calculation for conversations with scheduled messages. 2023-02-01 19:23:12 -05:00
Cody Henthorne
fe2d71fca0 Delete pending scheduled messages when leaving a group. 2023-02-01 19:04:29 -05:00
Greyson Parrelli
a12a246e87 Add foreign key constraint details to Spinner. 2023-02-01 17:41:28 -05:00
Alex Hart
4f387cf8d9 Add username education screen. 2023-02-01 17:41:28 -05:00
Cody Henthorne
dae69744c2 Prevent schedule send UI from showing in story send flow. 2023-02-01 17:41:28 -05:00
Cody Henthorne
4ad233c6d1 Show will send immediately warning if scheduled send is in the past. 2023-02-01 17:41:28 -05:00
Cody Henthorne
b4c572678c Fix incorrect ripple effect in search box.
Fixes #12641
2023-02-01 17:41:28 -05:00
Cody Henthorne
5024998a6f Improve clarity of screen lock timeout duration. 2023-02-01 17:41:28 -05:00
Jim Gustafson
43cde19071 Remove redundant logging.
And move the backup stun server to the end of the list.
2023-02-01 17:41:28 -05:00
Greyson Parrelli
62a2f3d8ba Fix bug when highlighting search results. 2023-02-01 17:41:28 -05:00
Greyson Parrelli
5bc44fa586 Improve network reliability. 2023-02-01 17:41:28 -05:00
Greyson Parrelli
f0b3aa66f7 Revert "Enable gradle configuration cache."
This reverts commit 6e5b4bbc15.
2023-02-01 17:41:28 -05:00
Clark
ef9cd2515e Add new story reaction bar. 2023-02-01 17:41:28 -05:00
Greyson Parrelli
4677f207e7 Rotate scheduled message feature flag. 2023-02-01 17:41:28 -05:00
Bernie Dolan
4c26f3258d Update Mobilecoin testnet enclave values. 2023-02-01 17:41:28 -05:00
Cody Henthorne
77a3037614 Update icons in popup/context menus. 2023-02-01 17:41:28 -05:00
Alex Hart
9600d6f6a9 Add different menu copy for clearing the enabled chat filter. 2023-02-01 17:41:28 -05:00
Alex Hart
36dfa19aec Add "contacts without threads" section to Conversation List Search. 2023-02-01 17:41:28 -05:00
Alex Hart
09902e5d11 Add "Group Members" section to ConversationList search results. 2023-02-01 17:41:27 -05:00
Nicholas Tinsley
e84c6187b9 Bump version to 6.10.5 2023-02-01 16:59:54 -05:00
Nicholas Tinsley
b5d52db57c Add logging for audio recorder exceptions. 2023-02-01 11:41:30 -05:00
Alex Hart
f320cf8833 Add factory for story privacy view model. 2023-02-01 12:22:55 -04:00
Cody Henthorne
c76ca957e1 Prevent keyboard from closing immediately after opening. 2023-02-01 10:35:42 -05:00
Cody Henthorne
56354f6aae Fix memory leak of schedule message observers. 2023-02-01 10:22:17 -05:00
Nicholas Tinsley
2bf84a5f77 Fix country code dropdown during registration. 2023-02-01 09:54:44 -05:00
Nicholas Tinsley
8b23d9a6c4 Bump version to 6.10.4 2023-01-31 17:20:05 -05:00
Nicholas Tinsley
2cae3ddf04 Updated language translations. 2023-01-31 17:20:05 -05:00
Greyson Parrelli
670b6c4c56 Revert "Upgrade to Glide 4.14.2"
This reverts commit 3ee889cb79.
2023-01-31 16:15:33 -05:00
Alex Hart
eceed641bf Fix leak in recording session. 2023-01-31 16:15:33 -05:00
Nicholas
5febe6490c Null check Media URI in save task. 2023-01-31 16:15:33 -05:00
Nicholas
dca47e4cb5 Strip mention Spans out of media captions. 2023-01-31 16:15:33 -05:00
Nicholas
c3bcba6380 Design improvements for registration flow. 2023-01-31 16:15:33 -05:00
Alex Hart
cb01692a50 Fix localization of note to self string in search. 2023-01-31 10:07:50 -04:00
Alex Hart
e90074ffef Fix issue with bottom sheets. 2023-01-31 10:00:25 -04:00
Alex Hart
691520bc75 Clean out dead code from contact search. 2023-01-30 12:52:27 -04:00
Greyson Parrelli
e6de06be6f Bump version to 6.10.3 2023-01-30 10:40:13 -05:00
Greyson Parrelli
a77079ac81 Updated language translations. 2023-01-30 10:39:52 -05:00
Greyson Parrelli
30b58fe5f4 Don't run FTS optimize job (for now). 2023-01-30 10:39:41 -05:00
Greyson Parrelli
7275b95b58 Bump version to 6.10.2 2023-01-27 17:42:22 -05:00
Greyson Parrelli
f04d46b4ed Updated language translations. 2023-01-27 17:42:22 -05:00
Greyson Parrelli
e12bbe943b Restore the 3-dot menu when creating a PIN. 2023-01-27 17:42:22 -05:00
Greyson Parrelli
7348224dc2 Prevent thread trimming from gumming up the database. 2023-01-27 17:42:22 -05:00
Cody Henthorne
30c33fdd77 Fix issues with scheduled messages and quotes.
- Tapping quote in schedule view will jump to message in chat
- Scheduling a quote will not make the quoted message render as "isQuoted"
- Scheduled quotes will not appear in the quoted message's sheet of replies
- Fixes an off-by-N where N = # of scheduled messages when calculating location for jumping to a message
2023-01-27 17:42:22 -05:00
Alex Hart
e7339af119 Add fix for group membership query. 2023-01-27 17:42:22 -05:00
Cody Henthorne
661fff7a0e Fix scheduled messages being sent out of order. 2023-01-27 17:42:22 -05:00
Alex Hart
c37bad0f7a Fix opening filter when swiping from within collapsingtoolbar. 2023-01-27 17:42:22 -05:00
Alex Hart
7f228fc0fd Do not display add to story if stories are disabled. 2023-01-27 17:42:22 -05:00
Cody Henthorne
14cd216668 Fix crash when delete scheduled message dialog is open and message sends. 2023-01-27 17:42:22 -05:00
Cody Henthorne
0cb0ef977c Add calendar icon to pick date and time item.
Co-authored-by: Sgn-32 <49990901+Sgn-32@users.noreply.github.com>
2023-01-27 17:42:22 -05:00
Cody Henthorne
1761529ce9 Disable schedule message for SMS. 2023-01-27 17:42:22 -05:00
Clark
a14fc82e83 Fix scheduled view once message looking weird. 2023-01-27 17:42:22 -05:00
Clark
b94f5501d9 Disable scheduling of voice note messages. 2023-01-27 17:42:21 -05:00
Clark
834283ba9b Hide scheduled messages bar with input panel. 2023-01-27 17:42:21 -05:00
Nicholas
23190a2f6e Fix NumericKeyboardView in RTL. 2023-01-27 11:06:04 -05:00
Alex Hart
04f4cd8edc Fix V172 Migration. 2023-01-27 10:20:44 -04:00
Greyson Parrelli
8f02e4e1f5 Bump version to 6.10.1 2023-01-26 20:25:28 -05:00
Greyson Parrelli
db81a5be04 Updated language translations. 2023-01-26 20:25:28 -05:00
Greyson Parrelli
fe40e37da4 Fix full text search migration after table name change. 2023-01-26 20:25:28 -05:00
Alex Hart
22a4271dfb Rotate paypal recurring donations flag. 2023-01-26 20:25:28 -05:00
Greyson Parrelli
1263b51e03 Patch random place where we forgot to update minSdk. 2023-01-26 20:25:28 -05:00
Nicholas
ca468047ef Adjust media caption height, fix rail visibility. 2023-01-26 20:25:28 -05:00
Clark
958c52a5b8 Force indexes for scheduled message queries. 2023-01-26 20:25:28 -05:00
Greyson Parrelli
9b28585c59 Add foreign key dependency between reactions and messages. 2023-01-26 20:25:27 -05:00
Clark
c5c60b7214 Add permissions dialogs for scheduled messages. 2023-01-26 20:25:27 -05:00
Nicholas
31bcc2e2eb Finish MediaPreviewV2Activity when jumping to a message. 2023-01-26 20:25:27 -05:00
Cody Henthorne
71ecba17fc Fix crash when saving empty formatted text drafts. 2023-01-26 20:25:27 -05:00
Greyson Parrelli
afa5c68312 Periodically optimize the FTS index. 2023-01-26 20:25:27 -05:00
Clark
f3e715e069 Add support for scheduled message sends. 2023-01-26 20:25:27 -05:00
Alex Hart
df695f7611 Fix crash when trying to create new group story.
Adds INNER JOIN to threads table to allow access to date in ORDER BY
2023-01-26 20:25:27 -05:00
Greyson Parrelli
27e1bc0854 Bump version to 6.10.0 2023-01-25 17:11:34 -05:00
Greyson Parrelli
f4371b9e96 Updated language translations. 2023-01-25 17:08:53 -05:00
Cody Henthorne
e0633180ef Fix crash when trying to update a group call without an era id. 2023-01-25 17:02:41 -05:00
Alex Hart
32dd227ab6 Utilize left join instead of inner join when querying groups. 2023-01-25 17:02:41 -05:00
Cody Henthorne
0deed9d4d2 Fix notification sound not respecting notification volume.
Fix is immediate for general messages channel and for future custom channel creation
2023-01-25 17:02:41 -05:00
Cody Henthorne
cc490f4b73 Add text formatting send and receive support for conversations. 2023-01-25 17:02:41 -05:00
Cody Henthorne
aa2075c78f Attempt to fix view jitter when switching keyboards. 2023-01-25 17:02:41 -05:00
Alex Hart
b4a34599d7 Add support for message and thread results. 2023-01-25 17:02:41 -05:00
Nicholas
8dd1d3bdeb Allow user-selected backup time. 2023-01-25 17:02:41 -05:00
Greyson Parrelli
a7d9bd944b Insert session switchover events when appropriate. 2023-01-25 17:02:41 -05:00
Clark
7745ae62ea Add logging for voice note recording events. 2023-01-25 17:02:41 -05:00
Greyson Parrelli
6e5b4bbc15 Enable gradle configuration cache.
Android Studio told me to do this and that it would save me over 7
seconds. We'll see if it breaks anything :p
2023-01-25 17:02:41 -05:00
Clark
e3b38e6d38 Fix some thumbnail images not showing up in MediaGallery. 2023-01-25 17:02:41 -05:00
Clark
25aa4f39a3 Close input streams on failed resource decryption. 2023-01-25 17:02:41 -05:00
Cody Henthorne
17849e20bd Update group receipt table when sync'ing story sends to distribution lists. 2023-01-25 17:02:41 -05:00
Alex Hart
c022172ace Add group member results to contact search. 2023-01-25 17:02:41 -05:00
Nicholas
eaeeb08987 Display message text in Media Preview. 2023-01-25 17:02:41 -05:00
Alex Hart
1b7e4e047c Introduce ManyToMany table for group membership. 2023-01-24 14:18:28 -05:00
Clark
d635683303 Fix share intent not being cleared from recents. 2023-01-24 14:18:28 -05:00
Clark
4dcbbfdd63 Fix voice note draft not being generated on audio focus loss. 2023-01-24 14:18:28 -05:00
Nicholas
150bbf181d Redesign FTUX to use Material Design 3. 2023-01-24 14:18:28 -05:00
Alex Hart
0303467c91 Add PayPal decline code errors. 2023-01-24 14:18:28 -05:00
Nicholas
88da382a6f For calling purposes, categorize hearing aids as Bluetooth headsets. 2023-01-24 14:18:28 -05:00
Alex Hart
5d14166a27 Add support for arbitrary rows in contact search. 2023-01-24 14:18:28 -05:00
Jim Gustafson
d76d13f76c Update to RingRTC v2.23.1 2023-01-24 14:18:28 -05:00
Greyson Parrelli
ad4ec23875 Bump version to 6.9.2 2023-01-24 14:15:52 -05:00
Greyson Parrelli
61df2afc32 Updated language translations. 2023-01-24 14:15:52 -05:00
Nicholas Tinsley
1c6d2f7198 If enabled, don't unarchive muted group chats.
Fixes #12732
2023-01-24 14:13:42 -05:00
Cody Henthorne
df8f9761b2 Fix incorrect total sms export count. 2023-01-24 13:49:25 -05:00
Greyson Parrelli
657c5d2bce Bump version to 6.9.1 2023-01-20 18:12:37 -05:00
Greyson Parrelli
81324c6923 Updated language translations. 2023-01-20 18:12:17 -05:00
Greyson Parrelli
269a2e2990 Improve timer event generation from GV2 sync messages. 2023-01-20 17:58:17 -05:00
clark-signal
73b453b0d4 Fix re-used share intent when restarting task from recent activities. 2023-01-20 13:42:13 -05:00
Cody Henthorne
97604dc4c5 Bump version to 6.9.0 2023-01-19 13:44:21 -05:00
Cody Henthorne
8e1c05ed64 Updated language translations. 2023-01-19 13:38:09 -05:00
Nicholas
231b55a956 Don't show media controls on new pages. 2023-01-19 13:33:07 -05:00
Alex Hart
4fcdee9fa5 Rotate PayPal recurring donations feature flag. 2023-01-19 13:33:07 -05:00
Cody Henthorne
6e2e5e21cc Update copy and behavior of SMS phased removal flow. 2023-01-19 13:33:07 -05:00
Alex Hart
8f49323648 Extract adapter creation from ContactSearchMediator. 2023-01-19 13:33:07 -05:00
Greyson Parrelli
13f969b622 Fix an app migration.
Fixes #12730
2023-01-19 13:33:07 -05:00
Greyson Parrelli
518d9b3984 Update SQLCipher to 4.5.1-S1
This reverts commit d1894caea6.
2023-01-19 13:33:07 -05:00
Alex Hart
8e313f8387 Collapse KnownRecipient / Story into single model. 2023-01-19 13:33:07 -05:00
Nicholas
70c6e9e60f Store additional data that will allow us to reduce the number of verification SMSs. 2023-01-19 13:32:35 -05:00
Cody Henthorne
dcf8a82c37 Fix no snippet being shown for threads.
Snippet query wasn't updated to exclude SMS types after the conjuction
of the tables.
2023-01-19 13:32:35 -05:00
Alex Hart
f368e5b133 Suppress deselection error when opening gallery from chat. 2023-01-19 13:32:35 -05:00
Alex Hart
3ee889cb79 Upgrade to Glide 4.14.2 2023-01-17 14:30:48 -05:00
Greyson Parrelli
3e7dc79fe8 Remove unnecessary code now that minSdk is 21. 2023-01-17 14:30:48 -05:00
Greyson Parrelli
8cfd02aff2 Bump minSdk to 21. 2023-01-17 14:30:48 -05:00
Alex Hart
f36efc562e Fix improper filtering of unread conversations. 2023-01-17 14:30:48 -05:00
Sgn-32
67b6b109de Use ic_save_24_tinted instead of ic_download_24_tinted. 2023-01-17 14:30:48 -05:00
Alex Hart
8fd378db4e Fix issue where links do not render in stories if previews are off. 2023-01-17 14:30:48 -05:00
Cody Henthorne
760ace93d4 Enable PNI group invite processing. 2023-01-17 14:30:48 -05:00
Nicholas
125fd83afa Programmatically dismiss logged out notification on registration. 2023-01-17 14:30:48 -05:00
Alex Hart
7dcb598b66 Inline gift badge flag. 2023-01-17 14:30:48 -05:00
Alex Hart
4917e93d9f Update CameraX to 1.2.0 2023-01-17 14:30:48 -05:00
Alex Hart
1e9115a917 Increment compileSdkVersion to 33. 2023-01-17 14:30:48 -05:00
clark-signal
7af94f60ae Fix story linking from private story reply. 2023-01-17 14:30:48 -05:00
clark-signal
c3c8f8e7e6 Update icons across home screen and settings screen. 2023-01-17 14:30:48 -05:00
clark-signal
011c85c75b Fix "My subscription" dark mode overlapping background bubbles. 2023-01-17 14:30:47 -05:00
Cody Henthorne
90112bec31 Bump version to 6.8.3 2023-01-17 14:15:45 -05:00
Cody Henthorne
bca43fb93b Updated language translations. 2023-01-17 14:00:36 -05:00
Alex Voloshyn
b6ee69d346 Add proguard config for payments. 2023-01-17 09:37:45 -05:00
Alex Hart
b2a4dc303b Bump version to 6.8.2 2023-01-13 16:47:35 -04:00
Alex Hart
8553cf6b96 Updated language translations. 2023-01-13 16:47:23 -04:00
Alex Hart
276f485b49 Fix media review screen view translation when animations are disabled.
Fixes #12698
2023-01-13 10:28:14 -04:00
Greyson Parrelli
54ffb4ad7b Improve network reliability. 2023-01-12 17:54:08 -05:00
Alex Hart
28531bb415 Bump version to 6.8.1 2023-01-12 15:46:19 -04:00
Alex Hart
d0c4cefaad Updated language translations. 2023-01-12 15:41:46 -04:00
Nicholas Tinsley
d765fb1d5d Fix androidTests for registration changes. 2023-01-12 15:36:21 -04:00
Greyson Parrelli
bce2dd1d1b Fix v171 migration.
Copy-pasted from the old one, but forgot that the sms table no longer
exists...
2023-01-12 15:36:21 -04:00
Sgn-32
c099ad0aa7 Replace the old SimpleProgressDialog with the new ProgressCard in WallpaperCropActivity. 2023-01-12 15:36:21 -04:00
Alex Hart
3738daf4a8 Pre-cache max unreserved player count on process startup.
This call was taking up to 20ms to run on the first time we would
load a conversation. Adding a post-render call to app launch reduces
this time to 0ms, as the data has been cached by the system. Doing
it on a background thread means we do not pay for it in TTFF.
2023-01-12 15:36:21 -04:00
Greyson Parrelli
564b9f47ee Add banner warning about API 19 deprecation. 2023-01-12 15:36:21 -04:00
Alex Hart
7617a164fc Bump version to 6.8.0 2023-01-11 14:25:59 -04:00
Alex Hart
46c98f4e0b Updated language translations. 2023-01-11 14:22:49 -04:00
Greyson Parrelli
522346479c Improve network reliability. 2023-01-11 14:18:25 -04:00
Greyson Parrelli
df86d1b4ba Re-run migration to add foreign key to thread table.
Unfortunately the table schema wasn't fixed for new installs, so we need
to run it again.
2023-01-11 14:18:25 -04:00
clark-signal
74760a4a64 Add alternate junit-bom 5.8.1 to fix CI. 2023-01-11 14:18:25 -04:00
clark-signal
9e903a023f Cleanup media rail crossfade animation. 2023-01-11 14:18:25 -04:00
clark-signal
6120902ff5 Prevent system UI from being hidden incorrectly while paging media. 2023-01-11 14:18:25 -04:00
Cody Henthorne
1d2fbf0ebf Make adding properties to media send result easier. 2023-01-11 14:18:25 -04:00
Greyson Parrelli
40f9a25b87 Set Accept-Language header when requesting SMS code. 2023-01-11 14:18:25 -04:00
clark-signal
f2881843db Fix MediaRailAdapter request counting. 2023-01-11 14:18:25 -04:00
Greyson Parrelli
c53b090b76 Fix formatting in LiveRecipient. 2023-01-11 14:18:24 -04:00
Alex Voloshyn
1ba2712375 Update MobileCoin SDK to v4.0.0 2023-01-11 14:18:24 -04:00
Alex Hart
71ff31e91f Fix Stripe json body error handling. 2023-01-11 14:18:24 -04:00
Nicholas
aa9a530e59 Upload PIN to KBS on successful register. 2023-01-11 14:18:24 -04:00
Nicholas
e4cc7f5181 Attempt to Skip PIN Entry on Re-Registration. 2023-01-11 14:18:24 -04:00
Cody Henthorne
13f43799d6 Unify registration response models and processors. 2023-01-11 14:18:24 -04:00
Alex Hart
3543a5ea24 Update donations strings. 2023-01-11 14:18:24 -04:00
Greyson Parrelli
429f89cba1 Add some logging for early delivery receipts. 2023-01-11 14:18:24 -04:00
clark-signal
5b0084a5e2 Fix group story selection crash. 2023-01-11 14:18:24 -04:00
clark-signal
a3bc831346 Fix mentions in drafts being uneditable. 2023-01-11 14:18:24 -04:00
Alex Hart
2fd6b7c49e Fix donation payment update issue. 2023-01-11 14:18:24 -04:00
clark-signal
fa613557e8 Hide "Add as contact" option when no phone number present. 2023-01-11 14:18:20 -04:00
clark-signal
87c366223a Load drafts from DB when opening conversation from notifications. 2023-01-11 14:18:15 -04:00
Alex Hart
aac1d0cedb Utilize switchMapSingle instead of flatMapSingle. 2023-01-11 13:17:26 -04:00
Jim Gustafson
ea12cde1d8 Update to RingRTC v2.23.0 2023-01-11 13:17:25 -04:00
Alex Hart
9e2a5002bc Update paypal payment method endpoint and enable subs in staging. 2023-01-11 13:16:43 -04:00
Alex Hart
396742f3ad Add new chat bubble pulse. 2023-01-11 13:16:43 -04:00
Alex Hart
af0fbdd2b2 Fix story splitting in multishare flow. 2023-01-11 13:16:43 -04:00
Cody Henthorne
bdbeefe08e Update MobileCoin measurements and configuration. 2023-01-11 13:16:43 -04:00
Nicholas
9a763bd726 Upgrade Lottie to 5.2.0. 2023-01-11 13:16:43 -04:00
Alex Hart
9636aa4d37 Bump version to 6.7.6 2023-01-11 11:00:13 -04:00
Alex Hart
410e6abba9 Updated language translations. 2023-01-11 11:00:00 -04:00
Greyson Parrelli
d1894caea6 Revert "Update SQLCipher to 4.5.1-S1"
This reverts commit f1d204b834.
2023-01-11 08:57:56 -05:00
Alex Hart
af335a447f Bump version to 6.7.5 2023-01-10 13:05:34 -04:00
Alex Hart
711eec4bf2 Updated language translations. 2023-01-10 13:03:00 -04:00
Alex Hart
80c5cbe0da Add padding around clear-filter tip. 2023-01-10 12:57:28 -04:00
Alex Hart
84d0283719 Ensure capability check occurs for gift recipients. 2023-01-10 12:57:03 -04:00
Alex Hart
53e347a67d Bump version to 6.7.4 2023-01-09 13:32:28 -04:00
Alex Hart
3d6220737a Updated language translations. 2023-01-09 13:29:12 -04:00
Alex Hart
383525e7b7 Update chat filter behaviour after round of feedback. 2023-01-06 16:40:51 -04:00
Cody Henthorne
3869de414f Bump version to 6.7.3 2023-01-06 14:15:29 -05:00
Cody Henthorne
89bbfd3ded Updated language translations. 2023-01-06 13:06:33 -05:00
Greyson Parrelli
e2fb65920c Ensure SMS and MMS messages are sent appropriately. 2023-01-06 11:27:33 -05:00
Greyson Parrelli
5537039e46 Bump version to 6.7.2 2023-01-05 15:03:41 -05:00
Greyson Parrelli
0a40432ed4 Fix crash that occurs during thread trims by date. 2023-01-05 14:52:51 -05:00
Cody Henthorne
73e46053f0 Fix voice note crash when future failed. 2023-01-05 12:12:03 -05:00
Greyson Parrelli
a835e5d143 Bump version to 6.7.1 2023-01-05 10:14:04 -05:00
Greyson Parrelli
073d5dfe8c Don't mark unauthorized unless we're registered. 2023-01-05 10:05:58 -05:00
Greyson Parrelli
bfba60b6b6 Improve reliability of SqlUtil.getNextAutoIncrementId() 2023-01-05 10:01:33 -05:00
Alex Hart
84d9e1d28e Drop proxy sheet events if fragment state has been saved. 2023-01-05 09:44:26 -04:00
Alex Hart
a14c61c370 Rotate chat-filters flag. 2023-01-05 09:38:54 -04:00
Greyson Parrelli
0db4630f58 Bump version to 6.7.0 2023-01-04 16:57:33 -05:00
Greyson Parrelli
6d2e51def6 Updated language translations. 2023-01-04 16:57:05 -05:00
Nicholas
091eb0aa2b Fix chaining of Stories FTUX for small screens. 2023-01-04 16:40:36 -05:00
Greyson Parrelli
59f05e0815 Improve performance of marking chats read.
SQLite isn't always smart enough to use the best index for a query.
The main improvement here was to force it to use a better index than the
one it was using (which, on my device, happened to by the story index,
which was only minimally useful here).
2023-01-04 16:40:36 -05:00
Greyson Parrelli
a513e93d18 Fix log around sending SKDM's. 2023-01-04 16:40:36 -05:00
Alex Hart
91f6cff4df Do not display recovery card if entropy is not set. 2023-01-04 16:40:36 -05:00
Alex Hart
ec3ec969eb Add updated search hint text when filter is enabled. 2023-01-04 16:40:36 -05:00
Alex Hart
54d0df9a05 Ensure only a single story is displayed above the fold in bottom sheets. 2023-01-04 16:40:36 -05:00
Greyson Parrelli
320669c54e Remove the unknown insert validation.
There's actually a legitimate case where this is ok: right after a
backup restore.

Restoring a backup means that you have possibly carried over some
unknownIds, and if you don't remember your PIN, those items wouldn't be
there remotely. And you _should_ insert them. Otherwise they're lost.

I don't think this validation is worth the trouble of carving out lots
of conditions to allow this usecase.
2023-01-04 16:40:36 -05:00
Alex Hart
bf491c25f7 Update view badge bottom sheet button copy. 2023-01-04 16:40:36 -05:00
Alex Hart
1e153e129c Ensure ShareViewModel#resolve is performed on the background thread.
Fixes #12696
2023-01-04 16:40:36 -05:00
Cody Henthorne
b546d661ba Update 1:1 call event copy. 2023-01-04 16:40:36 -05:00
Greyson Parrelli
6a1a657451 Fix story database observer updates to ensure rings are updated live. 2023-01-04 16:40:36 -05:00
Greyson Parrelli
ece087eaae Require non-null uri in UriAttachment.
Part of an effort to track down a different crash. This should make it
more obvious where the error is being made.
2023-01-04 16:40:36 -05:00
Alex Hart
a04590b658 Ensure pre-upload media is properly fanned out to group stories. 2023-01-04 16:40:36 -05:00
Alex Hart
eb6a14e686 Fix bad background on long text posts. 2023-01-04 16:40:36 -05:00
Alex Hart
c7bb0eadc2 Fix restore-state of filter pull view. 2023-01-04 16:40:36 -05:00
Nicholas
d70fe8f2cd Only notify for unauthorized response to messages endpoint. 2023-01-04 16:40:36 -05:00
Greyson Parrelli
0fd8f73cca Show username if appropriate in group chips. 2023-01-04 16:40:36 -05:00
Greyson Parrelli
b9fc36be5a Prevent crash when adding group member by username. 2023-01-04 16:40:36 -05:00
Alex Hart
4de27482bb Update blocking get call to safeBlockingGet. 2023-01-04 16:40:36 -05:00
Nicholas
ba347301cf Keep media preview image order even for RTL locales.
Addresses #12574.
2023-01-04 16:40:36 -05:00
Alex Hart
296a113c65 Add "You can pull to filter" tip. 2023-01-04 16:40:36 -05:00
Cody Henthorne
43fe789807 Add support for general media attachments to release notes channel messages. 2023-01-04 16:40:36 -05:00
Alex Hart
98dfd5bfbf Lower fade out duration of chat pill to 150ms. 2023-01-04 16:40:36 -05:00
Alex Hart
f387785a46 Add chat filter pill fade out on slide close. 2023-01-04 16:40:36 -05:00
Cody Henthorne
7b3d8d01ae Prevent dismissal of notification.
Fixes #12681
2023-01-03 11:03:06 -05:00
Alex Hart
1712442560 Add chat filter pill color lerp at close apex. 2023-01-03 10:47:53 -04:00
Nicholas
e4ddedcc48 Log type of connected headsets. 2023-01-03 09:33:08 -05:00
Alex Hart
14503b952a Add helper text when dragging filter at a low velocity. 2023-01-03 10:31:31 -04:00
Nicholas
5cb3e1cd02 Launch "Keep Muted Chats Archived". 2023-01-03 09:05:57 -05:00
Nicholas
7959343661 Add local notification when client receives HTTP 403
Also corrects typo in method name.
2023-01-02 17:20:42 -05:00
Alex Hart
52062679d4 Update filter chip padding to match spec. 2023-01-02 15:05:07 -04:00
Alex Hart
9e8350e8c2 Implement chat filter design feedback. 2023-01-02 12:19:04 -04:00
Sgn-32
495c91ba86 Use the correct delivery time in the message details
CLoses #11655
2023-01-02 09:51:20 -05:00
Greyson Parrelli
92b9fda6c7 Convert GroupTable to kotlin.
Also required converting some tests to mockk.
2023-01-01 23:05:02 -05:00
Greyson Parrelli
fecfd7cd78 Remove the rest of MmsSmsTable. 2022-12-31 13:43:12 -05:00
Greyson Parrelli
6cd6073bc7 Migrate most of MmsSmsTable. 2022-12-30 18:24:00 -05:00
Greyson Parrelli
f149c0adb9 Remove MmsSmsColumns.
All the columns got moved to MessageTable.
I kept the types though and renamed the class to MessageTypes because
it's a lot of boring domain-specific code.
2022-12-30 16:54:49 -05:00
Greyson Parrelli
3708cc5583 Add additional protections around recipientIds and threadIds matching. 2022-12-30 16:54:49 -05:00
Greyson Parrelli
4dd8e81db7 Fix some situations where MessageTable actions were doubled. 2022-12-30 16:54:49 -05:00
Cody Henthorne
06b414f4ef Add call disposition syncing. 2022-12-30 16:54:49 -05:00
Nicholas
d471647e12 Animate swapping of play/pause buttons. 2022-12-30 16:54:49 -05:00
Greyson Parrelli
dd3bad858d Prevent scrolling when context menu is showing on story landing page. 2022-12-30 15:11:57 -05:00
Cody Henthorne
0fe6538ce4 Fix media viewer rail items jumping around while paging. 2022-12-30 15:11:57 -05:00
Alex Hart
1e2f7f0775 Add currency selection logic update. 2022-12-30 15:11:57 -05:00
Cody Henthorne
055b4691d7 Fix video playback starting when off screen in media viewer. 2022-12-30 15:11:57 -05:00
Cody Henthorne
ebdfa88882 Schedule ExpireStoriesManager when viewing Stories tab. 2022-12-30 15:11:57 -05:00
Alex Hart
d79c4775b6 Add chat filter animation. 2022-12-30 15:11:56 -05:00
Cody Henthorne
a13599ae2a Add payment activation capability. 2022-12-30 15:11:56 -05:00
Alex Hart
96b2051400 Rotate chat-filters flag for internal testing. 2022-12-30 15:11:56 -05:00
Nicholas
ad6d1a2e8d Update styling of the media rail selection states. 2022-12-30 15:11:56 -05:00
Greyson Parrelli
eada1e96ee Improve emoji search rankings. 2022-12-30 15:11:56 -05:00
Greyson Parrelli
91fbc236ce Rename pnp capability to pni. 2022-12-30 15:11:56 -05:00
Greyson Parrelli
202f20893c Add internal setting for manually initializing PNP mode. 2022-12-30 15:11:56 -05:00
Greyson Parrelli
f1d204b834 Update SQLCipher to 4.5.1-S1 2022-12-30 15:11:56 -05:00
Greyson Parrelli
73e19209ff Fix some issues with projections result from MessageTable migration. 2022-12-30 15:11:56 -05:00
Greyson Parrelli
835bf3998f Perform large inserts in batches during MessageTable migration. 2022-12-30 15:11:56 -05:00
Greyson Parrelli
d83ef56ab1 Remove SmsMessageRecord. 2022-12-30 15:11:56 -05:00
Greyson Parrelli
a84a9c5381 Fetch isQuoted status in bulk during conversation load.
Improves overall time to load a page of messages by ~50%.
2022-12-30 15:11:56 -05:00
Greyson Parrelli
c6f29fc950 Migrate queued jobs during SMS migration. 2022-12-30 15:11:56 -05:00
Greyson Parrelli
4d9dc42868 Improve the performance of the migration by ~4x. 2022-12-30 15:11:56 -05:00
Greyson Parrelli
32b66643c5 Rename PushMediaSendJob -> IndividualSendJob. 2022-12-30 15:11:56 -05:00
Greyson Parrelli
3850c9c89d Remove isMms from MessageId. 2022-12-30 15:11:56 -05:00
Greyson Parrelli
60ae883df6 Rename SignalDatabase.sms/mms to SignalDatabase.messages 2022-12-30 15:11:56 -05:00
Greyson Parrelli
a7e3bdc892 Rename OutgoingMediaMessage -> OutgoingMessage. 2022-12-30 15:11:56 -05:00
Greyson Parrelli
9b60bd9a4b Remove OutgoingTextMessage and PushTextSendJob. 2022-12-30 15:11:56 -05:00
Greyson Parrelli
e9d98b7d39 Migrate SMS messages into the MMS table. 2022-12-30 15:11:56 -05:00
Greyson Parrelli
cb0e7ade14 Bump version to 6.6.3 2022-12-24 11:13:04 -05:00
Greyson Parrelli
268f5c807d Ensure that remapped records are valid.
Fixes #12691
2022-12-24 11:13:04 -05:00
5207 changed files with 403454 additions and 177981 deletions

View File

@@ -1,4 +1,9 @@
root = true
[*.kt]
[*.{kt,kts}]
indent_size = 2
ij_kotlin_allow_trailing_comma_on_call_site = false
ij_kotlin_allow_trailing_comma = false
ktlint_code_style = intellij_idea
twitter_compose_allowed_composition_locals=LocalExtendedColors
ktlint_standard_class-naming = disabled

View File

@@ -14,23 +14,23 @@ permissions:
jobs:
build:
runs-on: ubuntu-latest
runs-on: ubuntu-latest-8-cores
steps:
- uses: actions/checkout@v3
with:
submodules: true
- name: set up JDK 11
- name: set up JDK 17
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 11
java-version: 17
cache: gradle
- name: Validate Gradle Wrapper
uses: gradle/wrapper-validation-action@v1
- name: Remove Android 31 (S)
run: $ANDROID_HOME/tools/bin/sdkmanager --uninstall "platforms;android-31"
- name: Build with Gradle
run: ./gradlew qa

84
.github/workflows/diffuse.yml vendored Normal file
View File

@@ -0,0 +1,84 @@
name: APK Diff
on:
pull_request:
permissions:
contents: read # to fetch code (actions/checkout)
pull-requests: write # to comment on PR
jobs:
assemble-base:
if: ${{ github.repository != 'signalapp/Signal-Android' }}
runs-on: ubuntu-latest-8-cores
steps:
- uses: actions/checkout@v3
with:
submodules: true
ref: ${{ github.event.pull_request.base.sha }}
- name: set up JDK 17
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 17
cache: gradle
- name: Validate Gradle Wrapper
uses: gradle/wrapper-validation-action@v1
- name: Cache base apk
id: cache-base
uses: actions/cache@v3
with:
path: diffuse-base.apk
key: diffuse-${{ github.event.pull_request.base.sha }}
- name: Build with Gradle
if: steps.cache-base.outputs.cache-hit != 'true'
run: ./gradlew assemblePlayProdRelease
- name: Copy base apk
if: steps.cache-base.outputs.cache-hit != 'true'
run: mv app/build/outputs/apk/playProd/release/*arm64*.apk diffuse-base.apk
- uses: actions/checkout@v3
with:
submodules: true
clean: 'false'
- name: Build with Gradle
run: ./gradlew assemblePlayProdRelease
- name: Copy PR apk
run: mv app/build/outputs/apk/playProd/release/*arm64*.apk diffuse-new.apk
- id: diffuse
uses: usefulness/diffuse-action@v1
with:
old-file-path: diffuse-base.apk
new-file-path: diffuse-new.apk
- uses: peter-evans/find-comment@v2
id: find-comment
with:
issue-number: ${{ github.event.pull_request.number }}
body-includes: Diffuse output
- uses: peter-evans/create-or-update-comment@v3
with:
body: |
Diffuse output:
${{ steps.diffuse.outputs.diff-gh-comment }}
edit-mode: replace
comment-id: ${{ steps.find-comment.outputs.comment-id }}
issue-number: ${{ github.event.pull_request.number }}
token: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/upload-artifact@v3
with:
name: diffuse-output
path: ${{ steps.diffuse.outputs.diff-file }}

2
.gitignore vendored
View File

@@ -3,6 +3,7 @@ captures/
project.properties
keystore.debug.properties
keystore.staging.properties
nightly-url.txt
.project
.settings
bin/
@@ -28,3 +29,4 @@ jni/libspeex/.deps/
pkcs11.password
dev.keystore
maps.key
local/

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "libwebp"]
path = libwebp
url = https://github.com/webmproject/libwebp.git

View File

@@ -41,16 +41,6 @@
</value>
</option>
</JavaCodeStyleSettings>
<JetCodeStyleSettings>
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
<value>
<package name="kotlinx.android.synthetic" alias="false" withSubpackages="true" />
<package name="io.ktor" alias="false" withSubpackages="true" />
</value>
</option>
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" />
</JetCodeStyleSettings>
<codeStyleSettings language="HTML">
<indentOptions>
<option name="INDENT_SIZE" value="2" />
@@ -222,5 +212,12 @@
</rules>
</arrangement>
</codeStyleSettings>
<codeStyleSettings language="kotlin">
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
</code_scheme>
</component>

6
.idea/copyright/Signal.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<component name="CopyrightManager">
<copyright>
<option name="notice" value="Copyright &amp;#36;today.year Signal Messenger, LLC&#10;SPDX-License-Identifier: AGPL-3.0-only" />
<option name="myName" value="Signal" />
</copyright>
</component>

7
.idea/copyright/profiles_settings.xml generated Normal file
View File

@@ -0,0 +1,7 @@
<component name="CopyrightManager">
<settings>
<module2copyright>
<element module="All" copyright="Signal" />
</module2copyright>
</settings>
</component>

View File

@@ -0,0 +1,9 @@
/*
* Copyright ${YEAR} Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end
#parse("File Header.java")
public @interface ${NAME} {
}

9
.idea/fileTemplates/internal/Class.java generated Normal file
View File

@@ -0,0 +1,9 @@
/*
* Copyright ${YEAR} Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end
#parse("File Header.java")
public class ${NAME} {
}

9
.idea/fileTemplates/internal/Enum.java generated Normal file
View File

@@ -0,0 +1,9 @@
/*
* Copyright ${YEAR} Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end
#parse("File Header.java")
public enum ${NAME} {
}

View File

@@ -0,0 +1,9 @@
/*
* Copyright ${YEAR} Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end
#parse("File Header.java")
public interface ${NAME} {
}

View File

@@ -0,0 +1,11 @@
/*
* Copyright ${YEAR} Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME}
#end
#parse("File Header.java")
class ${NAME} {
}

View File

@@ -0,0 +1,11 @@
/*
* Copyright ${YEAR} Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME}
#end
#parse("File Header.java")
enum class ${NAME} {
}

View File

@@ -0,0 +1,9 @@
/*
* Copyright ${YEAR} Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME}
#end
#parse("File Header.java")

View File

@@ -0,0 +1,11 @@
/*
* Copyright ${YEAR} Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME}
#end
#parse("File Header.java")
interface ${NAME} {
}

View File

@@ -15,6 +15,12 @@ Truths which we believe to be self-evident:
1. **There is no such thing as time.** Protocol ideas that require synchronized clocks are doomed to failure.
## Building
1. You'll need to get the `libwebp` submodule after checking out the repository with `git submodule init && git submodule update`
1. Most things are pretty straightforward, and opening the project in Android Studio should get you most of the way there.
1. Depending on your configuration, you'll also likely need to install additional SDK Tool components, namely the versions of NDK and CMake we are currently using in our [Docker](https://github.com/signalapp/Signal-Android/blob/main/reproducible-builds/Dockerfile#L30) configuration.
## Issues
### Useful bug reports

152
LICENSE
View File

@@ -1,23 +1,21 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
software for all its users.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
@@ -26,44 +24,34 @@ them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.
The precise terms and conditions for copying, distribution and
modification follow.
@@ -72,7 +60,7 @@ modification follow.
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"This License" refers to version 3 of the GNU Affero General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
@@ -549,35 +537,45 @@ to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
the GNU Affero General Public License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
@@ -619,3 +617,45 @@ Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<https://www.gnu.org/licenses/>.

View File

@@ -1,4 +1,4 @@
# Signal Android
# Signal Android
Signal is a simple, powerful, and secure messenger.
@@ -54,8 +54,8 @@ The form and manner of this distribution makes it eligible for export under the
## License
Copyright 2013-2022 Signal
Copyright 2013-2024 Signal Messenger, LLC
Licensed under the GPLv3: http://www.gnu.org/licenses/gpl-3.0.html
Licensed under the GNU AGPLv3: https://www.gnu.org/licenses/agpl-3.0.html
Google Play and the Google Play logo are trademarks of Google LLC.

2
apntool/.gitignore vendored
View File

@@ -1,2 +0,0 @@
*.db
*.db.gz

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,106 +0,0 @@
import sys
import re
import argparse
import sqlite3
import gzip
from progressbar import ProgressBar, Counter, Timer
from lxml import etree
parser = argparse.ArgumentParser(prog='apntool', description="""Process Android's apn xml files and drop them into an
easily queryable SQLite db. Tested up to version 9 of
their APN file.""")
parser.add_argument('-v', '--version', action='version', version='%(prog)s v1.1')
parser.add_argument('-i', '--input', help='the xml file to parse', default='apns.xml', required=False)
parser.add_argument('-o', '--output', help='the sqlite db output file', default='apns.db', required=False)
parser.add_argument('--quiet', help='do not show progress or verbose instructions', action='store_true', required=False)
parser.add_argument('--no-gzip', help="do not gzip after creation", action='store_true', required=False)
args = parser.parse_args()
def normalized(target):
o2_typo = re.compile(r"02\.co\.uk")
port_typo = re.compile(r"(\d+\.\d+\.\d+\.\d+)\.(\d+)")
leading_zeros = re.compile(r"(/|\.|^)0+(\d+)")
subbed = o2_typo.sub(r'o2.co.uk', target)
subbed = port_typo.sub(r'\1:\2', subbed)
subbed = leading_zeros.sub(r'\1\2', subbed)
return subbed
try:
connection = sqlite3.connect(args.output)
cursor = connection.cursor()
cursor.execute('SELECT SQLITE_VERSION()')
version = cursor.fetchone()
if not args.quiet:
print("SQLite version: %s" % version)
print("Opening %s" % args.input)
cursor.execute("PRAGMA legacy_file_format=ON")
cursor.execute("PRAGMA journal_mode=DELETE")
cursor.execute("PRAGMA page_size=32768")
cursor.execute("VACUUM")
cursor.execute("DROP TABLE IF EXISTS apns")
cursor.execute("""CREATE TABLE apns(_id INTEGER PRIMARY KEY, mccmnc TEXT, mcc TEXT, mnc TEXT, carrier TEXT,
apn TEXT, mmsc TEXT, port INTEGER, type TEXT, protocol TEXT, bearer TEXT, roaming_protocol TEXT,
carrier_enabled INTEGER, mmsproxy TEXT, mmsport INTEGER, proxy TEXT, mvno_match_data TEXT,
mvno_type TEXT, authtype INTEGER, user TEXT, password TEXT, server TEXT)""")
apns = etree.parse(args.input)
root = apns.getroot()
pbar = None
if not args.quiet:
pbar = ProgressBar(widgets=['Processed: ', Counter(), ' apns (', Timer(), ')'], maxval=len(list(root))).start()
count = 0
for apn in root.iter("apn"):
if apn.get("mmsc") is None:
continue
sqlvars = ["?" for x in apn.attrib.keys()] + ["?"]
mccmnc = "%s%s" % (apn.get("mcc"), apn.get("mnc"))
normalized_mmsc = normalized(apn.get("mmsc"))
if normalized_mmsc != apn.get("mmsc"):
print("normalize MMSC: %s => %s" % (apn.get("mmsc"), normalized_mmsc))
apn.set("mmsc", normalized_mmsc)
if not apn.get("mmsproxy") is None:
normalized_mmsproxy = normalized(apn.get("mmsproxy"))
if normalized_mmsproxy != apn.get("mmsproxy"):
print("normalize proxy: %s => %s" % (apn.get("mmsproxy"), normalized_mmsproxy))
apn.set("mmsproxy", normalized_mmsproxy)
values = [apn.get(attrib) for attrib in apn.attrib.keys()] + [mccmnc]
keys = apn.attrib.keys() + ["mccmnc"]
cursor.execute("SELECT 1 FROM apns WHERE mccmnc = ? AND apn = ?", [mccmnc, apn.get("apn")])
if cursor.fetchone() is None:
statement = "INSERT INTO apns (%s) VALUES (%s)" % (", ".join(keys), ", ".join(sqlvars))
cursor.execute(statement, values)
count += 1
if not args.quiet:
pbar.update(count)
if not args.quiet:
pbar.finish()
connection.commit()
print("Successfully written to %s" % args.output)
if not args.no_gzip:
gzipped_file = "%s.gz" % (args.output,)
with open(args.output, 'rb') as orig:
with gzip.open(gzipped_file, 'wb') as gzipped:
gzipped.writelines(orig)
print("Successfully gzipped to %s" % gzipped_file)
if not args.quiet:
print("\nTo include this in the distribution, copy it to the project's assets/databases/ directory.")
print("If you support API 10 or lower, you must use the gzipped version to avoid corruption.")
except sqlite3.Error as e:
if connection:
connection.rollback()
print("Error: %s" % e.args[0])
sys.exit(1)
finally:
if connection:
connection.close()

View File

@@ -1,3 +0,0 @@
argparse>=1.2.1
lxml>=3.3.3
progressbar-latest>=2.4

View File

@@ -1,664 +0,0 @@
import com.android.build.api.dsl.ManagedVirtualDevice
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'com.google.protobuf'
apply plugin: 'androidx.navigation.safeargs'
apply plugin: 'org.jlleitschuh.gradle.ktlint'
apply from: 'translations.gradle'
apply plugin: 'org.jetbrains.kotlin.android'
apply plugin: 'app.cash.exhaustive'
apply plugin: 'kotlin-parcelize'
apply from: 'static-ips.gradle'
repositories {
maven {
url "https://raw.githubusercontent.com/signalapp/maven/master/sqlcipher/release/"
content {
includeGroupByRegex "org\\.signal.*"
}
}
google()
mavenCentral()
mavenLocal()
maven {
url "https://dl.cloudsmith.io/qxAgwaeEE1vN8aLU/mobilecoin/mobilecoin/maven/"
}
jcenter {
content {
includeVersion "mobi.upod", "time-duration-picker", "1.1.3"
}
}
}
protobuf {
protoc {
artifact = 'com.google.protobuf:protoc:3.18.0'
}
generateProtoTasks {
all().each { task ->
task.builtins {
java {
option "lite"
}
}
}
}
}
ktlint {
// Use a newer version to resolve https://github.com/JLLeitschuh/ktlint-gradle/issues/507
version = "0.43.2"
}
def canonicalVersionCode = 1184
def canonicalVersionName = "6.6.2"
def postFixSize = 100
def abiPostFix = ['universal' : 0,
'armeabi-v7a' : 1,
'arm64-v8a' : 2,
'x86' : 3,
'x86_64' : 4]
def keystores = [ 'debug' : loadKeystoreProperties('keystore.debug.properties') ]
def selectableVariants = [
'nightlyProdSpinner',
'nightlyProdPerf',
'nightlyProdRelease',
'playProdDebug',
'playProdSpinner',
'playProdPerf',
'playProdInstrumentation',
'playProdRelease',
'playStagingDebug',
'playStagingSpinner',
'playStagingPerf',
'playStagingInstrumentation',
'playStagingRelease',
'websiteProdSpinner',
'websiteProdRelease',
]
android {
buildToolsVersion BUILD_TOOL_VERSION
compileSdkVersion COMPILE_SDK
flavorDimensions 'distribution', 'environment'
useLibrary 'org.apache.http.legacy'
testBuildType 'instrumentation'
kotlinOptions {
jvmTarget = "1.8"
freeCompilerArgs = ["-Xallow-result-return-type"]
}
signingConfigs {
if (keystores.debug != null) {
debug {
storeFile file("${project.rootDir}/${keystores.debug.storeFile}")
storePassword keystores.debug.storePassword
keyAlias keystores.debug.keyAlias
keyPassword keystores.debug.keyPassword
}
}
}
testOptions {
execution 'ANDROIDX_TEST_ORCHESTRATOR'
unitTests {
includeAndroidResources = true
}
managedDevices {
devices {
pixel3api30 (ManagedVirtualDevice) {
device = "Pixel 3"
apiLevel = 30
systemImageSource = "google-atd"
require64Bit = false
}
}
}
}
lintOptions {
checkReleaseBuilds false
abortOnError true
baseline file("lint-baseline.xml")
disable "LintError"
}
sourceSets {
test {
java.srcDirs += "$projectDir/src/testShared"
}
androidTest {
java.srcDirs += "$projectDir/src/testShared"
}
}
compileOptions {
coreLibraryDesugaringEnabled true
sourceCompatibility JAVA_VERSION
targetCompatibility JAVA_VERSION
}
packagingOptions {
exclude 'LICENSE.txt'
exclude 'LICENSE'
exclude 'NOTICE'
exclude 'asm-license.txt'
exclude 'META-INF/LICENSE'
exclude 'META-INF/NOTICE'
exclude 'META-INF/proguard/androidx-annotations.pro'
exclude 'libsignal_jni.dylib'
exclude 'signal_jni.dll'
}
buildFeatures {
viewBinding true
}
defaultConfig {
versionCode canonicalVersionCode * postFixSize
versionName canonicalVersionName
minSdkVersion MINIMUM_SDK
targetSdkVersion TARGET_SDK
multiDexEnabled true
vectorDrawables.useSupportLibrary = true
project.ext.set("archivesBaseName", "Signal")
manifestPlaceholders = [mapsKey:"AIzaSyCSx9xea86GwDKGznCAULE9Y5a8b-TfN9U"]
buildConfigField "long", "BUILD_TIMESTAMP", getLastCommitTimestamp() + "L"
buildConfigField "String", "GIT_HASH", "\"${getGitHash()}\""
buildConfigField "String", "SIGNAL_URL", "\"https://chat.signal.org\""
buildConfigField "String", "STORAGE_URL", "\"https://storage.signal.org\""
buildConfigField "String", "SIGNAL_CDN_URL", "\"https://cdn.signal.org\""
buildConfigField "String", "SIGNAL_CDN2_URL", "\"https://cdn2.signal.org\""
buildConfigField "String", "SIGNAL_CONTACT_DISCOVERY_URL", "\"https://api.directory.signal.org\""
buildConfigField "String", "SIGNAL_CDSI_URL", "\"https://cdsi.signal.org\""
buildConfigField "String", "SIGNAL_SERVICE_STATUS_URL", "\"uptime.signal.org\""
buildConfigField "String", "SIGNAL_KEY_BACKUP_URL", "\"https://api.backup.signal.org\""
buildConfigField "String", "SIGNAL_SFU_URL", "\"https://sfu.voip.signal.org\""
buildConfigField "String", "SIGNAL_STAGING_SFU_URL", "\"https://sfu.staging.voip.signal.org\""
buildConfigField "String[]", "SIGNAL_SFU_INTERNAL_NAMES", "new String[]{\"Test\", \"Staging\", \"Development\"}"
buildConfigField "String[]", "SIGNAL_SFU_INTERNAL_URLS", "new String[]{\"https://sfu.test.voip.signal.org\", \"https://sfu.staging.voip.signal.org\", \"https://sfu.staging.test.voip.signal.org\"}"
buildConfigField "String", "CONTENT_PROXY_HOST", "\"contentproxy.signal.org\""
buildConfigField "int", "CONTENT_PROXY_PORT", "443"
buildConfigField "String[]", "SIGNAL_SERVICE_IPS", service_ips
buildConfigField "String[]", "SIGNAL_STORAGE_IPS", storage_ips
buildConfigField "String[]", "SIGNAL_CDN_IPS", cdn_ips
buildConfigField "String[]", "SIGNAL_CDN2_IPS", cdn2_ips
buildConfigField "String[]", "SIGNAL_CDS_IPS", cds_ips
buildConfigField "String[]", "SIGNAL_KBS_IPS", kbs_ips
buildConfigField "String[]", "SIGNAL_SFU_IPS", sfu_ips
buildConfigField "String[]", "SIGNAL_CONTENT_PROXY_IPS", content_proxy_ips
buildConfigField "String", "SIGNAL_AGENT", "\"OWA\""
buildConfigField "String", "CDS_MRENCLAVE", "\"74778bb0f93ae1f78c26e67152bab0bbeb693cd56d1bb9b4e9244157acc58081\""
buildConfigField "String", "CDSI_MRENCLAVE", "\"ef4787a56a154ac6d009138cac17155acd23cfe4329281252365dd7c252e7fbf\""
buildConfigField "org.thoughtcrime.securesms.KbsEnclave", "KBS_ENCLAVE", "new org.thoughtcrime.securesms.KbsEnclave(\"e18376436159cda3ad7a45d9320e382e4a497f26b0dca34d8eab0bd0139483b5\", " +
"\"3a485adb56e2058ef7737764c738c4069dd62bc457637eafb6bbce1ce29ddb89\", " +
"\"45627094b2ea4a66f4cf0b182858a8dcf4b8479122c3820fe7fd0551a6d4cf5c\")"
buildConfigField "org.thoughtcrime.securesms.KbsEnclave[]", "KBS_FALLBACKS", "new org.thoughtcrime.securesms.KbsEnclave[] { new org.thoughtcrime.securesms.KbsEnclave(\"0cedba03535b41b67729ce9924185f831d7767928a1d1689acb689bc079c375f\", " +
"\"187d2739d22be65e74b65f0055e74d31310e4267e5fac2b1246cc8beba81af39\", " +
"\"ee19f1965b1eefa3dc4204eb70c04f397755f771b8c1909d080c04dad2a6a9ba\") }"
buildConfigField "String", "UNIDENTIFIED_SENDER_TRUST_ROOT", "\"BXu6QIKVz5MA8gstzfOgRQGqyLqOwNKHL6INkv3IHWMF\""
buildConfigField "String", "ZKGROUP_SERVER_PUBLIC_PARAMS", "\"AMhf5ywVwITZMsff/eCyudZx9JDmkkkbV6PInzG4p8x3VqVJSFiMvnvlEKWuRob/1eaIetR31IYeAbm0NdOuHH8Qi+Rexi1wLlpzIo1gstHWBfZzy1+qHRV5A4TqPp15YzBPm0WSggW6PbSn+F4lf57VCnHF7p8SvzAA2ZZJPYJURt8X7bbg+H3i+PEjH9DXItNEqs2sNcug37xZQDLm7X36nOoGPs54XsEGzPdEV+itQNGUFEjY6X9Uv+Acuks7NpyGvCoKxGwgKgE5XyJ+nNKlyHHOLb6N1NuHyBrZrgtY/JYJHRooo5CEqYKBqdFnmbTVGEkCvJKxLnjwKWf+fEPoWeQFj5ObDjcKMZf2Jm2Ae69x+ikU5gBXsRmoF94GXTLfN0/vLt98KDPnxwAQL9j5V1jGOY8jQl6MLxEs56cwXN0dqCnImzVH3TZT1cJ8SW1BRX6qIVxEzjsSGx3yxF3suAilPMqGRp4ffyopjMD1JXiKR2RwLKzizUe5e8XyGOy9fplzhw3jVzTRyUZTRSZKkMLWcQ/gv0E4aONNqs4P\""
buildConfigField "String[]", "LANGUAGES", "new String[]{\"" + autoResConfig().collect { s -> s.replace('-r', '_') }.join('", "') + '"}'
buildConfigField "int", "CANONICAL_VERSION_CODE", "$canonicalVersionCode"
buildConfigField "String", "DEFAULT_CURRENCIES", "\"EUR,AUD,GBP,CAD,CNY\""
buildConfigField "String", "GIPHY_API_KEY", "\"3o6ZsYH6U6Eri53TXy\""
buildConfigField "String", "SIGNAL_CAPTCHA_URL", "\"https://signalcaptchas.org/registration/generate.html\""
buildConfigField "String", "RECAPTCHA_PROOF_URL", "\"https://signalcaptchas.org/challenge/generate.html\""
buildConfigField "String", "BUILD_DISTRIBUTION_TYPE", "\"unset\""
buildConfigField "String", "BUILD_ENVIRONMENT_TYPE", "\"unset\""
buildConfigField "String", "BUILD_VARIANT_TYPE", "\"unset\""
buildConfigField "String", "BADGE_STATIC_ROOT", "\"https://updates2.signal.org/static/badges/\""
buildConfigField "String", "STRIPE_PUBLISHABLE_KEY", "\"pk_live_6cmGZopuTsV8novGgJJW9JpC00vLIgtQ1D\""
ndk {
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
}
resConfigs autoResConfig()
splits {
abi {
enable true
reset()
include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
universalApk true
}
}
testInstrumentationRunner "org.thoughtcrime.securesms.testing.SignalTestRunner"
testInstrumentationRunnerArguments clearPackageData: 'true'
}
buildTypes {
debug {
if (keystores['debug'] != null) {
signingConfig signingConfigs.debug
}
isDefault true
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard/proguard-firebase-messaging.pro',
'proguard/proguard-google-play-services.pro',
'proguard/proguard-jackson.pro',
'proguard/proguard-sqlite.pro',
'proguard/proguard-appcompat-v7.pro',
'proguard/proguard-square-okhttp.pro',
'proguard/proguard-square-okio.pro',
'proguard/proguard-rounded-image-view.pro',
'proguard/proguard-glide.pro',
'proguard/proguard-shortcutbadger.pro',
'proguard/proguard-retrofit.pro',
'proguard/proguard-webrtc.pro',
'proguard/proguard-klinker.pro',
'proguard/proguard-retrolambda.pro',
'proguard/proguard-okhttp.pro',
'proguard/proguard-ez-vcard.pro',
'proguard/proguard.cfg'
testProguardFiles 'proguard/proguard-automation.pro',
'proguard/proguard.cfg'
manifestPlaceholders = [mapsKey:getMapsKey()]
buildConfigField "String", "BUILD_VARIANT_TYPE", "\"Debug\""
}
instrumentation {
initWith debug
isDefault false
minifyEnabled false
matchingFallbacks = ['debug']
applicationIdSuffix ".instrumentation"
buildConfigField "String", "BUILD_VARIANT_TYPE", "\"Instrumentation\""
}
spinner {
initWith debug
isDefault false
minifyEnabled false
matchingFallbacks = ['debug']
buildConfigField "String", "BUILD_VARIANT_TYPE", "\"Spinner\""
}
release {
minifyEnabled true
proguardFiles = buildTypes.debug.proguardFiles
buildConfigField "String", "BUILD_VARIANT_TYPE", "\"Release\""
}
perf {
initWith debug
isDefault false
debuggable false
minifyEnabled true
matchingFallbacks = ['debug']
buildConfigField "String", "BUILD_VARIANT_TYPE", "\"Perf\""
}
}
productFlavors {
play {
dimension 'distribution'
isDefault true
ext.websiteUpdateUrl = "null"
buildConfigField "boolean", "PLAY_STORE_DISABLED", "false"
buildConfigField "String", "NOPLAY_UPDATE_URL", "$ext.websiteUpdateUrl"
buildConfigField "String", "BUILD_DISTRIBUTION_TYPE", "\"play\""
}
website {
dimension 'distribution'
ext.websiteUpdateUrl = "https://updates.signal.org/android"
buildConfigField "boolean", "PLAY_STORE_DISABLED", "true"
buildConfigField "String", "NOPLAY_UPDATE_URL", "\"$ext.websiteUpdateUrl\""
buildConfigField "String", "BUILD_DISTRIBUTION_TYPE", "\"website\""
}
nightly {
dimension 'distribution'
versionNameSuffix "-nightly-untagged-${getDateSuffix()}"
ext.websiteUpdateUrl = "null"
buildConfigField "boolean", "PLAY_STORE_DISABLED", "false"
buildConfigField "String", "NOPLAY_UPDATE_URL", "$ext.websiteUpdateUrl"
buildConfigField "String", "BUILD_DISTRIBUTION_TYPE", "\"nightly\""
}
prod {
dimension 'environment'
isDefault true
buildConfigField "String", "MOBILE_COIN_ENVIRONMENT", "\"mainnet\""
buildConfigField "String", "BUILD_ENVIRONMENT_TYPE", "\"Prod\""
}
staging {
dimension 'environment'
applicationIdSuffix ".staging"
buildConfigField "String", "SIGNAL_URL", "\"https://chat.staging.signal.org\""
buildConfigField "String", "STORAGE_URL", "\"https://storage-staging.signal.org\""
buildConfigField "String", "SIGNAL_CDN_URL", "\"https://cdn-staging.signal.org\""
buildConfigField "String", "SIGNAL_CDN2_URL", "\"https://cdn2-staging.signal.org\""
buildConfigField "String", "SIGNAL_CONTACT_DISCOVERY_URL", "\"https://api-staging.directory.signal.org\""
buildConfigField "String", "SIGNAL_CDSI_URL", "\"https://cdsi.staging.signal.org\""
buildConfigField "String", "SIGNAL_KEY_BACKUP_URL", "\"https://api-staging.backup.signal.org\""
buildConfigField "String", "CDS_MRENCLAVE", "\"74778bb0f93ae1f78c26e67152bab0bbeb693cd56d1bb9b4e9244157acc58081\""
buildConfigField "org.thoughtcrime.securesms.KbsEnclave", "KBS_ENCLAVE", "new org.thoughtcrime.securesms.KbsEnclave(\"39963b736823d5780be96ab174869a9499d56d66497aa8f9b2244f777ebc366b\", " +
"\"9dbc6855c198e04f21b5cc35df839fdcd51b53658454dfa3f817afefaffc95ef\", " +
"\"45627094b2ea4a66f4cf0b182858a8dcf4b8479122c3820fe7fd0551a6d4cf5c\")"
buildConfigField "org.thoughtcrime.securesms.KbsEnclave[]", "KBS_FALLBACKS", "new org.thoughtcrime.securesms.KbsEnclave[] { new org.thoughtcrime.securesms.KbsEnclave(\"dd6f66d397d9e8cf6ec6db238e59a7be078dd50e9715427b9c89b409ffe53f99\", " +
"\"4200003414528c151e2dccafbc87aa6d3d66a5eb8f8c05979a6e97cb33cd493a\", " +
"\"ee19f1965b1eefa3dc4204eb70c04f397755f771b8c1909d080c04dad2a6a9ba\") }"
buildConfigField "String", "UNIDENTIFIED_SENDER_TRUST_ROOT", "\"BbqY1DzohE4NUZoVF+L18oUPrK3kILllLEJh2UnPSsEx\""
buildConfigField "String", "ZKGROUP_SERVER_PUBLIC_PARAMS", "\"ABSY21VckQcbSXVNCGRYJcfWHiAMZmpTtTELcDmxgdFbtp/bWsSxZdMKzfCp8rvIs8ocCU3B37fT3r4Mi5qAemeGeR2X+/YmOGR5ofui7tD5mDQfstAI9i+4WpMtIe8KC3wU5w3Inq3uNWVmoGtpKndsNfwJrCg0Hd9zmObhypUnSkfYn2ooMOOnBpfdanRtrvetZUayDMSC5iSRcXKpdlukrpzzsCIvEwjwQlJYVPOQPj4V0F4UXXBdHSLK05uoPBCQG8G9rYIGedYsClJXnbrgGYG3eMTG5hnx4X4ntARBgELuMWWUEEfSK0mjXg+/2lPmWcTZWR9nkqgQQP0tbzuiPm74H2wMO4u1Wafe+UwyIlIT9L7KLS19Aw8r4sPrXZSSsOZ6s7M1+rTJN0bI5CKY2PX29y5Ok3jSWufIKcgKOnWoP67d5b2du2ZVJjpjfibNIHbT/cegy/sBLoFwtHogVYUewANUAXIaMPyCLRArsKhfJ5wBtTminG/PAvuBdJ70Z/bXVPf8TVsR292zQ65xwvWTejROW6AZX6aqucUj\""
buildConfigField "String", "MOBILE_COIN_ENVIRONMENT", "\"testnet\""
buildConfigField "String", "SIGNAL_CAPTCHA_URL", "\"https://signalcaptchas.org/staging/registration/generate.html\""
buildConfigField "String", "RECAPTCHA_PROOF_URL", "\"https://signalcaptchas.org/staging/challenge/generate.html\""
buildConfigField "String", "BUILD_ENVIRONMENT_TYPE", "\"Staging\""
buildConfigField "String", "STRIPE_PUBLISHABLE_KEY", "\"pk_test_sngOd8FnXNkpce9nPXawKrJD00kIDngZkD\""
}
}
android.applicationVariants.all { variant ->
variant.outputs.each { output ->
if (output.baseName.contains('nightly')) {
output.versionCodeOverride = canonicalVersionCode * postFixSize + 5
def tag = getCurrentGitTag()
if (tag != null && tag.length() > 0) {
if (tag.startsWith("v")) {
tag = tag.substring(1)
}
output.versionNameOverride = tag
}
} else {
output.outputFileName = output.outputFileName.replace(".apk", "-${variant.versionName}.apk")
def abiName = output.getFilter("ABI") ?: 'universal'
def postFix = abiPostFix.get(abiName, 0)
if (postFix >= postFixSize) throw new AssertionError("postFix is too large")
output.versionCodeOverride = canonicalVersionCode * postFixSize + postFix
}
}
}
android.variantFilter { variant ->
def distribution = variant.getFlavors().get(0).name
def environment = variant.getFlavors().get(1).name
def buildType = variant.buildType.name
def fullName = distribution + environment.capitalize() + buildType.capitalize()
if (!selectableVariants.contains(fullName)) {
variant.setIgnore(true)
}
}
}
dependencies {
implementation libs.androidx.core.ktx
implementation libs.androidx.fragment.ktx
lintChecks project(':lintchecks')
coreLibraryDesugaring libs.android.tools.desugar
implementation (libs.androidx.appcompat) {
version {
strictly '1.5.1'
}
}
implementation libs.androidx.window.window
implementation libs.androidx.window.java
implementation libs.androidx.recyclerview
implementation libs.material.material
implementation libs.androidx.legacy.support
implementation libs.androidx.cardview
implementation libs.androidx.preference
implementation libs.androidx.legacy.preference
implementation libs.androidx.gridlayout
implementation libs.androidx.exifinterface
implementation libs.androidx.constraintlayout
implementation libs.androidx.multidex
implementation libs.androidx.navigation.fragment.ktx
implementation libs.androidx.navigation.ui.ktx
implementation libs.androidx.lifecycle.viewmodel.ktx
implementation libs.androidx.lifecycle.livedata.ktx
implementation libs.androidx.lifecycle.process
implementation libs.androidx.lifecycle.viewmodel.savedstate
implementation libs.androidx.lifecycle.common.java8
implementation libs.androidx.lifecycle.reactivestreams.ktx
implementation libs.androidx.camera.core
implementation libs.androidx.camera.camera2
implementation libs.androidx.camera.lifecycle
implementation libs.androidx.camera.view
implementation libs.androidx.concurrent.futures
implementation libs.androidx.autofill
implementation libs.androidx.biometric
implementation libs.androidx.sharetarget
implementation (libs.firebase.messaging) {
exclude group: 'com.google.firebase', module: 'firebase-core'
exclude group: 'com.google.firebase', module: 'firebase-analytics'
exclude group: 'com.google.firebase', module: 'firebase-measurement-connector'
}
implementation libs.google.play.services.maps
implementation libs.google.play.services.auth
implementation libs.bundles.exoplayer
implementation libs.conscrypt.android
implementation libs.signal.aesgcmprovider
implementation project(':libsignal-service')
implementation project(':paging')
implementation project(':core-util')
implementation project(':glide-config')
implementation project(':video')
implementation project(':device-transfer')
implementation project(':image-editor')
implementation project(':donations')
implementation project(':contacts')
implementation project(':qr')
implementation project(':sms-exporter')
implementation project(':sticky-header-grid')
implementation project(':photoview')
implementation libs.libsignal.android
implementation libs.google.protobuf.javalite
implementation(libs.mobilecoin) {
exclude group: 'com.google.protobuf'
}
implementation(libs.signal.argon2) {
artifact {
type = "aar"
}
}
implementation libs.signal.ringrtc
implementation libs.leolin.shortcutbadger
implementation libs.emilsjolander.stickylistheaders
implementation libs.jpardogo.materialtabstrip
implementation libs.apache.httpclient.android
implementation libs.glide.glide
implementation libs.roundedimageview
implementation libs.materialish.progress
implementation libs.greenrobot.eventbus
implementation libs.waitingdots
implementation libs.google.zxing.android.integration
implementation libs.time.duration.picker
implementation libs.google.zxing.core
implementation libs.google.flexbox
implementation (libs.subsampling.scale.image.view) {
exclude group: 'com.android.support', module: 'support-annotations'
}
implementation (libs.android.tooltips) {
exclude group: 'com.android.support', module: 'appcompat-v7'
}
implementation (libs.android.smsmms) {
exclude group: 'com.squareup.okhttp', module: 'okhttp'
exclude group: 'com.squareup.okhttp', module: 'okhttp-urlconnection'
}
implementation libs.stream
implementation libs.lottie
implementation libs.signal.android.database.sqlcipher
implementation libs.androidx.sqlite
implementation (libs.google.ez.vcard) {
exclude group: 'com.fasterxml.jackson.core'
exclude group: 'org.freemarker'
}
implementation libs.dnsjava
spinnerImplementation project(":spinner")
spinnerImplementation libs.square.leakcanary
testImplementation testLibs.junit.junit
testImplementation testLibs.assertj.core
testImplementation testLibs.mockito.core
testImplementation testLibs.mockito.kotlin
testImplementation testLibs.androidx.test.core
testImplementation (testLibs.robolectric.robolectric) {
exclude group: 'com.google.protobuf', module: 'protobuf-java'
}
testImplementation testLibs.robolectric.shadows.multidex
testImplementation (testLibs.bouncycastle.bcprov.jdk15on) {
force = true
}
testImplementation testLibs.hamcrest.hamcrest
testImplementation testLibs.mockk
testImplementation(testFixtures(project(":libsignal-service")))
androidTestImplementation testLibs.androidx.test.ext.junit
androidTestImplementation testLibs.espresso.core
androidTestImplementation testLibs.androidx.test.core
androidTestImplementation testLibs.androidx.test.core.ktx
androidTestImplementation testLibs.androidx.test.ext.junit.ktx
androidTestImplementation testLibs.mockito.android
androidTestImplementation testLibs.mockito.kotlin
androidTestImplementation testLibs.square.okhttp.mockserver
instrumentationImplementation (libs.androidx.fragment.testing) {
exclude group: 'androidx.test', module: 'core'
}
testImplementation testLibs.espresso.core
implementation libs.kotlin.stdlib.jdk8
implementation libs.kotlin.reflect
implementation libs.jackson.module.kotlin
implementation libs.rxjava3.rxandroid
implementation libs.rxjava3.rxkotlin
implementation libs.rxdogtag
androidTestUtil testLibs.androidx.test.orchestrator
}
def getLastCommitTimestamp() {
if (!(new File('.git').exists())) {
return System.currentTimeMillis().toString()
}
new ByteArrayOutputStream().withStream { os ->
exec {
executable = 'git'
args = ['log', '-1', '--pretty=format:%ct']
standardOutput = os
}
return os.toString() + "000"
}
}
def getGitHash() {
if (!(new File('.git').exists())) {
throw new IllegalStateException("Must be a git repository to guarantee reproducible builds! (git hash is part of APK)")
}
def stdout = new ByteArrayOutputStream()
exec {
commandLine 'git', 'rev-parse', 'HEAD'
standardOutput = stdout
}
return stdout.toString().trim().substring(0, 12)
}
def getCurrentGitTag() {
if (!(new File('.git').exists())) {
throw new IllegalStateException("Must be a git repository to guarantee reproducible builds! (git hash is part of APK)")
}
def stdout = new ByteArrayOutputStream()
exec {
commandLine 'git', 'tag', '--points-at', 'HEAD'
standardOutput = stdout
}
def output = stdout.toString().trim()
if (output != null && output.size() > 0) {
def tags = output.split('\n').toList()
return tags.stream().filter(t -> t.contains('nightly')).findFirst().orElse(tags.get(0))
} else {
return null
}
}
tasks.withType(Test) {
testLogging {
events "failed"
exceptionFormat "full"
showCauses true
showExceptions true
showStackTraces true
}
}
def loadKeystoreProperties(filename) {
def keystorePropertiesFile = file("${project.rootDir}/${filename}")
if (keystorePropertiesFile.exists()) {
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
return keystoreProperties
} else {
return null
}
}
static def getDateSuffix() {
def date = new Date()
def formattedDate = date.format('yyyy-MM-dd-HH:mm')
return formattedDate
}
def getMapsKey() {
def mapKey = file("${project.rootDir}/maps.key")
if (mapKey.exists()) {
return mapKey.readLines()[0]
}
return "AIzaSyCSx9xea86GwDKGznCAULE9Y5a8b-TfN9U"
}

722
app/build.gradle.kts Normal file
View File

@@ -0,0 +1,722 @@
import com.android.build.api.dsl.ManagedVirtualDevice
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import java.io.ByteArrayOutputStream
import java.io.FileInputStream
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Properties
plugins {
id("com.android.application")
id("kotlin-android")
id("androidx.navigation.safeargs")
id("org.jlleitschuh.gradle.ktlint")
id("org.jetbrains.kotlin.android")
id("app.cash.exhaustive")
id("kotlin-parcelize")
id("com.squareup.wire")
id("translations")
id("licenses")
}
apply(from = "static-ips.gradle.kts")
val canonicalVersionCode = 1403
val canonicalVersionName = "7.2.2"
val postFixSize = 100
val abiPostFix: Map<String, Int> = mapOf(
"universal" to 0,
"armeabi-v7a" to 1,
"arm64-v8a" to 2,
"x86" to 3,
"x86_64" to 4
)
val keystores: Map<String, Properties?> = mapOf("debug" to loadKeystoreProperties("keystore.debug.properties"))
val selectableVariants = listOf(
"nightlyProdSpinner",
"nightlyProdPerf",
"nightlyProdRelease",
"nightlyStagingRelease",
"playProdDebug",
"playProdSpinner",
"playProdCanary",
"playProdPerf",
"playProdBenchmark",
"playProdInstrumentation",
"playProdRelease",
"playStagingDebug",
"playStagingCanary",
"playStagingSpinner",
"playStagingPerf",
"playStagingInstrumentation",
"playStagingRelease",
"websiteProdSpinner",
"websiteProdRelease"
)
val signalBuildToolsVersion: String by rootProject.extra
val signalCompileSdkVersion: String by rootProject.extra
val signalTargetSdkVersion: Int by rootProject.extra
val signalMinSdkVersion: Int by rootProject.extra
val signalJavaVersion: JavaVersion by rootProject.extra
val signalKotlinJvmTarget: String by rootProject.extra
wire {
kotlin {
javaInterop = true
}
sourcePath {
srcDir("src/main/protowire")
}
protoPath {
srcDir("${project.rootDir}/libsignal-service/src/main/protowire")
}
}
ktlint {
version.set("0.49.1")
}
android {
namespace = "org.thoughtcrime.securesms"
buildToolsVersion = signalBuildToolsVersion
compileSdkVersion = signalCompileSdkVersion
flavorDimensions += listOf("distribution", "environment")
useLibrary("org.apache.http.legacy")
testBuildType = "instrumentation"
kotlinOptions {
jvmTarget = signalKotlinJvmTarget
}
keystores["debug"]?.let { properties ->
signingConfigs.getByName("debug").apply {
storeFile = file("${project.rootDir}/${properties.getProperty("storeFile")}")
storePassword = properties.getProperty("storePassword")
keyAlias = properties.getProperty("keyAlias")
keyPassword = properties.getProperty("keyPassword")
}
}
testOptions {
execution = "ANDROIDX_TEST_ORCHESTRATOR"
unitTests {
isIncludeAndroidResources = true
}
managedDevices {
devices {
create<ManagedVirtualDevice>("pixel3api30") {
device = "Pixel 3"
apiLevel = 30
systemImageSource = "google-atd"
require64Bit = false
}
}
}
}
sourceSets {
getByName("test") {
java.srcDir("$projectDir/src/testShared")
}
getByName("androidTest") {
java.srcDir("$projectDir/src/testShared")
}
}
compileOptions {
isCoreLibraryDesugaringEnabled = true
sourceCompatibility = signalJavaVersion
targetCompatibility = signalJavaVersion
}
packagingOptions {
resources {
excludes += setOf("LICENSE.txt", "LICENSE", "NOTICE", "asm-license.txt", "META-INF/LICENSE", "META-INF/LICENSE.md", "META-INF/NOTICE", "META-INF/LICENSE-notice.md", "META-INF/proguard/androidx-annotations.pro", "libsignal_jni.dylib", "signal_jni.dll")
}
}
buildFeatures {
viewBinding = true
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.4.4"
}
defaultConfig {
versionCode = canonicalVersionCode * postFixSize
versionName = canonicalVersionName
minSdk = signalMinSdkVersion
targetSdk = signalTargetSdkVersion
multiDexEnabled = true
vectorDrawables.useSupportLibrary = true
project.ext.set("archivesBaseName", "Signal")
manifestPlaceholders["mapsKey"] = "AIzaSyCSx9xea86GwDKGznCAULE9Y5a8b-TfN9U"
buildConfigField("long", "BUILD_TIMESTAMP", getLastCommitTimestamp() + "L")
buildConfigField("String", "GIT_HASH", "\"${getGitHash()}\"")
buildConfigField("String", "SIGNAL_URL", "\"https://chat.signal.org\"")
buildConfigField("String", "STORAGE_URL", "\"https://storage.signal.org\"")
buildConfigField("String", "SIGNAL_CDN_URL", "\"https://cdn.signal.org\"")
buildConfigField("String", "SIGNAL_CDN2_URL", "\"https://cdn2.signal.org\"")
buildConfigField("String", "SIGNAL_CDN3_URL", "\"https://cdn3.signal.org\"")
buildConfigField("String", "SIGNAL_CDSI_URL", "\"https://cdsi.signal.org\"")
buildConfigField("String", "SIGNAL_SERVICE_STATUS_URL", "\"uptime.signal.org\"")
buildConfigField("String", "SIGNAL_KEY_BACKUP_URL", "\"https://api.backup.signal.org\"")
buildConfigField("String", "SIGNAL_SVR2_URL", "\"https://svr2.signal.org\"")
buildConfigField("String", "SIGNAL_SFU_URL", "\"https://sfu.voip.signal.org\"")
buildConfigField("String", "SIGNAL_STAGING_SFU_URL", "\"https://sfu.staging.voip.signal.org\"")
buildConfigField("String[]", "SIGNAL_SFU_INTERNAL_NAMES", "new String[]{\"Test\", \"Staging\", \"Development\"}")
buildConfigField("String[]", "SIGNAL_SFU_INTERNAL_URLS", "new String[]{\"https://sfu.test.voip.signal.org\", \"https://sfu.staging.voip.signal.org\", \"https://sfu.staging.test.voip.signal.org\"}")
buildConfigField("String", "CONTENT_PROXY_HOST", "\"contentproxy.signal.org\"")
buildConfigField("int", "CONTENT_PROXY_PORT", "443")
buildConfigField("String[]", "SIGNAL_SERVICE_IPS", rootProject.extra["service_ips"] as String)
buildConfigField("String[]", "SIGNAL_STORAGE_IPS", rootProject.extra["storage_ips"] as String)
buildConfigField("String[]", "SIGNAL_CDN_IPS", rootProject.extra["cdn_ips"] as String)
buildConfigField("String[]", "SIGNAL_CDN2_IPS", rootProject.extra["cdn2_ips"] as String)
buildConfigField("String[]", "SIGNAL_CDN3_IPS", rootProject.extra["cdn3_ips"] as String)
buildConfigField("String[]", "SIGNAL_SFU_IPS", rootProject.extra["sfu_ips"] as String)
buildConfigField("String[]", "SIGNAL_CONTENT_PROXY_IPS", rootProject.extra["content_proxy_ips"] as String)
buildConfigField("String[]", "SIGNAL_CDSI_IPS", rootProject.extra["cdsi_ips"] as String)
buildConfigField("String[]", "SIGNAL_SVR2_IPS", rootProject.extra["svr2_ips"] as String)
buildConfigField("String", "SIGNAL_AGENT", "\"OWA\"")
buildConfigField("String", "CDSI_MRENCLAVE", "\"0f6fd79cdfdaa5b2e6337f534d3baf999318b0c462a7ac1f41297a3e4b424a57\"")
buildConfigField("String", "SVR2_MRENCLAVE", "\"a6622ad4656e1abcd0bc0ff17c229477747d2ded0495c4ebee7ed35c1789fa97\"")
buildConfigField("String", "UNIDENTIFIED_SENDER_TRUST_ROOT", "\"BXu6QIKVz5MA8gstzfOgRQGqyLqOwNKHL6INkv3IHWMF\"")
buildConfigField("String", "ZKGROUP_SERVER_PUBLIC_PARAMS", "\"AMhf5ywVwITZMsff/eCyudZx9JDmkkkbV6PInzG4p8x3VqVJSFiMvnvlEKWuRob/1eaIetR31IYeAbm0NdOuHH8Qi+Rexi1wLlpzIo1gstHWBfZzy1+qHRV5A4TqPp15YzBPm0WSggW6PbSn+F4lf57VCnHF7p8SvzAA2ZZJPYJURt8X7bbg+H3i+PEjH9DXItNEqs2sNcug37xZQDLm7X36nOoGPs54XsEGzPdEV+itQNGUFEjY6X9Uv+Acuks7NpyGvCoKxGwgKgE5XyJ+nNKlyHHOLb6N1NuHyBrZrgtY/JYJHRooo5CEqYKBqdFnmbTVGEkCvJKxLnjwKWf+fEPoWeQFj5ObDjcKMZf2Jm2Ae69x+ikU5gBXsRmoF94GXTLfN0/vLt98KDPnxwAQL9j5V1jGOY8jQl6MLxEs56cwXN0dqCnImzVH3TZT1cJ8SW1BRX6qIVxEzjsSGx3yxF3suAilPMqGRp4ffyopjMD1JXiKR2RwLKzizUe5e8XyGOy9fplzhw3jVzTRyUZTRSZKkMLWcQ/gv0E4aONNqs4P+NameAZYOD12qRkxosQQP5uux6B2nRyZ7sAV54DgFyLiRcq1FvwKw2EPQdk4HDoePrO/RNUbyNddnM/mMgj4FW65xCoT1LmjrIjsv/Ggdlx46ueczhMgtBunx1/w8k8V+l8LVZ8gAT6wkU5J+DPQalQguMg12Jzug3q4TbdHiGCmD9EunCwOmsLuLJkz6EcSYXtrlDEnAM+hicw7iergYLLlMXpfTdGxJCWJmP4zqUFeTTmsmhsjGBt7NiEB/9pFFEB3pSbf4iiUukw63Eo8Aqnf4iwob6X1QviCWuc8t0LUlT9vALgh/f2DPVOOmR0RW6bgRvc7DSF20V/omg+YBw==\"")
buildConfigField("String", "GENERIC_SERVER_PUBLIC_PARAMS", "\"AByD873dTilmOSG0TjKrvpeaKEsUmIO8Vx9BeMmftwUs9v7ikPwM8P3OHyT0+X3EUMZrSe9VUp26Wai51Q9I8mdk0hX/yo7CeFGJyzoOqn8e/i4Ygbn5HoAyXJx5eXfIbqpc0bIxzju4H/HOQeOpt6h742qii5u/cbwOhFZCsMIbElZTaeU+BWMBQiZHIGHT5IE0qCordQKZ5iPZom0HeFa8Yq0ShuEyAl0WINBiY6xE3H/9WnvzXBbMuuk//eRxXgzO8ieCeK8FwQNxbfXqZm6Ro1cMhCOF3u7xoX83QhpN\"")
buildConfigField("String", "BACKUP_SERVER_PUBLIC_PARAMS", "\"AJwNSU55fsFCbgaxGRD11wO1juAs8Yr5GF8FPlGzzvdJJIKH5/4CC7ZJSOe3yL2vturVaRU2Cx0n751Vt8wkj1bozK3CBV1UokxV09GWf+hdVImLGjXGYLLhnI1J2TWEe7iWHyb553EEnRb5oxr9n3lUbNAJuRmFM7hrr0Al0F0wrDD4S8lo2mGaXe0MJCOM166F8oYRQqpFeEHfiLnxA1O8ZLh7vMdv4g9jI5phpRBTsJ5IjiJrWeP0zdIGHEssUeprDZ9OUJ14m0v61eYJMKsf59Bn+mAT2a7YfB+Don9O\"")
buildConfigField("String[]", "LANGUAGES", "new String[]{ ${languageList().map { "\"$it\"" }.joinToString(separator = ", ")} }")
buildConfigField("int", "CANONICAL_VERSION_CODE", "$canonicalVersionCode")
buildConfigField("String", "DEFAULT_CURRENCIES", "\"EUR,AUD,GBP,CAD,CNY\"")
buildConfigField("String", "GIPHY_API_KEY", "\"3o6ZsYH6U6Eri53TXy\"")
buildConfigField("String", "SIGNAL_CAPTCHA_URL", "\"https://signalcaptchas.org/registration/generate.html\"")
buildConfigField("String", "RECAPTCHA_PROOF_URL", "\"https://signalcaptchas.org/challenge/generate.html\"")
buildConfigField("org.signal.libsignal.net.Network.Environment", "LIBSIGNAL_NET_ENV", "org.signal.libsignal.net.Network.Environment.PRODUCTION")
buildConfigField("String", "BUILD_DISTRIBUTION_TYPE", "\"unset\"")
buildConfigField("String", "BUILD_ENVIRONMENT_TYPE", "\"unset\"")
buildConfigField("String", "BUILD_VARIANT_TYPE", "\"unset\"")
buildConfigField("String", "BADGE_STATIC_ROOT", "\"https://updates2.signal.org/static/badges/\"")
buildConfigField("String", "STRIPE_PUBLISHABLE_KEY", "\"pk_live_6cmGZopuTsV8novGgJJW9JpC00vLIgtQ1D\"")
buildConfigField("boolean", "TRACING_ENABLED", "false")
buildConfigField("boolean", "MESSAGE_BACKUP_RESTORE_ENABLED", "false")
ndk {
abiFilters += listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
}
resourceConfigurations += listOf()
splits {
abi {
isEnable = !project.hasProperty("generateBaselineProfile")
reset()
include("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
isUniversalApk = true
}
}
testInstrumentationRunner = "org.thoughtcrime.securesms.testing.SignalTestRunner"
testInstrumentationRunnerArguments["clearPackageData"] = "true"
}
buildTypes {
getByName("debug") {
if (keystores["debug"] != null) {
signingConfig = signingConfigs["debug"]
}
isDefault = true
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android.txt"),
"proguard/proguard-firebase-messaging.pro",
"proguard/proguard-google-play-services.pro",
"proguard/proguard-jackson.pro",
"proguard/proguard-sqlite.pro",
"proguard/proguard-appcompat-v7.pro",
"proguard/proguard-square-okhttp.pro",
"proguard/proguard-square-okio.pro",
"proguard/proguard-rounded-image-view.pro",
"proguard/proguard-glide.pro",
"proguard/proguard-shortcutbadger.pro",
"proguard/proguard-retrofit.pro",
"proguard/proguard-webrtc.pro",
"proguard/proguard-klinker.pro",
"proguard/proguard-mobilecoin.pro",
"proguard/proguard-retrolambda.pro",
"proguard/proguard-okhttp.pro",
"proguard/proguard-ez-vcard.pro",
"proguard/proguard.cfg"
)
testProguardFiles(
"proguard/proguard-automation.pro",
"proguard/proguard.cfg"
)
manifestPlaceholders["mapsKey"] = getMapsKey()
buildConfigField("String", "BUILD_VARIANT_TYPE", "\"Debug\"")
}
getByName("release") {
isMinifyEnabled = true
proguardFiles(*buildTypes["debug"].proguardFiles.toTypedArray())
buildConfigField("String", "BUILD_VARIANT_TYPE", "\"Release\"")
}
create("instrumentation") {
initWith(getByName("debug"))
isDefault = false
isMinifyEnabled = false
matchingFallbacks += "debug"
applicationIdSuffix = ".instrumentation"
buildConfigField("String", "BUILD_VARIANT_TYPE", "\"Instrumentation\"")
}
create("spinner") {
initWith(getByName("debug"))
isDefault = false
isMinifyEnabled = false
matchingFallbacks += "debug"
buildConfigField("String", "BUILD_VARIANT_TYPE", "\"Spinner\"")
}
create("perf") {
initWith(getByName("debug"))
isDefault = false
isDebuggable = false
isMinifyEnabled = true
matchingFallbacks += "debug"
buildConfigField("String", "BUILD_VARIANT_TYPE", "\"Perf\"")
buildConfigField("boolean", "TRACING_ENABLED", "true")
}
create("benchmark") {
initWith(getByName("debug"))
isDefault = false
isDebuggable = false
isMinifyEnabled = true
matchingFallbacks += "debug"
buildConfigField("String", "BUILD_VARIANT_TYPE", "\"Benchmark\"")
buildConfigField("boolean", "TRACING_ENABLED", "true")
}
create("canary") {
initWith(getByName("debug"))
isDefault = false
isMinifyEnabled = false
matchingFallbacks += "debug"
buildConfigField("String", "BUILD_VARIANT_TYPE", "\"Canary\"")
}
}
productFlavors {
create("play") {
dimension = "distribution"
isDefault = true
buildConfigField("boolean", "MANAGES_APP_UPDATES", "false")
buildConfigField("String", "APK_UPDATE_MANIFEST_URL", "null")
buildConfigField("String", "BUILD_DISTRIBUTION_TYPE", "\"play\"")
}
create("website") {
dimension = "distribution"
buildConfigField("boolean", "MANAGES_APP_UPDATES", "true")
buildConfigField("String", "APK_UPDATE_MANIFEST_URL", "\"https://updates.signal.org/android/latest.json\"")
buildConfigField("String", "BUILD_DISTRIBUTION_TYPE", "\"website\"")
}
create("nightly") {
val apkUpdateManifestUrl = if (file("${project.rootDir}/nightly-url.txt").exists()) {
file("${project.rootDir}/nightly-url.txt").readText().trim()
} else {
"<unset>"
}
dimension = "distribution"
versionNameSuffix = "-nightly-untagged-${getDateSuffix()}"
buildConfigField("boolean", "MANAGES_APP_UPDATES", "true")
buildConfigField("String", "APK_UPDATE_MANIFEST_URL", "\"${apkUpdateManifestUrl}\"")
buildConfigField("String", "BUILD_DISTRIBUTION_TYPE", "\"nightly\"")
}
create("prod") {
dimension = "environment"
isDefault = true
buildConfigField("String", "MOBILE_COIN_ENVIRONMENT", "\"mainnet\"")
buildConfigField("String", "BUILD_ENVIRONMENT_TYPE", "\"Prod\"")
}
create("staging") {
dimension = "environment"
applicationIdSuffix = ".staging"
buildConfigField("String", "SIGNAL_URL", "\"https://chat.staging.signal.org\"")
buildConfigField("String", "STORAGE_URL", "\"https://storage-staging.signal.org\"")
buildConfigField("String", "SIGNAL_CDN_URL", "\"https://cdn-staging.signal.org\"")
buildConfigField("String", "SIGNAL_CDN2_URL", "\"https://cdn2-staging.signal.org\"")
buildConfigField("String", "SIGNAL_CDN3_URL", "\"https://cdn3-staging.signal.org\"")
buildConfigField("String", "SIGNAL_CDSI_URL", "\"https://cdsi.staging.signal.org\"")
buildConfigField("String", "SIGNAL_KEY_BACKUP_URL", "\"https://api-staging.backup.signal.org\"")
buildConfigField("String", "SIGNAL_SVR2_URL", "\"https://svr2.staging.signal.org\"")
buildConfigField("String", "SVR2_MRENCLAVE", "\"acb1973aa0bbbd14b3b4e06f145497d948fd4a98efc500fcce363b3b743ec482\"")
buildConfigField("String", "UNIDENTIFIED_SENDER_TRUST_ROOT", "\"BbqY1DzohE4NUZoVF+L18oUPrK3kILllLEJh2UnPSsEx\"")
buildConfigField("String", "ZKGROUP_SERVER_PUBLIC_PARAMS", "\"ABSY21VckQcbSXVNCGRYJcfWHiAMZmpTtTELcDmxgdFbtp/bWsSxZdMKzfCp8rvIs8ocCU3B37fT3r4Mi5qAemeGeR2X+/YmOGR5ofui7tD5mDQfstAI9i+4WpMtIe8KC3wU5w3Inq3uNWVmoGtpKndsNfwJrCg0Hd9zmObhypUnSkfYn2ooMOOnBpfdanRtrvetZUayDMSC5iSRcXKpdlukrpzzsCIvEwjwQlJYVPOQPj4V0F4UXXBdHSLK05uoPBCQG8G9rYIGedYsClJXnbrgGYG3eMTG5hnx4X4ntARBgELuMWWUEEfSK0mjXg+/2lPmWcTZWR9nkqgQQP0tbzuiPm74H2wMO4u1Wafe+UwyIlIT9L7KLS19Aw8r4sPrXZSSsOZ6s7M1+rTJN0bI5CKY2PX29y5Ok3jSWufIKcgKOnWoP67d5b2du2ZVJjpjfibNIHbT/cegy/sBLoFwtHogVYUewANUAXIaMPyCLRArsKhfJ5wBtTminG/PAvuBdJ70Z/bXVPf8TVsR292zQ65xwvWTejROW6AZX6aqucUjlENAErBme1YHmOSpU6tr6doJ66dPzVAWIanmO/5mgjNEDeK7DDqQdB1xd03HT2Qs2TxY3kCK8aAb/0iM0HQiXjxZ9HIgYhbtvGEnDKW5ILSUydqH/KBhW4Pb0jZWnqN/YgbWDKeJxnDbYcUob5ZY5Lt5ZCMKuaGUvCJRrCtuugSMaqjowCGRempsDdJEt+cMaalhZ6gczklJB/IbdwENW9KeVFPoFNFzhxWUIS5ML9riVYhAtE6JE5jX0xiHNVIIPthb458cfA8daR0nYfYAUKogQArm0iBezOO+mPk5vCNWI+wwkyFCqNDXz/qxl1gAntuCJtSfq9OC3NkdhQlgYQ==\"")
buildConfigField("String", "GENERIC_SERVER_PUBLIC_PARAMS", "\"AHILOIrFPXX9laLbalbA9+L1CXpSbM/bTJXZGZiuyK1JaI6dK5FHHWL6tWxmHKYAZTSYmElmJ5z2A5YcirjO/yfoemE03FItyaf8W1fE4p14hzb5qnrmfXUSiAIVrhaXVwIwSzH6RL/+EO8jFIjJ/YfExfJ8aBl48CKHgu1+A6kWynhttonvWWx6h7924mIzW0Czj2ROuh4LwQyZypex4GuOPW8sgIT21KNZaafgg+KbV7XM1x1tF3XA17B4uGUaDbDw2O+nR1+U5p6qHPzmJ7ggFjSN6Utu+35dS1sS0P9N\"")
buildConfigField("String", "BACKUP_SERVER_PUBLIC_PARAMS", "\"AHYrGb9IfugAAJiPKp+mdXUx+OL9zBolPYHYQz6GI1gWjpEu5me3zVNSvmYY4zWboZHif+HG1sDHSuvwFd0QszSwuSF4X4kRP3fJREdTZ5MCR0n55zUppTwfHRW2S4sdQ0JGz7YDQIJCufYSKh0pGNEHL6hv79Agrdnr4momr3oXdnkpVBIp3HWAQ6IbXQVSG18X36GaicI1vdT0UFmTwU2KTneluC2eyL9c5ff8PcmiS+YcLzh0OKYQXB5ZfQ06d6DiINvDQLy75zcfUOniLAj0lGJiHxGczin/RXisKSR8\"")
buildConfigField("String", "MOBILE_COIN_ENVIRONMENT", "\"testnet\"")
buildConfigField("String", "SIGNAL_CAPTCHA_URL", "\"https://signalcaptchas.org/staging/registration/generate.html\"")
buildConfigField("String", "RECAPTCHA_PROOF_URL", "\"https://signalcaptchas.org/staging/challenge/generate.html\"")
buildConfigField("org.signal.libsignal.net.Network.Environment", "LIBSIGNAL_NET_ENV", "org.signal.libsignal.net.Network.Environment.STAGING")
buildConfigField("String", "BUILD_ENVIRONMENT_TYPE", "\"Staging\"")
buildConfigField("String", "STRIPE_PUBLISHABLE_KEY", "\"pk_test_sngOd8FnXNkpce9nPXawKrJD00kIDngZkD\"")
buildConfigField("boolean", "MESSAGE_BACKUP_RESTORE_ENABLED", "true")
}
}
lint {
abortOnError = true
baseline = file("lint-baseline.xml")
checkReleaseBuilds = false
disable += "LintError"
}
applicationVariants.all {
outputs
.map { it as com.android.build.gradle.internal.api.ApkVariantOutputImpl }
.forEach { output ->
if (output.baseName.contains("nightly")) {
output.versionCodeOverride = canonicalVersionCode * postFixSize + 5
var tag = getCurrentGitTag()
if (!tag.isNullOrEmpty()) {
if (tag.startsWith("v")) {
tag = tag.substring(1)
}
output.versionNameOverride = tag
output.outputFileName = output.outputFileName.replace(".apk", "-${output.versionNameOverride}.apk")
} else {
output.outputFileName = output.outputFileName.replace(".apk", "-$versionName.apk")
}
} else {
output.outputFileName = output.outputFileName.replace(".apk", "-$versionName.apk")
val abiName: String = output.getFilter("ABI") ?: "universal"
val postFix: Int = abiPostFix[abiName]!!
if (postFix >= postFixSize) {
throw AssertionError("postFix is too large")
}
output.versionCodeOverride = canonicalVersionCode * postFixSize + postFix
}
}
}
androidComponents {
beforeVariants { variant ->
variant.enable = variant.name in selectableVariants
}
}
val releaseDir = "$projectDir/src/release/java"
val debugDir = "$projectDir/src/debug/java"
android.buildTypes.configureEach {
val path = if (name == "release") releaseDir else debugDir
sourceSets.named(name) {
java.srcDir(path)
}
}
}
dependencies {
lintChecks(project(":lintchecks"))
ktlintRuleset(libs.ktlint.twitter.compose)
coreLibraryDesugaring(libs.android.tools.desugar)
implementation(project(":libsignal-service"))
implementation(project(":paging"))
implementation(project(":core-util"))
implementation(project(":glide-config"))
implementation(project(":video"))
implementation(project(":device-transfer"))
implementation(project(":image-editor"))
implementation(project(":donations"))
implementation(project(":contacts"))
implementation(project(":qr"))
implementation(project(":sticky-header-grid"))
implementation(project(":photoview"))
implementation(project(":core-ui"))
implementation(libs.androidx.fragment.ktx)
implementation(libs.androidx.appcompat) {
version {
strictly("1.6.1")
}
}
implementation(libs.androidx.window.window)
implementation(libs.androidx.window.java)
implementation(libs.androidx.recyclerview)
implementation(libs.material.material)
implementation(libs.androidx.legacy.support)
implementation(libs.androidx.preference)
implementation(libs.androidx.legacy.preference)
implementation(libs.androidx.gridlayout)
implementation(libs.androidx.exifinterface)
implementation(libs.androidx.compose.rxjava3)
implementation(libs.androidx.compose.runtime.livedata)
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.constraintlayout)
implementation(libs.androidx.multidex)
implementation(libs.androidx.navigation.fragment.ktx)
implementation(libs.androidx.navigation.ui.ktx)
implementation(libs.androidx.navigation.compose)
implementation(libs.androidx.lifecycle.viewmodel.ktx)
implementation(libs.androidx.lifecycle.livedata.ktx)
implementation(libs.androidx.lifecycle.process)
implementation(libs.androidx.lifecycle.viewmodel.savedstate)
implementation(libs.androidx.lifecycle.common.java8)
implementation(libs.androidx.lifecycle.reactivestreams.ktx)
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.camera.core)
implementation(libs.androidx.camera.camera2)
implementation(libs.androidx.camera.extensions)
implementation(libs.androidx.camera.lifecycle)
implementation(libs.androidx.camera.view)
implementation(libs.androidx.concurrent.futures)
implementation(libs.androidx.autofill)
implementation(libs.androidx.biometric)
implementation(libs.androidx.sharetarget)
implementation(libs.androidx.profileinstaller)
implementation(libs.androidx.asynclayoutinflater)
implementation(libs.androidx.asynclayoutinflater.appcompat)
implementation(libs.firebase.messaging) {
exclude(group = "com.google.firebase", module = "firebase-core")
exclude(group = "com.google.firebase", module = "firebase-analytics")
exclude(group = "com.google.firebase", module = "firebase-measurement-connector")
}
implementation(libs.google.play.services.maps)
implementation(libs.google.play.services.auth)
implementation(libs.bundles.media3)
implementation(libs.conscrypt.android)
implementation(libs.signal.aesgcmprovider)
implementation(libs.libsignal.android)
implementation(libs.mobilecoin)
implementation(libs.signal.ringrtc)
implementation(libs.leolin.shortcutbadger)
implementation(libs.emilsjolander.stickylistheaders)
implementation(libs.apache.httpclient.android)
implementation(libs.glide.glide)
implementation(libs.roundedimageview)
implementation(libs.materialish.progress)
implementation(libs.greenrobot.eventbus)
implementation(libs.google.zxing.android.integration)
implementation(libs.google.zxing.core)
implementation(libs.google.flexbox)
implementation(libs.subsampling.scale.image.view) {
exclude(group = "com.android.support", module = "support-annotations")
}
implementation(libs.android.tooltips) {
exclude(group = "com.android.support", module = "appcompat-v7")
}
implementation(libs.stream)
implementation(libs.lottie)
implementation(libs.signal.android.database.sqlcipher)
implementation(libs.androidx.sqlite)
implementation(libs.google.ez.vcard) {
exclude(group = "com.fasterxml.jackson.core")
exclude(group = "org.freemarker")
}
implementation(libs.dnsjava)
implementation(libs.kotlinx.collections.immutable)
implementation(libs.accompanist.permissions)
implementation(libs.kotlin.stdlib.jdk8)
implementation(libs.kotlin.reflect)
implementation(libs.jackson.module.kotlin)
implementation(libs.rxjava3.rxandroid)
implementation(libs.rxjava3.rxkotlin)
implementation(libs.rxdogtag)
"spinnerImplementation"(project(":spinner"))
"canaryImplementation"(libs.square.leakcanary)
"instrumentationImplementation"(libs.androidx.fragment.testing) {
exclude(group = "androidx.test", module = "core")
}
testImplementation(testLibs.junit.junit)
testImplementation(testLibs.assertj.core)
testImplementation(testLibs.mockito.core)
testImplementation(testLibs.mockito.kotlin)
testImplementation(testLibs.androidx.test.core)
testImplementation(testLibs.robolectric.robolectric) {
exclude(group = "com.google.protobuf", module = "protobuf-java")
}
testImplementation(testLibs.robolectric.shadows.multidex)
testImplementation(testLibs.bouncycastle.bcprov.jdk15on) {
version {
strictly("1.70")
}
}
testImplementation(testLibs.bouncycastle.bcpkix.jdk15on) {
version {
strictly("1.70")
}
}
testImplementation(testLibs.conscrypt.openjdk.uber)
testImplementation(testLibs.hamcrest.hamcrest)
testImplementation(testLibs.mockk)
testImplementation(testFixtures(project(":libsignal-service")))
testImplementation(testLibs.espresso.core)
androidTestImplementation(testLibs.androidx.test.ext.junit)
androidTestImplementation(testLibs.espresso.core)
androidTestImplementation(testLibs.androidx.test.core)
androidTestImplementation(testLibs.androidx.test.core.ktx)
androidTestImplementation(testLibs.androidx.test.ext.junit.ktx)
androidTestImplementation(testLibs.mockito.android)
androidTestImplementation(testLibs.mockito.kotlin)
androidTestImplementation(testLibs.mockk.android)
androidTestImplementation(testLibs.square.okhttp.mockserver)
androidTestUtil(testLibs.androidx.test.orchestrator)
}
fun assertIsGitRepo() {
if (!file("${project.rootDir}/.git").exists()) {
throw IllegalStateException("Must be a git repository to guarantee reproducible builds! (git hash is part of APK)")
}
}
fun getLastCommitTimestamp(): String {
assertIsGitRepo()
ByteArrayOutputStream().use { os ->
exec {
executable = "git"
args = listOf("log", "-1", "--pretty=format:%ct")
standardOutput = os
}
return os.toString() + "000"
}
}
fun getGitHash(): String {
assertIsGitRepo()
val stdout = ByteArrayOutputStream()
exec {
commandLine = listOf("git", "rev-parse", "HEAD")
standardOutput = stdout
}
return stdout.toString().trim().substring(0, 12)
}
fun getCurrentGitTag(): String? {
assertIsGitRepo()
val stdout = ByteArrayOutputStream()
exec {
commandLine = listOf("git", "tag", "--points-at", "HEAD")
standardOutput = stdout
}
val output: String = stdout.toString().trim()
return if (output.isNotEmpty()) {
val tags = output.split("\n").toList()
tags.firstOrNull { it.contains("nightly") } ?: tags[0]
} else {
null
}
}
tasks.withType<Test>().configureEach {
testLogging {
events("failed")
exceptionFormat = TestExceptionFormat.FULL
showCauses = true
showExceptions = true
showStackTraces = true
}
}
project.tasks.configureEach {
if (name.lowercase().contains("nightly") && name != "checkNightlyParams") {
dependsOn(tasks.getByName("checkNightlyParams"))
}
}
tasks.register("checkNightlyParams") {
doFirst {
if (project.gradle.startParameter.taskNames.any { it.lowercase().contains("nightly") }) {
if (!file("${project.rootDir}/nightly-url.txt").exists()) {
throw GradleException("Cannot find 'nightly-url.txt' for nightly build! It must exist in the root of this project and contain the location of the nightly manifest.")
}
}
}
}
fun loadKeystoreProperties(filename: String): Properties? {
val keystorePropertiesFile = file("${project.rootDir}/$filename")
return if (keystorePropertiesFile.exists()) {
val keystoreProperties = Properties()
keystoreProperties.load(FileInputStream(keystorePropertiesFile))
keystoreProperties
} else {
null
}
}
fun getDateSuffix(): String {
return SimpleDateFormat("yyyy-MM-dd-HH:mm").format(Date())
}
fun getMapsKey(): String {
val mapKey = file("${project.rootDir}/maps.key")
return if (mapKey.exists()) {
mapKey.readLines()[0]
} else {
"AIzaSyCSx9xea86GwDKGznCAULE9Y5a8b-TfN9U"
}
}
fun Project.languageList(): List<String> {
return fileTree("src/main/res") { include("**/strings.xml") }
.map { stringFile -> stringFile.parentFile.name }
.map { valuesFolderName -> valuesFolderName.replace("values-", "") }
.filter { valuesFolderName -> valuesFolderName != "values" }
.map { languageCode -> languageCode.replace("-r", "_") }
.distinct() + "en"
}
fun String.capitalize(): String {
return this.replaceFirstChar { it.uppercase() }
}

View File

@@ -0,0 +1,2 @@
# MobileCoin
-keep class com.mobilecoin.** { *; }

View File

@@ -2,8 +2,11 @@
-dontobfuscate
-keepattributes SourceFile,LineNumberTable
-keep class org.whispersystems.** { *; }
-keep class org.signal.libsignal.net.** { *; }
-keep class org.signal.libsignal.protocol.** { *; }
-keep class org.signal.libsignal.usernames.** { *; }
-keep class org.thoughtcrime.securesms.** { *; }
-keep class org.signal.donations.json.** { *; }
-keepclassmembers class ** {
public void onEvent*(**);
}
@@ -11,4 +14,11 @@
# Protobuf lite
-keep class * extends com.google.protobuf.GeneratedMessageLite { *; }
-keep class androidx.window.** { *; }
-keep class androidx.window.** { *; }
# AGP generated dont warns
-dontwarn com.android.org.conscrypt.SSLParametersImpl
-dontwarn org.apache.harmony.xnet.provider.jsse.SSLParametersImpl
-dontwarn org.slf4j.impl.StaticLoggerBinder
-dontwarn sun.net.spi.nameservice.NameService
-dontwarn sun.net.spi.nameservice.NameServiceDescriptor

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

View File

@@ -1,15 +1,52 @@
package org.thoughtcrime.securesms
import org.signal.core.util.concurrent.SignalExecutors
import org.signal.core.util.logging.AndroidLogger
import org.signal.core.util.logging.Log
import org.signal.libsignal.protocol.logging.SignalProtocolLoggerProvider
import org.thoughtcrime.securesms.database.LogDatabase
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.dependencies.ApplicationDependencyProvider
import org.thoughtcrime.securesms.dependencies.InstrumentationApplicationDependencyProvider
import org.thoughtcrime.securesms.logging.CustomSignalProtocolLogger
import org.thoughtcrime.securesms.logging.PersistentLogger
import org.thoughtcrime.securesms.testing.InMemoryLogger
/**
* Application context for running instrumentation tests (aka androidTests).
*/
class SignalInstrumentationApplicationContext : ApplicationContext() {
val inMemoryLogger: InMemoryLogger = InMemoryLogger()
override fun initializeAppDependencies() {
val default = ApplicationDependencyProvider(this)
ApplicationDependencies.init(this, InstrumentationApplicationDependencyProvider(this, default))
ApplicationDependencies.getDeadlockDetector().start()
}
override fun initializeLogging() {
Log.initialize({ true }, AndroidLogger(), PersistentLogger(this), inMemoryLogger)
SignalProtocolLoggerProvider.setProvider(CustomSignalProtocolLogger())
SignalExecutors.UNBOUNDED.execute {
Log.blockUntilAllWritesFinished()
LogDatabase.getInstance(this).logs.trimToSize()
}
}
override fun beginJobLoop() = Unit
/**
* Some of the jobs can interfere with some of the instrumentation tests.
*
* For example, we may try to create a release channel recipient while doing
* an import/backup test.
*
* This can be used to start the job loop if needed for tests that rely on it.
*/
fun beginJobLoopForTests() {
super.beginJobLoop()
}
}

View File

@@ -0,0 +1,675 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.backup.v2
import android.content.ContentValues
import android.database.Cursor
import androidx.core.content.contentValuesOf
import net.zetetic.database.sqlcipher.SQLiteDatabase
import org.junit.Before
import org.junit.Test
import org.signal.core.util.Hex
import org.signal.core.util.SqlUtil
import org.signal.core.util.insertInto
import org.signal.core.util.readToList
import org.signal.core.util.readToSingleObject
import org.signal.core.util.requireBlob
import org.signal.core.util.requireLong
import org.signal.core.util.requireString
import org.signal.core.util.select
import org.signal.core.util.toInt
import org.signal.core.util.withinTransaction
import org.signal.libsignal.zkgroup.profiles.ProfileKey
import org.thoughtcrime.securesms.backup.v2.database.clearAllDataForBackupRestore
import org.thoughtcrime.securesms.database.CallTable
import org.thoughtcrime.securesms.database.EmojiSearchTable
import org.thoughtcrime.securesms.database.MessageTable
import org.thoughtcrime.securesms.database.MessageTypes
import org.thoughtcrime.securesms.database.RecipientTable
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.keyvalue.PhoneNumberPrivacyValues
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.mms.QuoteModel
import org.thoughtcrime.securesms.profiles.ProfileName
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.subscription.Subscriber
import org.thoughtcrime.securesms.testing.assertIs
import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.whispersystems.signalservice.api.push.ServiceId.ACI
import org.whispersystems.signalservice.api.push.ServiceId.PNI
import org.whispersystems.signalservice.api.subscriptions.SubscriberId
import java.io.ByteArrayInputStream
import java.util.UUID
import kotlin.random.Random
typealias DatabaseData = Map<String, List<Map<String, Any?>>>
class BackupTest {
companion object {
val SELF_ACI = ACI.from(UUID.fromString("77770000-b477-4f35-a824-d92987a63641"))
val SELF_PNI = PNI.from(UUID.fromString("77771111-b014-41fb-bf73-05cb2ec52910"))
const val SELF_E164 = "+10000000000"
val SELF_PROFILE_KEY = ProfileKey(Random.nextBytes(32))
val ALICE_ACI = ACI.from(UUID.fromString("aaaa0000-5a76-47fa-a98a-7e72c948a82e"))
val ALICE_PNI = PNI.from(UUID.fromString("aaaa1111-c960-4f6c-8385-671ad2ffb999"))
val ALICE_E164 = "+12222222222"
/** Columns that we don't need to check equality of */
private val IGNORED_COLUMNS: Map<String, Set<String>> = mapOf(
RecipientTable.TABLE_NAME to setOf(RecipientTable.STORAGE_SERVICE_ID),
MessageTable.TABLE_NAME to setOf(MessageTable.FROM_DEVICE_ID)
)
/** Tables we don't need to check equality of */
private val IGNORED_TABLES: Set<String> = setOf(
EmojiSearchTable.TABLE_NAME,
"sqlite_sequence",
"message_fts_data",
"message_fts_idx",
"message_fts_docsize"
)
}
@Before
fun setup() {
SignalStore.account().setE164(SELF_E164)
SignalStore.account().setAci(SELF_ACI)
SignalStore.account().setPni(SELF_PNI)
SignalStore.account().generateAciIdentityKeyIfNecessary()
SignalStore.account().generatePniIdentityKeyIfNecessary()
}
@Test
fun emptyDatabase() {
backupTest { }
}
@Test
fun noteToSelf() {
backupTest {
individualChat(aci = SELF_ACI, givenName = "Note to Self") {
standardMessage(outgoing = true, body = "A")
standardMessage(outgoing = true, body = "B")
standardMessage(outgoing = true, body = "C")
}
}
}
@Test
fun individualChat() {
backupTest {
individualChat(aci = ALICE_ACI, givenName = "Alice") {
val m1 = standardMessage(outgoing = true, body = "Outgoing 1")
val m2 = standardMessage(outgoing = false, body = "Incoming 1", read = true)
standardMessage(outgoing = true, body = "Outgoing 2", quotes = m2)
standardMessage(outgoing = false, body = "Incoming 2", quotes = m1, quoteTargetMissing = true, read = false)
standardMessage(outgoing = true, body = "Outgoing 3, with mention", randomMention = true)
standardMessage(outgoing = false, body = "Incoming 3, with style", read = false, randomStyling = true)
remoteDeletedMessage(outgoing = true)
remoteDeletedMessage(outgoing = false)
}
}
}
@Test
fun individualRecipients() {
backupTest {
// Comprehensive example
individualRecipient(
aci = ALICE_ACI,
pni = ALICE_PNI,
e164 = ALICE_E164,
givenName = "Alice",
familyName = "Smith",
username = "alice.99",
hidden = false,
registeredState = RecipientTable.RegisteredState.REGISTERED,
profileKey = ProfileKey(Random.nextBytes(32)),
profileSharing = true,
hideStory = false
)
// Trying to get coverage of all the various values
individualRecipient(aci = ACI.from(UUID.randomUUID()), registeredState = RecipientTable.RegisteredState.NOT_REGISTERED)
individualRecipient(aci = ACI.from(UUID.randomUUID()), registeredState = RecipientTable.RegisteredState.UNKNOWN)
individualRecipient(pni = PNI.from(UUID.randomUUID()))
individualRecipient(e164 = "+15551234567")
individualRecipient(aci = ACI.from(UUID.randomUUID()), givenName = "Bob")
individualRecipient(aci = ACI.from(UUID.randomUUID()), familyName = "Smith")
individualRecipient(aci = ACI.from(UUID.randomUUID()), profileSharing = false)
individualRecipient(aci = ACI.from(UUID.randomUUID()), hideStory = true)
individualRecipient(aci = ACI.from(UUID.randomUUID()), hidden = true)
}
}
@Test
fun individualCallLogs() {
backupTest {
val aliceId = individualRecipient(
aci = ALICE_ACI,
pni = ALICE_PNI,
e164 = ALICE_E164,
givenName = "Alice",
familyName = "Smith",
username = "alice.99",
hidden = false,
registeredState = RecipientTable.RegisteredState.REGISTERED,
profileKey = ProfileKey(Random.nextBytes(32)),
profileSharing = true,
hideStory = false
)
insertOneToOneCallVariations(1, 1, aliceId)
}
}
private fun insertOneToOneCallVariations(callId: Long, timestamp: Long, id: RecipientId): Long {
val directions = arrayOf(CallTable.Direction.INCOMING, CallTable.Direction.OUTGOING)
val callTypes = arrayOf(CallTable.Type.AUDIO_CALL, CallTable.Type.VIDEO_CALL)
val events = arrayOf(
CallTable.Event.MISSED,
CallTable.Event.OUTGOING_RING,
CallTable.Event.ONGOING,
CallTable.Event.ACCEPTED,
CallTable.Event.NOT_ACCEPTED
)
var callTimestamp: Long = timestamp
var currentCallId = callId
for (direction in directions) {
for (event in events) {
for (type in callTypes) {
insertOneToOneCall(callId = currentCallId, callTimestamp, id, type, direction, event)
callTimestamp++
currentCallId++
}
}
}
return currentCallId
}
private fun insertOneToOneCall(callId: Long, timestamp: Long, peer: RecipientId, type: CallTable.Type, direction: CallTable.Direction, event: CallTable.Event) {
val messageType: Long = CallTable.Call.getMessageType(type, direction, event)
SignalDatabase.rawDatabase.withinTransaction {
val recipient = Recipient.resolved(peer)
val threadId = SignalDatabase.threads.getOrCreateThreadIdFor(recipient)
val outgoing = direction == CallTable.Direction.OUTGOING
val messageValues = contentValuesOf(
MessageTable.FROM_RECIPIENT_ID to if (outgoing) Recipient.self().id.serialize() else peer.serialize(),
MessageTable.FROM_DEVICE_ID to 1,
MessageTable.TO_RECIPIENT_ID to if (outgoing) peer.serialize() else Recipient.self().id.serialize(),
MessageTable.DATE_RECEIVED to timestamp,
MessageTable.DATE_SENT to timestamp,
MessageTable.READ to 1,
MessageTable.TYPE to messageType,
MessageTable.THREAD_ID to threadId
)
val messageId = SignalDatabase.rawDatabase.insert(MessageTable.TABLE_NAME, null, messageValues)
val values = contentValuesOf(
CallTable.CALL_ID to callId,
CallTable.MESSAGE_ID to messageId,
CallTable.PEER to peer.serialize(),
CallTable.TYPE to CallTable.Type.serialize(type),
CallTable.DIRECTION to CallTable.Direction.serialize(direction),
CallTable.EVENT to CallTable.Event.serialize(event),
CallTable.TIMESTAMP to timestamp
)
SignalDatabase.rawDatabase.insert(CallTable.TABLE_NAME, null, values)
SignalDatabase.threads.update(threadId, true)
}
}
@Test
fun accountData() {
val context = ApplicationDependencies.getApplication()
backupTest(validateKeyValue = true) {
val self = Recipient.self()
// TODO note-to-self archived
// TODO note-to-self unread
SignalStore.account().setAci(SELF_ACI)
SignalStore.account().setPni(SELF_PNI)
SignalStore.account().setE164(SELF_E164)
SignalStore.account().generateAciIdentityKeyIfNecessary()
SignalStore.account().generatePniIdentityKeyIfNecessary()
SignalDatabase.recipients.setProfileKey(self.id, ProfileKey(Random.nextBytes(32)))
SignalDatabase.recipients.setProfileName(self.id, ProfileName.fromParts("Peter", "Parker"))
SignalDatabase.recipients.setProfileAvatar(self.id, "https://example.com/")
SignalStore.donationsValues().markUserManuallyCancelled()
SignalStore.donationsValues().setSubscriber(Subscriber(SubscriberId.generate(), "USD"))
SignalStore.donationsValues().setDisplayBadgesOnProfile(false)
SignalStore.phoneNumberPrivacy().phoneNumberDiscoverabilityMode = PhoneNumberPrivacyValues.PhoneNumberDiscoverabilityMode.NOT_DISCOVERABLE
SignalStore.phoneNumberPrivacy().phoneNumberSharingMode = PhoneNumberPrivacyValues.PhoneNumberSharingMode.NOBODY
SignalStore.settings().isLinkPreviewsEnabled = false
SignalStore.settings().isPreferSystemContactPhotos = true
SignalStore.settings().universalExpireTimer = 42
SignalStore.settings().setKeepMutedChatsArchived(true)
SignalStore.storyValues().viewedReceiptsEnabled = false
SignalStore.storyValues().userHasViewedOnboardingStory = true
SignalStore.storyValues().isFeatureDisabled = false
SignalStore.storyValues().userHasBeenNotifiedAboutStories = true
SignalStore.storyValues().userHasSeenGroupStoryEducationSheet = true
SignalStore.emojiValues().reactions = listOf("a", "b", "c")
TextSecurePreferences.setTypingIndicatorsEnabled(context, false)
TextSecurePreferences.setReadReceiptsEnabled(context, false)
TextSecurePreferences.setShowUnidentifiedDeliveryIndicatorsEnabled(context, true)
}
// Have to check TextSecurePreferences ourselves, since they're not in a database
TextSecurePreferences.isTypingIndicatorsEnabled(context) assertIs false
TextSecurePreferences.isReadReceiptsEnabled(context) assertIs false
TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(context) assertIs true
}
/**
* Sets up the database, then executes your setup code, then compares snapshots of the database
* before an after an import to ensure that no data was lost/changed.
*
* @param validateKeyValue If true, this will also validate the KeyValueDatabase. You only want to do this if you
* intend on setting most of the values. Otherwise stuff tends to not match since values are lazily written.
*/
private fun backupTest(validateKeyValue: Boolean = false, content: () -> Unit) {
// Under normal circumstances, My Story ends up being the first recipient in the table, and is added automatically.
// This screws with the tests by offsetting all the recipientIds in the initial state.
// Easiest way to get around this is to make the DB a true clean slate by clearing everything.
// (We only really need to clear Recipient/dlists, but doing everything to be consistent.)
SignalDatabase.distributionLists.clearAllDataForBackupRestore()
SignalDatabase.recipients.clearAllDataForBackupRestore()
SignalDatabase.messages.clearAllDataForBackupRestore()
SignalDatabase.threads.clearAllDataForBackupRestore()
// Again, for comparison purposes, because we always import self first, we want to ensure it's the first item
// in the table when we export.
individualRecipient(
aci = SELF_ACI,
pni = SELF_PNI,
e164 = SELF_E164,
profileKey = SELF_PROFILE_KEY,
profileSharing = true
)
content()
val startingMainData: DatabaseData = SignalDatabase.rawDatabase.readAllContents()
val startingKeyValueData: DatabaseData = if (validateKeyValue) SignalDatabase.rawDatabase.readAllContents() else emptyMap()
val exported: ByteArray = BackupRepository.export()
BackupRepository.import(length = exported.size.toLong(), inputStreamFactory = { ByteArrayInputStream(exported) }, selfData = BackupRepository.SelfData(SELF_ACI, SELF_PNI, SELF_E164, SELF_PROFILE_KEY))
val endingData: DatabaseData = SignalDatabase.rawDatabase.readAllContents()
val endingKeyValueData: DatabaseData = if (validateKeyValue) SignalDatabase.rawDatabase.readAllContents() else emptyMap()
assertDatabaseMatches(startingMainData, endingData)
assertDatabaseMatches(startingKeyValueData, endingKeyValueData)
}
private fun individualChat(aci: ACI, givenName: String, familyName: String? = null, init: IndividualChatCreator.() -> Unit) {
val recipientId = individualRecipient(aci = aci, givenName = givenName, familyName = familyName, profileSharing = true)
val threadId: Long = SignalDatabase.threads.getOrCreateThreadIdFor(recipientId, false)
IndividualChatCreator(SignalDatabase.rawDatabase, recipientId, threadId).init()
SignalDatabase.threads.update(threadId, false)
}
private fun individualRecipient(
aci: ACI? = null,
pni: PNI? = null,
e164: String? = null,
givenName: String? = null,
familyName: String? = null,
username: String? = null,
hidden: Boolean = false,
registeredState: RecipientTable.RegisteredState = RecipientTable.RegisteredState.UNKNOWN,
profileKey: ProfileKey? = null,
profileSharing: Boolean = false,
hideStory: Boolean = false
): RecipientId {
check(aci != null || pni != null || e164 != null)
val recipientId = SignalDatabase.recipients.getAndPossiblyMerge(aci, pni, e164, pniVerified = true, changeSelf = true)
if (givenName != null || familyName != null) {
SignalDatabase.recipients.setProfileName(recipientId, ProfileName.fromParts(givenName, familyName))
}
if (username != null) {
SignalDatabase.recipients.setUsername(recipientId, username)
}
if (registeredState == RecipientTable.RegisteredState.REGISTERED) {
SignalDatabase.recipients.markRegistered(recipientId, aci ?: pni!!)
} else if (registeredState == RecipientTable.RegisteredState.NOT_REGISTERED) {
SignalDatabase.recipients.markUnregistered(recipientId)
}
if (profileKey != null) {
SignalDatabase.recipients.setProfileKey(recipientId, profileKey)
}
SignalDatabase.recipients.setProfileSharing(recipientId, profileSharing)
SignalDatabase.recipients.setHideStory(recipientId, hideStory)
if (hidden) {
SignalDatabase.recipients.markHidden(recipientId)
}
return recipientId
}
private inner class IndividualChatCreator(
private val db: SQLiteDatabase,
private val recipientId: RecipientId,
private val threadId: Long
) {
fun standardMessage(
outgoing: Boolean,
sentTimestamp: Long = System.currentTimeMillis(),
receivedTimestamp: Long = if (outgoing) sentTimestamp else sentTimestamp + 1,
serverTimestamp: Long = sentTimestamp,
body: String? = null,
read: Boolean = true,
quotes: Long? = null,
quoteTargetMissing: Boolean = false,
randomMention: Boolean = false,
randomStyling: Boolean = false
): Long {
return db.insertMessage(
from = if (outgoing) Recipient.self().id else recipientId,
to = if (outgoing) recipientId else Recipient.self().id,
outgoing = outgoing,
threadId = threadId,
sentTimestamp = sentTimestamp,
receivedTimestamp = receivedTimestamp,
serverTimestamp = serverTimestamp,
body = body,
read = read,
quotes = quotes,
quoteTargetMissing = quoteTargetMissing,
randomMention = randomMention,
randomStyling = randomStyling
)
}
fun remoteDeletedMessage(
outgoing: Boolean,
sentTimestamp: Long = System.currentTimeMillis(),
receivedTimestamp: Long = if (outgoing) sentTimestamp else sentTimestamp + 1,
serverTimestamp: Long = sentTimestamp
): Long {
return db.insertMessage(
from = if (outgoing) Recipient.self().id else recipientId,
to = if (outgoing) recipientId else Recipient.self().id,
outgoing = outgoing,
threadId = threadId,
sentTimestamp = sentTimestamp,
receivedTimestamp = receivedTimestamp,
serverTimestamp = serverTimestamp,
remoteDeleted = true
)
}
}
private fun SQLiteDatabase.insertMessage(
from: RecipientId,
to: RecipientId,
outgoing: Boolean,
threadId: Long,
sentTimestamp: Long = System.currentTimeMillis(),
receivedTimestamp: Long = if (outgoing) sentTimestamp else sentTimestamp + 1,
serverTimestamp: Long = sentTimestamp,
body: String? = null,
read: Boolean = true,
quotes: Long? = null,
quoteTargetMissing: Boolean = false,
randomMention: Boolean = false,
randomStyling: Boolean = false,
remoteDeleted: Boolean = false
): Long {
val type = if (outgoing) {
MessageTypes.BASE_SENT_TYPE
} else {
MessageTypes.BASE_INBOX_TYPE
} or MessageTypes.SECURE_MESSAGE_BIT or MessageTypes.PUSH_MESSAGE_BIT
val contentValues = ContentValues()
contentValues.put(MessageTable.DATE_SENT, sentTimestamp)
contentValues.put(MessageTable.DATE_RECEIVED, receivedTimestamp)
contentValues.put(MessageTable.FROM_RECIPIENT_ID, from.serialize())
contentValues.put(MessageTable.TO_RECIPIENT_ID, to.serialize())
contentValues.put(MessageTable.THREAD_ID, threadId)
contentValues.put(MessageTable.BODY, body)
contentValues.put(MessageTable.TYPE, type)
contentValues.put(MessageTable.READ, if (read) 1 else 0)
if (!outgoing) {
contentValues.put(MessageTable.DATE_SERVER, serverTimestamp)
}
if (remoteDeleted) {
contentValues.put(MessageTable.REMOTE_DELETED, 1)
return this
.insertInto(MessageTable.TABLE_NAME)
.values(contentValues)
.run()
}
if (quotes != null) {
val quoteDetails = this.getQuoteDetailsFor(quotes)
contentValues.put(MessageTable.QUOTE_ID, if (quoteTargetMissing) MessageTable.QUOTE_TARGET_MISSING_ID else quoteDetails.quotedSentTimestamp)
contentValues.put(MessageTable.QUOTE_AUTHOR, quoteDetails.authorId.serialize())
contentValues.put(MessageTable.QUOTE_BODY, quoteDetails.body)
contentValues.put(MessageTable.QUOTE_BODY_RANGES, quoteDetails.bodyRanges)
contentValues.put(MessageTable.QUOTE_TYPE, quoteDetails.type)
contentValues.put(MessageTable.QUOTE_MISSING, quoteTargetMissing.toInt())
}
if (body != null && (randomMention || randomStyling)) {
val ranges: MutableList<BodyRangeList.BodyRange> = mutableListOf()
if (randomMention) {
ranges += BodyRangeList.BodyRange(
start = 0,
length = Random.nextInt(body.length),
mentionUuid = if (outgoing) Recipient.resolved(to).requireAci().toString() else Recipient.resolved(from).requireAci().toString()
)
}
if (randomStyling) {
ranges += BodyRangeList.BodyRange(
start = 0,
length = Random.nextInt(body.length),
style = BodyRangeList.BodyRange.Style.fromValue(Random.nextInt(BodyRangeList.BodyRange.Style.values().size))
)
}
contentValues.put(MessageTable.MESSAGE_RANGES, BodyRangeList(ranges = ranges).encode())
}
return this
.insertInto(MessageTable.TABLE_NAME)
.values(contentValues)
.run()
}
private fun assertDatabaseMatches(expected: DatabaseData, actual: DatabaseData) {
assert(expected.keys.size == actual.keys.size) { "Mismatched table count! Expected: ${expected.keys} || Actual: ${actual.keys}" }
assert(expected.keys.containsAll(actual.keys)) { "Table names differ! Expected: ${expected.keys} || Actual: ${actual.keys}" }
val tablesToCheck = expected.keys.filter { !IGNORED_TABLES.contains(it) }
for (table in tablesToCheck) {
val expectedTable: List<Map<String, Any?>> = expected[table]!!
val actualTable: List<Map<String, Any?>> = actual[table]!!
assert(expectedTable.size == actualTable.size) { "Mismatched number of rows for table '$table'! Expected: ${expectedTable.size} || Actual: ${actualTable.size}\n $actualTable" }
val expectedFiltered: List<Map<String, Any?>> = expectedTable.withoutExcludedColumns(IGNORED_COLUMNS[table])
val actualFiltered: List<Map<String, Any?>> = actualTable.withoutExcludedColumns(IGNORED_COLUMNS[table])
assert(contentEquals(expectedFiltered, actualFiltered)) { "Data did not match for table '$table'!\n${prettyDiff(expectedFiltered, actualFiltered)}" }
}
}
private fun contentEquals(expectedRows: List<Map<String, Any?>>, actualRows: List<Map<String, Any?>>): Boolean {
if (expectedRows == actualRows) {
return true
}
assert(expectedRows.size == actualRows.size)
for (i in expectedRows.indices) {
val expectedRow = expectedRows[i]
val actualRow = actualRows[i]
for (key in expectedRow.keys) {
val expectedValue = expectedRow[key]
val actualValue = actualRow[key]
if (!contentEquals(expectedValue, actualValue)) {
return false
}
}
}
return true
}
private fun contentEquals(lhs: Any?, rhs: Any?): Boolean {
return if (lhs is ByteArray && rhs is ByteArray) {
lhs.contentEquals(rhs)
} else {
lhs == rhs
}
}
private fun prettyDiff(expectedRows: List<Map<String, Any?>>, actualRows: List<Map<String, Any?>>): String {
val builder = StringBuilder()
assert(expectedRows.size == actualRows.size)
for (i in expectedRows.indices) {
val expectedRow = expectedRows[i]
val actualRow = actualRows[i]
var describedRow = false
for (key in expectedRow.keys) {
val expectedValue = expectedRow[key]
val actualValue = actualRow[key]
if (!contentEquals(expectedValue, actualValue)) {
if (!describedRow) {
builder.append("-- ROW ${i + 1}\n")
describedRow = true
}
builder.append("  [$key] Expected: ${expectedValue.prettyPrint()} || Actual: ${actualValue.prettyPrint()} \n")
}
}
if (describedRow) {
builder.append("\n")
builder.append("Expected: $expectedRow\n")
builder.append("Actual: $actualRow\n")
}
}
return builder.toString()
}
private fun Any?.prettyPrint(): String {
return when (this) {
is ByteArray -> "Bytes(${Hex.toString(this)})"
else -> this.toString()
}
}
private fun List<Map<String, Any?>>.withoutExcludedColumns(ignored: Set<String>?): List<Map<String, Any?>> {
return if (ignored != null) {
this.map { row ->
row.filterKeys { !ignored.contains(it) }
}
} else {
this
}
}
private fun SQLiteDatabase.getQuoteDetailsFor(messageId: Long): QuoteDetails {
return this
.select(
MessageTable.DATE_SENT,
MessageTable.FROM_RECIPIENT_ID,
MessageTable.BODY,
MessageTable.MESSAGE_RANGES
)
.from(MessageTable.TABLE_NAME)
.where("${MessageTable.ID} = ?", messageId)
.run()
.readToSingleObject { cursor ->
QuoteDetails(
quotedSentTimestamp = cursor.requireLong(MessageTable.DATE_SENT),
authorId = RecipientId.from(cursor.requireLong(MessageTable.FROM_RECIPIENT_ID)),
body = cursor.requireString(MessageTable.BODY),
bodyRanges = cursor.requireBlob(MessageTable.MESSAGE_RANGES),
type = QuoteModel.Type.NORMAL.code
)
}!!
}
private fun SQLiteDatabase.readAllContents(): DatabaseData {
return SqlUtil.getAllTables(this).associateWith { table -> this.getAllTableData(table) }
}
private fun SQLiteDatabase.getAllTableData(table: String): List<Map<String, Any?>> {
return this
.select()
.from(table)
.run()
.readToList { cursor ->
val map: MutableMap<String, Any?> = mutableMapOf()
for (i in 0 until cursor.columnCount) {
val column = cursor.getColumnName(i)
when (cursor.getType(i)) {
Cursor.FIELD_TYPE_INTEGER -> map[column] = cursor.getInt(i)
Cursor.FIELD_TYPE_FLOAT -> map[column] = cursor.getFloat(i)
Cursor.FIELD_TYPE_STRING -> map[column] = cursor.getString(i)
Cursor.FIELD_TYPE_BLOB -> map[column] = cursor.getBlob(i)
Cursor.FIELD_TYPE_NULL -> map[column] = null
}
}
map
}
}
private data class QuoteDetails(
val quotedSentTimestamp: Long,
val authorId: RecipientId,
val body: String?,
val bodyRanges: ByteArray?,
val type: Int
)
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.backup.v2
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil
import org.whispersystems.signalservice.api.util.toByteArray
import java.util.UUID
import kotlin.random.Random
object TestRecipientUtils {
private var upperGenAci = 13131313L
private var lowerGenAci = 0L
private var upperGenPni = 12121212L
private var lowerGenPni = 0L
private var groupMasterKeyRandom = Random(12345)
fun generateProfileKey(): ByteArray {
return ProfileKeyUtil.createNew().serialize()
}
fun nextPni(): ByteArray {
synchronized(this) {
lowerGenPni++
var uuid = UUID(upperGenPni, lowerGenPni)
return uuid.toByteArray()
}
}
fun nextAci(): ByteArray {
synchronized(this) {
lowerGenAci++
var uuid = UUID(upperGenAci, lowerGenAci)
return uuid.toByteArray()
}
}
fun generateGroupMasterKey(): ByteArray {
val masterKey = ByteArray(32)
groupMasterKeyRandom.nextBytes(masterKey)
return masterKey
}
}

View File

@@ -10,18 +10,17 @@ import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.mock
import org.signal.core.util.ThreadUtil
import org.signal.libsignal.protocol.state.SignedPreKeyRecord
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.dependencies.InstrumentationApplicationDependencyProvider
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.pin.KbsRepository
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.registration.VerifyAccountRepository
import org.thoughtcrime.securesms.registration.VerifyAccountResponseProcessor
import org.thoughtcrime.securesms.registration.VerifyResponseProcessor
import org.thoughtcrime.securesms.testing.Get
import org.thoughtcrime.securesms.testing.MockProvider
import org.thoughtcrime.securesms.testing.Post
import org.thoughtcrime.securesms.testing.Put
import org.thoughtcrime.securesms.testing.SignalActivityRule
import org.thoughtcrime.securesms.testing.assertIs
@@ -36,6 +35,7 @@ import org.thoughtcrime.securesms.testing.success
import org.thoughtcrime.securesms.testing.timeout
import org.whispersystems.signalservice.api.account.ChangePhoneNumberRequest
import org.whispersystems.signalservice.api.push.ServiceId
import org.whispersystems.signalservice.api.push.ServiceId.PNI
import org.whispersystems.signalservice.internal.push.MismatchedDevices
import org.whispersystems.signalservice.internal.push.PreKeyState
import java.util.UUID
@@ -47,20 +47,16 @@ class ChangeNumberViewModelTest {
val harness = SignalActivityRule()
private lateinit var viewModel: ChangeNumberViewModel
private lateinit var kbsRepository: KbsRepository
@Before
fun setUp() {
ApplicationDependencies.getSignalServiceAccountManager().setSoTimeoutMillis(1000)
kbsRepository = mock()
ThreadUtil.runOnMainSync {
viewModel = ChangeNumberViewModel(
localNumber = harness.self.requireE164(),
changeNumberRepository = ChangeNumberRepository(),
savedState = SavedStateHandle(),
password = SignalStore.account().servicePassword!!,
verifyAccountRepository = VerifyAccountRepository(harness.application),
kbsRepository = kbsRepository
verifyAccountRepository = VerifyAccountRepository(harness.application)
)
viewModel.setNewCountry(1)
@@ -77,13 +73,15 @@ class ChangeNumberViewModelTest {
fun testChangeNumber_givenOnlyPrimaryAndNoRegLock() {
// GIVEN
val aci = Recipient.self().requireServiceId()
val newPni = ServiceId.from(UUID.randomUUID())
val newPni = PNI.from(UUID.randomUUID())
lateinit var changeNumberRequest: ChangePhoneNumberRequest
lateinit var setPreKeysRequest: PreKeyState
InstrumentationApplicationDependencyProvider.addMockWebRequestHandlers(
Post("/v1/verification/session") { MockResponse().success(MockProvider.sessionMetadataJson.copy(verified = false)) },
Put("/v1/verification/session/${MockProvider.sessionMetadataJson.id}/code") { MockResponse().success(MockProvider.sessionMetadataJson) },
Get("/v1/devices") { MockResponse().success(MockProvider.primaryOnlyDeviceList) },
Put("/v1/accounts/number") { r ->
Put("/v2/accounts/number") { r ->
changeNumberRequest = r.parsedRequestBody()
MockResponse().success(MockProvider.createVerifyAccountResponse(aci, newPni))
},
@@ -95,6 +93,7 @@ class ChangeNumberViewModelTest {
)
// WHEN
viewModel.requestVerificationCode(VerifyAccountRepository.Mode.SMS_WITHOUT_LISTENER, null, null).blockingGet().resultOrThrow
viewModel.verifyCodeWithoutRegistrationLock("123456").blockingGet().resultOrThrow
// THEN
@@ -112,12 +111,15 @@ class ChangeNumberViewModelTest {
val oldE164 = Recipient.self().requireE164()
InstrumentationApplicationDependencyProvider.addMockWebRequestHandlers(
Post("/v1/verification/session") { MockResponse().success(MockProvider.sessionMetadataJson.copy(verified = false)) },
Put("/v1/verification/session/${MockProvider.sessionMetadataJson.id}/code") { MockResponse().success(MockProvider.sessionMetadataJson) },
Get("/v1/devices") { MockResponse().success(MockProvider.primaryOnlyDeviceList) },
Put("/v1/accounts/number") { MockResponse().failure(500) },
Put("/v2/accounts/number") { MockResponse().failure(500) }
)
// WHEN
val processor: VerifyAccountResponseProcessor = viewModel.verifyCodeWithoutRegistrationLock("123456").blockingGet()
viewModel.requestVerificationCode(VerifyAccountRepository.Mode.SMS_WITHOUT_LISTENER, null, null).blockingGet().resultOrThrow
val processor: VerifyResponseProcessor = viewModel.verifyCodeWithoutRegistrationLock("123456").blockingGet()
// THEN
processor.isServerSentError() assertIs true
@@ -142,13 +144,16 @@ class ChangeNumberViewModelTest {
val oldE164 = Recipient.self().requireE164()
InstrumentationApplicationDependencyProvider.addMockWebRequestHandlers(
Post("/v1/verification/session") { MockResponse().success(MockProvider.sessionMetadataJson.copy(verified = false)) },
Put("/v1/verification/session/${MockProvider.sessionMetadataJson.id}/code") { MockResponse().success(MockProvider.sessionMetadataJson) },
Get("/v1/devices") { MockResponse().success(MockProvider.primaryOnlyDeviceList) },
Put("/v1/accounts/number") { MockResponse().connectionFailure() },
Put("/v2/accounts/number") { MockResponse().connectionFailure() },
Get("/v1/accounts/whoami") { MockResponse().success(MockProvider.createWhoAmIResponse(aci, oldPni, oldE164)) }
)
// WHEN
val processor: VerifyAccountResponseProcessor = viewModel.verifyCodeWithoutRegistrationLock("123456").blockingGet()
viewModel.requestVerificationCode(VerifyAccountRepository.Mode.SMS_WITHOUT_LISTENER, null, null).blockingGet().resultOrThrow
val processor: VerifyResponseProcessor = viewModel.verifyCodeWithoutRegistrationLock("123456").blockingGet()
// THEN
processor.isServerSentError() assertIs false
@@ -175,14 +180,16 @@ class ChangeNumberViewModelTest {
val aci = Recipient.self().requireServiceId()
val oldPni = Recipient.self().requirePni()
val oldE164 = Recipient.self().requireE164()
val newPni = ServiceId.from(UUID.randomUUID())
val newPni = PNI.from(UUID.randomUUID())
lateinit var changeNumberRequest: ChangePhoneNumberRequest
lateinit var setPreKeysRequest: PreKeyState
InstrumentationApplicationDependencyProvider.addMockWebRequestHandlers(
Post("/v1/verification/session") { MockResponse().success(MockProvider.sessionMetadataJson.copy(verified = false)) },
Put("/v1/verification/session/${MockProvider.sessionMetadataJson.id}/code") { MockResponse().success(MockProvider.sessionMetadataJson) },
Get("/v1/devices") { MockResponse().success(MockProvider.primaryOnlyDeviceList) },
Put("/v1/accounts/number") { r ->
Put("/v2/accounts/number") { r ->
changeNumberRequest = r.parsedRequestBody()
MockResponse().timeout()
},
@@ -195,7 +202,8 @@ class ChangeNumberViewModelTest {
)
// WHEN
val processor: VerifyAccountResponseProcessor = viewModel.verifyCodeWithoutRegistrationLock("123456").blockingGet()
viewModel.requestVerificationCode(VerifyAccountRepository.Mode.SMS_WITHOUT_LISTENER, null, null).blockingGet().resultOrThrow
val processor: VerifyResponseProcessor = viewModel.verifyCodeWithoutRegistrationLock("123456").blockingGet()
// THEN
processor.isServerSentError() assertIs false
@@ -217,16 +225,16 @@ class ChangeNumberViewModelTest {
fun testChangeNumber_givenOnlyPrimaryAndRegistrationLock() {
// GIVEN
val aci = Recipient.self().requireServiceId()
val newPni = ServiceId.from(UUID.randomUUID())
val newPni = PNI.from(UUID.randomUUID())
lateinit var changeNumberRequest: ChangePhoneNumberRequest
lateinit var setPreKeysRequest: PreKeyState
MockProvider.mockGetRegistrationLockStringFlow(kbsRepository)
InstrumentationApplicationDependencyProvider.addMockWebRequestHandlers(
Post("/v1/verification/session") { MockResponse().success(MockProvider.sessionMetadataJson.copy(verified = false)) },
Put("/v1/verification/session/${MockProvider.sessionMetadataJson.id}/code") { MockResponse().success(MockProvider.sessionMetadataJson) },
Get("/v1/devices") { MockResponse().success(MockProvider.primaryOnlyDeviceList) },
Put("/v1/accounts/number") { r ->
Put("/v2/accounts/number") { r ->
changeNumberRequest = r.parsedRequestBody()
if (changeNumberRequest.registrationLock.isNullOrEmpty()) {
MockResponse().failure(423, MockProvider.lockedFailure)
@@ -242,6 +250,7 @@ class ChangeNumberViewModelTest {
)
// WHEN
viewModel.requestVerificationCode(VerifyAccountRepository.Mode.SMS_WITHOUT_LISTENER, null, null).blockingGet().resultOrThrow
viewModel.verifyCodeWithoutRegistrationLock("123456").blockingGet().also { processor ->
processor.registrationLock() assertIs true
Recipient.self().requirePni() assertIsNot newPni
@@ -258,13 +267,15 @@ class ChangeNumberViewModelTest {
fun testChangeNumber_givenMismatchedDevicesOnFirstCall() {
// GIVEN
val aci = Recipient.self().requireServiceId()
val newPni = ServiceId.from(UUID.randomUUID())
val newPni = PNI.from(UUID.randomUUID())
lateinit var changeNumberRequest: ChangePhoneNumberRequest
lateinit var setPreKeysRequest: PreKeyState
InstrumentationApplicationDependencyProvider.addMockWebRequestHandlers(
Post("/v1/verification/session") { MockResponse().success(MockProvider.sessionMetadataJson.copy(verified = false)) },
Put("/v1/verification/session/${MockProvider.sessionMetadataJson.id}/code") { MockResponse().success(MockProvider.sessionMetadataJson) },
Get("/v1/devices") { MockResponse().success(MockProvider.primaryOnlyDeviceList) },
Put("/v1/accounts/number") { r ->
Put("/v2/accounts/number") { r ->
changeNumberRequest = r.parsedRequestBody()
if (changeNumberRequest.deviceMessages.isEmpty()) {
MockResponse().failure(
@@ -289,6 +300,7 @@ class ChangeNumberViewModelTest {
)
// WHEN
viewModel.requestVerificationCode(VerifyAccountRepository.Mode.SMS_WITHOUT_LISTENER, null, null).blockingGet().resultOrThrow
viewModel.verifyCodeWithoutRegistrationLock("123456").blockingGet().resultOrThrow
// THEN
@@ -299,15 +311,15 @@ class ChangeNumberViewModelTest {
fun testChangeNumber_givenRegLockAndMismatchedDevicesOnFirstTwoCalls() {
// GIVEN
val aci = Recipient.self().requireServiceId()
val newPni = ServiceId.from(UUID.randomUUID())
val newPni = PNI.from(UUID.randomUUID())
lateinit var changeNumberRequest: ChangePhoneNumberRequest
lateinit var setPreKeysRequest: PreKeyState
MockProvider.mockGetRegistrationLockStringFlow(kbsRepository)
InstrumentationApplicationDependencyProvider.addMockWebRequestHandlers(
Put("/v1/accounts/number") { r ->
Post("/v1/verification/session") { MockResponse().success(MockProvider.sessionMetadataJson.copy(verified = false)) },
Put("/v1/verification/session/${MockProvider.sessionMetadataJson.id}/code") { MockResponse().success(MockProvider.sessionMetadataJson) },
Put("/v2/accounts/number") { r ->
changeNumberRequest = r.parsedRequestBody()
if (changeNumberRequest.registrationLock.isNullOrEmpty()) {
MockResponse().failure(423, MockProvider.lockedFailure)
@@ -345,6 +357,7 @@ class ChangeNumberViewModelTest {
)
// WHEN
viewModel.requestVerificationCode(VerifyAccountRepository.Mode.SMS_WITHOUT_LISTENER, null, null).blockingGet().resultOrThrow
viewModel.verifyCodeWithoutRegistrationLock("123456").blockingGet().also { processor ->
processor.registrationLock() assertIs true
Recipient.self().requirePni() assertIsNot newPni

View File

@@ -8,9 +8,11 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.signal.core.util.ThreadUtil
import org.thoughtcrime.securesms.attachments.PointerAttachment
import org.thoughtcrime.securesms.conversation.v2.ConversationActivity
import org.thoughtcrime.securesms.database.MessageType
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.mms.IncomingMediaMessage
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage
import org.thoughtcrime.securesms.mms.IncomingMessage
import org.thoughtcrime.securesms.mms.OutgoingMessage
import org.thoughtcrime.securesms.profiles.ProfileName
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.releasechannel.ReleaseChannel
@@ -63,16 +65,17 @@ class ConversationItemPreviewer {
attachment()
}
val message = IncomingMediaMessage(
val message = IncomingMessage(
type = MessageType.NORMAL,
from = other.id,
body = body,
sentTimeMillis = System.currentTimeMillis(),
serverTimeMillis = System.currentTimeMillis(),
receivedTimeMillis = System.currentTimeMillis(),
attachments = PointerAttachment.forPointers(Optional.of(attachments)),
attachments = PointerAttachment.forPointers(Optional.of(attachments))
)
SignalDatabase.mms.insertSecureDecryptedMessageInbox(message, SignalDatabase.threads.getOrCreateThreadIdFor(other)).get()
SignalDatabase.messages.insertMessageInbox(message, SignalDatabase.threads.getOrCreateThreadIdFor(other)).get()
ThreadUtil.sleep(1)
}
@@ -82,16 +85,17 @@ class ConversationItemPreviewer {
attachment()
}
val message = IncomingMediaMessage(
val message = IncomingMessage(
type = MessageType.NORMAL,
from = other.id,
body = body,
sentTimeMillis = System.currentTimeMillis(),
serverTimeMillis = System.currentTimeMillis(),
receivedTimeMillis = System.currentTimeMillis(),
attachments = PointerAttachment.forPointers(Optional.of(attachments)),
attachments = PointerAttachment.forPointers(Optional.of(attachments))
)
val insert = SignalDatabase.mms.insertSecureDecryptedMessageInbox(message, SignalDatabase.threads.getOrCreateThreadIdFor(other)).get()
val insert = SignalDatabase.messages.insertMessageInbox(message, SignalDatabase.threads.getOrCreateThreadIdFor(other)).get()
SignalDatabase.attachments.getAttachmentsForMessage(insert.messageId).forEachIndexed { index, attachment ->
// if (index != 1) {
@@ -109,7 +113,7 @@ class ConversationItemPreviewer {
attachment()
}
val message = OutgoingMediaMessage(
val message = OutgoingMessage(
recipient = other,
body = body,
attachments = PointerAttachment.forPointers(Optional.of(attachments)),
@@ -117,7 +121,7 @@ class ConversationItemPreviewer {
isSecure = true
)
val insert = SignalDatabase.mms.insertMessageOutbox(
val insert = SignalDatabase.messages.insertMessageOutbox(
message,
SignalDatabase.threads.getOrCreateThreadIdFor(other),
false,
@@ -142,6 +146,8 @@ class ConversationItemPreviewer {
1024,
1024,
Optional.empty(),
Optional.empty(),
0,
Optional.of("/not-there.jpg"),
false,
false,

View File

@@ -7,6 +7,7 @@ import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey
import org.thoughtcrime.securesms.conversation.v2.ConversationActivity
import org.thoughtcrime.securesms.database.IdentityTable
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.model.DistributionListId
@@ -64,7 +65,7 @@ class SafetyNumberChangeDialogPreviewer {
SafetyNumberBottomSheet
.forIdentityRecordsAndDestinations(
identityRecords = ApplicationDependencies.getProtocolStore().aci().identities().getIdentityRecords(othersRecipients).identityRecords,
destinations = listOf(ContactSearchKey.RecipientSearchKey.Story(myStoryRecipientId))
destinations = listOf(ContactSearchKey.RecipientSearchKey(myStoryRecipientId, true))
)
.show(conversationActivity.supportFragmentManager)
}

View File

@@ -0,0 +1,332 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.conversation.v2.items
import android.net.Uri
import android.view.View
import androidx.lifecycle.Observer
import com.bumptech.glide.RequestManager
import io.mockk.mockk
import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.Test
import org.signal.ringrtc.CallLinkRootKey
import org.thoughtcrime.securesms.components.voice.VoiceNotePlaybackState
import org.thoughtcrime.securesms.contactshare.Contact
import org.thoughtcrime.securesms.conversation.ConversationAdapter
import org.thoughtcrime.securesms.conversation.ConversationItem
import org.thoughtcrime.securesms.conversation.ConversationItemDisplayMode
import org.thoughtcrime.securesms.conversation.ConversationMessage
import org.thoughtcrime.securesms.conversation.colors.Colorizer
import org.thoughtcrime.securesms.conversation.mutiselect.MultiselectPart
import org.thoughtcrime.securesms.database.FakeMessageRecords
import org.thoughtcrime.securesms.database.model.InMemoryMessageRecord
import org.thoughtcrime.securesms.database.model.MessageRecord
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
import org.thoughtcrime.securesms.groups.GroupId
import org.thoughtcrime.securesms.groups.GroupMigrationMembershipChange
import org.thoughtcrime.securesms.linkpreview.LinkPreview
import org.thoughtcrime.securesms.mediapreview.MediaIntentFactory
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.stickers.StickerLocator
import org.thoughtcrime.securesms.testing.SignalActivityRule
import kotlin.time.Duration.Companion.minutes
class V2ConversationItemShapeTest {
@get:Rule
val harness = SignalActivityRule(othersCount = 10)
@Test
fun givenNextAndPreviousMessageDoNotExist_whenISetMessageShape_thenIExpectSingle() {
val testSubject = V2ConversationItemShape(FakeConversationContext())
val expected = V2ConversationItemShape.MessageShape.SINGLE
val actual = testSubject.setMessageShape(
currentMessage = getMessageRecord(),
isGroupThread = false,
adapterPosition = 5
)
assertEquals(expected, actual)
}
@Test
fun givenPreviousWithinTimeoutAndNoNext_whenISetMessageShape_thenIExpectEnd() {
val now = System.currentTimeMillis()
val prev = now - 2.minutes.inWholeMilliseconds
val testSubject = V2ConversationItemShape(
FakeConversationContext(
previousMessage = getMessageRecord(prev)
)
)
val expected = V2ConversationItemShape.MessageShape.END
val actual = testSubject.setMessageShape(
currentMessage = getMessageRecord(now),
isGroupThread = false,
adapterPosition = 5
)
assertEquals(expected, actual)
}
@Test
fun givenNextWithinTimeoutAndNoPrevious_whenISetMessageShape_thenIExpectStart() {
val now = System.currentTimeMillis()
val prev = now - 2.minutes.inWholeMilliseconds
val testSubject = V2ConversationItemShape(
FakeConversationContext(
nextMessage = getMessageRecord(now)
)
)
val expected = V2ConversationItemShape.MessageShape.START
val actual = testSubject.setMessageShape(
currentMessage = getMessageRecord(prev),
isGroupThread = false,
adapterPosition = 5
)
assertEquals(expected, actual)
}
@Test
fun givenPreviousAndNextWithinTimeout_whenISetMessageShape_thenIExpectMiddle() {
val now = System.currentTimeMillis()
val prev = now - 2.minutes.inWholeMilliseconds
val next = now + 2.minutes.inWholeMilliseconds
val testSubject = V2ConversationItemShape(
FakeConversationContext(
previousMessage = getMessageRecord(prev),
nextMessage = getMessageRecord(next)
)
)
val expected = V2ConversationItemShape.MessageShape.MIDDLE
val actual = testSubject.setMessageShape(
currentMessage = getMessageRecord(now),
isGroupThread = false,
adapterPosition = 5
)
assertEquals(expected, actual)
}
@Test
fun givenPreviousOutsideTimeoutAndNoNext_whenISetMessageShape_thenIExpectSingle() {
val now = System.currentTimeMillis()
val prev = now - 4.minutes.inWholeMilliseconds
val testSubject = V2ConversationItemShape(
FakeConversationContext(
previousMessage = getMessageRecord(prev)
)
)
val expected = V2ConversationItemShape.MessageShape.SINGLE
val actual = testSubject.setMessageShape(
currentMessage = getMessageRecord(now),
isGroupThread = false,
adapterPosition = 5
)
assertEquals(expected, actual)
}
@Test
fun givenNextOutsideTimeoutAndNoPrevious_whenISetMessageShape_thenIExpectSingle() {
val now = System.currentTimeMillis()
val prev = now - 4.minutes.inWholeMilliseconds
val testSubject = V2ConversationItemShape(
FakeConversationContext(
nextMessage = getMessageRecord(now)
)
)
val expected = V2ConversationItemShape.MessageShape.SINGLE
val actual = testSubject.setMessageShape(
currentMessage = getMessageRecord(prev),
isGroupThread = false,
adapterPosition = 5
)
assertEquals(expected, actual)
}
@Test
fun givenPreviousAndNextOutsideTimeout_whenISetMessageShape_thenIExpectSingle() {
val now = System.currentTimeMillis()
val prev = now - 4.minutes.inWholeMilliseconds
val next = now + 4.minutes.inWholeMilliseconds
val testSubject = V2ConversationItemShape(
FakeConversationContext(
previousMessage = getMessageRecord(prev),
nextMessage = getMessageRecord(next)
)
)
val expected = V2ConversationItemShape.MessageShape.SINGLE
val actual = testSubject.setMessageShape(
currentMessage = getMessageRecord(now),
isGroupThread = false,
adapterPosition = 5
)
assertEquals(expected, actual)
}
private fun getMessageRecord(
timestamp: Long = System.currentTimeMillis()
): MessageRecord {
return FakeMessageRecords.buildMediaMmsMessageRecord(
dateReceived = timestamp,
dateSent = timestamp,
dateServer = timestamp
)
}
private class FakeConversationContext(
private val hasWallpaper: Boolean = false,
private val previousMessage: MessageRecord? = null,
private val nextMessage: MessageRecord? = null
) : V2ConversationContext {
private val colorizer = Colorizer()
override val displayMode: ConversationItemDisplayMode = ConversationItemDisplayMode.Standard
override val clickListener: ConversationAdapter.ItemClickListener = FakeConversationItemClickListener
override val selectedItems: Set<MultiselectPart> = emptySet()
override val isMessageRequestAccepted: Boolean = true
override val searchQuery: String? = null
override val requestManager: RequestManager = mockk()
override val isParentInScroll: Boolean = false
override fun getChatColorsData(): ChatColorsDrawable.ChatColorsData = ChatColorsDrawable.ChatColorsData(null, null)
override fun onStartExpirationTimeout(messageRecord: MessageRecord) = Unit
override fun hasWallpaper(): Boolean = hasWallpaper
override fun getColorizer(): Colorizer = colorizer
override fun getNextMessage(adapterPosition: Int): MessageRecord? = nextMessage
override fun getPreviousMessage(adapterPosition: Int): MessageRecord? = previousMessage
}
private object FakeConversationItemClickListener : ConversationAdapter.ItemClickListener {
override fun onQuoteClicked(messageRecord: MmsMessageRecord?) = Unit
override fun onLinkPreviewClicked(linkPreview: LinkPreview) = Unit
override fun onQuotedIndicatorClicked(messageRecord: MessageRecord) = Unit
override fun onMoreTextClicked(conversationRecipientId: RecipientId, messageId: Long, isMms: Boolean) = Unit
override fun onStickerClicked(stickerLocator: StickerLocator) = Unit
override fun onViewOnceMessageClicked(messageRecord: MmsMessageRecord) = Unit
override fun onSharedContactDetailsClicked(contact: Contact, avatarTransitionView: View) = Unit
override fun onAddToContactsClicked(contact: Contact) = Unit
override fun onMessageSharedContactClicked(choices: MutableList<Recipient>) = Unit
override fun onInviteSharedContactClicked(choices: MutableList<Recipient>) = Unit
override fun onReactionClicked(multiselectPart: MultiselectPart, messageId: Long, isMms: Boolean) = Unit
override fun onGroupMemberClicked(recipientId: RecipientId, groupId: GroupId) = Unit
override fun onMessageWithErrorClicked(messageRecord: MessageRecord) = Unit
override fun onMessageWithRecaptchaNeededClicked(messageRecord: MessageRecord) = Unit
override fun onIncomingIdentityMismatchClicked(recipientId: RecipientId) = Unit
override fun onRegisterVoiceNoteCallbacks(onPlaybackStartObserver: Observer<VoiceNotePlaybackState>) = Unit
override fun onUnregisterVoiceNoteCallbacks(onPlaybackStartObserver: Observer<VoiceNotePlaybackState>) = Unit
override fun onVoiceNotePause(uri: Uri) = Unit
override fun onVoiceNotePlay(uri: Uri, messageId: Long, position: Double) = Unit
override fun onVoiceNoteSeekTo(uri: Uri, position: Double) = Unit
override fun onVoiceNotePlaybackSpeedChanged(uri: Uri, speed: Float) = Unit
override fun onGroupMigrationLearnMoreClicked(membershipChange: GroupMigrationMembershipChange) = Unit
override fun onChatSessionRefreshLearnMoreClicked() = Unit
override fun onBadDecryptLearnMoreClicked(author: RecipientId) = Unit
override fun onSafetyNumberLearnMoreClicked(recipient: Recipient) = Unit
override fun onJoinGroupCallClicked() = Unit
override fun onInviteFriendsToGroupClicked(groupId: GroupId.V2) = Unit
override fun onEnableCallNotificationsClicked() = Unit
override fun onPlayInlineContent(conversationMessage: ConversationMessage?) = Unit
override fun onInMemoryMessageClicked(messageRecord: InMemoryMessageRecord) = Unit
override fun onViewGroupDescriptionChange(groupId: GroupId?, description: String, isMessageRequestAccepted: Boolean) = Unit
override fun onChangeNumberUpdateContact(recipient: Recipient) = Unit
override fun onCallToAction(action: String) = Unit
override fun onDonateClicked() = Unit
override fun onBlockJoinRequest(recipient: Recipient) = Unit
override fun onRecipientNameClicked(target: RecipientId) = Unit
override fun onInviteToSignalClicked() = Unit
override fun onActivatePaymentsClicked() = Unit
override fun onSendPaymentClicked(recipientId: RecipientId) = Unit
override fun onScheduledIndicatorClicked(view: View, conversationMessage: ConversationMessage) = Unit
override fun onUrlClicked(url: String): Boolean = false
override fun onViewGiftBadgeClicked(messageRecord: MessageRecord) = Unit
override fun onGiftBadgeRevealed(messageRecord: MessageRecord) = Unit
override fun goToMediaPreview(parent: ConversationItem?, sharedElement: View?, args: MediaIntentFactory.MediaPreviewArgs?) = Unit
override fun onEditedIndicatorClicked(messageRecord: MessageRecord) = Unit
override fun onShowGroupDescriptionClicked(groupName: String, description: String, shouldLinkifyWebLinks: Boolean) = Unit
override fun onJoinCallLink(callLinkRootKey: CallLinkRootKey) = Unit
override fun onItemClick(item: MultiselectPart?) = Unit
override fun onItemLongClick(itemView: View?, item: MultiselectPart?) = Unit
override fun onShowSafetyTips(forGroup: Boolean) = Unit
override fun onReportSpamLearnMoreClicked() = Unit
override fun onMessageRequestAcceptOptionsClicked() = Unit
}
}

View File

@@ -2,15 +2,20 @@ package org.thoughtcrime.securesms.database
import android.net.Uri
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.FlakyTest
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotEquals
import org.junit.Before
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.thoughtcrime.securesms.attachments.AttachmentId
import org.thoughtcrime.securesms.attachments.UriAttachment
import org.thoughtcrime.securesms.mms.MediaStream
import org.thoughtcrime.securesms.mms.SentMediaQuality
import org.thoughtcrime.securesms.providers.BlobProvider
import org.thoughtcrime.securesms.testing.assertIs
import org.thoughtcrime.securesms.testing.assertIsNot
import org.thoughtcrime.securesms.util.MediaUtil
import java.util.Optional
@@ -34,6 +39,7 @@ class AttachmentTableTest {
assertEquals(attachment2.fileName, attachment.fileName)
}
@FlakyTest
@Test
fun givenABlobAndDifferentTransformQuality_whenIInsert2AttachmentsForPreUpload_thenIExpectDifferentFileInfos() {
val blob = BlobProvider.getInstance().forData(byteArrayOf(1, 2, 3, 4, 5)).createForSingleSessionInMemory()
@@ -45,22 +51,22 @@ class AttachmentTableTest {
SignalDatabase.attachments.updateAttachmentData(
attachment,
createMediaStream(byteArrayOf(1, 2, 3, 4, 5)),
false
createMediaStream(byteArrayOf(1, 2, 3, 4, 5))
)
SignalDatabase.attachments.updateAttachmentData(
attachment2,
createMediaStream(byteArrayOf(1, 2, 3)),
false
createMediaStream(byteArrayOf(1, 2, 3))
)
val attachment1Info = SignalDatabase.attachments.getAttachmentDataFileInfo(attachment.attachmentId, AttachmentTable.DATA)
val attachment2Info = SignalDatabase.attachments.getAttachmentDataFileInfo(attachment2.attachmentId, AttachmentTable.DATA)
val attachment1Info = SignalDatabase.attachments.getDataFileInfo(attachment.attachmentId)
val attachment2Info = SignalDatabase.attachments.getDataFileInfo(attachment2.attachmentId)
assertNotEquals(attachment1Info, attachment2Info)
}
@FlakyTest
@Ignore("test is flaky")
@Test
fun givenIdenticalAttachmentsInsertedForPreUpload_whenIUpdateAttachmentDataAndSpecifyOnlyModifyThisAttachment_thenIExpectDifferentFileInfos() {
val blob = BlobProvider.getInstance().forData(byteArrayOf(1, 2, 3, 4, 5)).createForSingleSessionInMemory()
@@ -71,22 +77,92 @@ class AttachmentTableTest {
SignalDatabase.attachments.updateAttachmentData(
attachment,
createMediaStream(byteArrayOf(1, 2, 3, 4, 5)),
true
createMediaStream(byteArrayOf(1, 2, 3, 4, 5))
)
SignalDatabase.attachments.updateAttachmentData(
attachment2,
createMediaStream(byteArrayOf(1, 2, 3, 4)),
true
createMediaStream(byteArrayOf(1, 2, 3, 4))
)
val attachment1Info = SignalDatabase.attachments.getAttachmentDataFileInfo(attachment.attachmentId, AttachmentTable.DATA)
val attachment2Info = SignalDatabase.attachments.getAttachmentDataFileInfo(attachment2.attachmentId, AttachmentTable.DATA)
val attachment1Info = SignalDatabase.attachments.getDataFileInfo(attachment.attachmentId)
val attachment2Info = SignalDatabase.attachments.getDataFileInfo(attachment2.attachmentId)
assertNotEquals(attachment1Info, attachment2Info)
}
/**
* Given: A previous attachment and two pre-upload attachments with the same data but different transform properties (standard and high).
*
* When changing content of standard pre-upload attachment to match pre-existing attachment
*
* Then update standard pre-upload attachment to match previous attachment, do not update high pre-upload attachment, and do
* not delete shared pre-upload uri from disk as it is still being used by the high pre-upload attachment.
*/
@Test
fun doNotDeleteDedupedFileIfUsedByAnotherAttachmentWithADifferentTransformProperties() {
// GIVEN
val uncompressData = byteArrayOf(1, 2, 3, 4, 5)
val compressedData = byteArrayOf(1, 2, 3)
val blobUncompressed = BlobProvider.getInstance().forData(uncompressData).createForSingleSessionInMemory()
val previousAttachment = createAttachment(1, BlobProvider.getInstance().forData(compressedData).createForSingleSessionInMemory(), AttachmentTable.TransformProperties.empty())
val previousDatabaseAttachmentId: AttachmentId = SignalDatabase.attachments.insertAttachmentsForMessage(1, listOf(previousAttachment), emptyList()).values.first()
val standardQualityPreUpload = createAttachment(1, blobUncompressed, AttachmentTable.TransformProperties.empty())
val standardDatabaseAttachment = SignalDatabase.attachments.insertAttachmentForPreUpload(standardQualityPreUpload)
val highQualityPreUpload = createAttachment(1, blobUncompressed, AttachmentTable.TransformProperties.forSentMediaQuality(Optional.empty(), SentMediaQuality.HIGH))
val highDatabaseAttachment = SignalDatabase.attachments.insertAttachmentForPreUpload(highQualityPreUpload)
// WHEN
SignalDatabase.attachments.updateAttachmentData(standardDatabaseAttachment, createMediaStream(compressedData))
// THEN
val previousInfo = SignalDatabase.attachments.getDataFileInfo(previousDatabaseAttachmentId)!!
val standardInfo = SignalDatabase.attachments.getDataFileInfo(standardDatabaseAttachment.attachmentId)!!
val highInfo = SignalDatabase.attachments.getDataFileInfo(highDatabaseAttachment.attachmentId)!!
assertNotEquals(standardInfo, highInfo)
highInfo.file assertIsNot standardInfo.file
highInfo.file.exists() assertIs true
}
/**
* Given: Three pre-upload attachments with the same data but different transform properties (1x standard and 2x high).
*
* When inserting content of high pre-upload attachment.
*
* Then do not deduplicate with standard pre-upload attachment, but do deduplicate second high insert.
*/
@Test
fun doNotDedupedFileIfUsedByAnotherAttachmentWithADifferentTransformProperties() {
// GIVEN
val uncompressData = byteArrayOf(1, 2, 3, 4, 5)
val blobUncompressed = BlobProvider.getInstance().forData(uncompressData).createForSingleSessionInMemory()
val standardQualityPreUpload = createAttachment(1, blobUncompressed, AttachmentTable.TransformProperties.empty())
val standardDatabaseAttachment = SignalDatabase.attachments.insertAttachmentForPreUpload(standardQualityPreUpload)
// WHEN
val highQualityPreUpload = createAttachment(1, blobUncompressed, AttachmentTable.TransformProperties.forSentMediaQuality(Optional.empty(), SentMediaQuality.HIGH))
val highDatabaseAttachment = SignalDatabase.attachments.insertAttachmentForPreUpload(highQualityPreUpload)
val secondHighQualityPreUpload = createAttachment(1, blobUncompressed, AttachmentTable.TransformProperties.forSentMediaQuality(Optional.empty(), SentMediaQuality.HIGH))
val secondHighDatabaseAttachment = SignalDatabase.attachments.insertAttachmentForPreUpload(secondHighQualityPreUpload)
// THEN
val standardInfo = SignalDatabase.attachments.getDataFileInfo(standardDatabaseAttachment.attachmentId)!!
val highInfo = SignalDatabase.attachments.getDataFileInfo(highDatabaseAttachment.attachmentId)!!
val secondHighInfo = SignalDatabase.attachments.getDataFileInfo(secondHighDatabaseAttachment.attachmentId)!!
highInfo.file assertIsNot standardInfo.file
secondHighInfo.file assertIs highInfo.file
standardInfo.file.exists() assertIs true
highInfo.file.exists() assertIs true
}
private fun createAttachment(id: Long, uri: Uri, transformProperties: AttachmentTable.TransformProperties): UriAttachment {
return UriAttachmentBuilder.build(
id,

View File

@@ -0,0 +1,804 @@
package org.thoughtcrime.securesms.database
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Assert.assertArrayEquals
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.signal.core.util.Base64
import org.signal.core.util.update
import org.thoughtcrime.securesms.attachments.AttachmentId
import org.thoughtcrime.securesms.attachments.PointerAttachment
import org.thoughtcrime.securesms.database.AttachmentTable.TransformProperties
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.mms.MediaStream
import org.thoughtcrime.securesms.mms.OutgoingMessage
import org.thoughtcrime.securesms.mms.QuoteModel
import org.thoughtcrime.securesms.mms.SentMediaQuality
import org.thoughtcrime.securesms.providers.BlobProvider
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.util.MediaUtil
import org.whispersystems.signalservice.api.push.ServiceId
import java.io.File
import java.util.UUID
import kotlin.random.Random
import kotlin.time.Duration.Companion.days
/**
* Collection of [AttachmentTable] tests focused around deduping logic.
*/
@RunWith(AndroidJUnit4::class)
class AttachmentTableTest_deduping {
companion object {
val DATA_A = byteArrayOf(1, 2, 3)
val DATA_A_COMPRESSED = byteArrayOf(4, 5, 6)
val DATA_A_HASH = byteArrayOf(1, 1, 1)
val DATA_B = byteArrayOf(7, 8, 9)
}
@Before
fun setUp() {
SignalStore.account().setAci(ServiceId.ACI.from(UUID.randomUUID()))
SignalStore.account().setPni(ServiceId.PNI.from(UUID.randomUUID()))
SignalStore.account().setE164("+15558675309")
SignalDatabase.attachments.deleteAllAttachments()
}
/**
* Creates two different files with different data. Should not dedupe.
*/
@Test
fun differentFiles() {
test {
val id1 = insertWithData(DATA_A)
val id2 = insertWithData(DATA_B)
assertDataFilesAreDifferent(id1, id2)
}
}
/**
* Inserts files with identical data but with transform properties that make them incompatible. Should not dedupe.
*/
@Test
fun identicalFiles_incompatibleTransforms() {
// Non-matching qualities
test {
val id1 = insertWithData(DATA_A, TransformProperties(sentMediaQuality = SentMediaQuality.STANDARD.code))
val id2 = insertWithData(DATA_A, TransformProperties(sentMediaQuality = SentMediaQuality.HIGH.code))
assertDataFilesAreDifferent(id1, id2)
assertDataHashStartMatches(id1, id2)
}
// Non-matching video trim flag
test {
val id1 = insertWithData(DATA_A, TransformProperties())
val id2 = insertWithData(DATA_A, TransformProperties(videoTrim = true))
assertDataFilesAreDifferent(id1, id2)
assertDataHashStartMatches(id1, id2)
}
// Non-matching video trim start time
test {
val id1 = insertWithData(DATA_A, TransformProperties(videoTrim = true, videoTrimStartTimeUs = 1, videoTrimEndTimeUs = 2))
val id2 = insertWithData(DATA_A, TransformProperties(videoTrim = true, videoTrimStartTimeUs = 0, videoTrimEndTimeUs = 2))
assertDataFilesAreDifferent(id1, id2)
assertDataHashStartMatches(id1, id2)
}
// Non-matching video trim end time
test {
val id1 = insertWithData(DATA_A, TransformProperties(videoTrim = true, videoTrimStartTimeUs = 0, videoTrimEndTimeUs = 1))
val id2 = insertWithData(DATA_A, TransformProperties(videoTrim = true, videoTrimStartTimeUs = 0, videoTrimEndTimeUs = 2))
assertDataFilesAreDifferent(id1, id2)
assertDataHashStartMatches(id1, id2)
}
// Non-matching mp4 fast start
test {
val id1 = insertWithData(DATA_A, TransformProperties(mp4FastStart = true))
val id2 = insertWithData(DATA_A, TransformProperties(mp4FastStart = false))
assertDataFilesAreDifferent(id1, id2)
assertDataHashStartMatches(id1, id2)
}
}
/**
* Inserts files with identical data and compatible transform properties. Should dedupe.
*/
@Test
fun identicalFiles_compatibleTransforms() {
test {
val id1 = insertWithData(DATA_A)
val id2 = insertWithData(DATA_A)
assertDataFilesAreTheSame(id1, id2)
assertDataHashStartMatches(id1, id2)
assertSkipTransform(id1, false)
assertSkipTransform(id2, false)
}
test {
val id1 = insertWithData(DATA_A, TransformProperties(sentMediaQuality = SentMediaQuality.STANDARD.code))
val id2 = insertWithData(DATA_A, TransformProperties(sentMediaQuality = SentMediaQuality.STANDARD.code))
assertDataFilesAreTheSame(id1, id2)
assertDataHashStartMatches(id1, id2)
assertSkipTransform(id1, false)
assertSkipTransform(id2, false)
}
test {
val id1 = insertWithData(DATA_A, TransformProperties(sentMediaQuality = SentMediaQuality.HIGH.code))
val id2 = insertWithData(DATA_A, TransformProperties(sentMediaQuality = SentMediaQuality.HIGH.code))
assertDataFilesAreTheSame(id1, id2)
assertDataHashStartMatches(id1, id2)
assertSkipTransform(id1, false)
assertSkipTransform(id2, false)
}
test {
val id1 = insertWithData(DATA_A, TransformProperties(videoTrim = true, videoTrimStartTimeUs = 1, videoTrimEndTimeUs = 2))
val id2 = insertWithData(DATA_A, TransformProperties(videoTrim = true, videoTrimStartTimeUs = 1, videoTrimEndTimeUs = 2))
assertDataFilesAreTheSame(id1, id2)
assertDataHashStartMatches(id1, id2)
assertSkipTransform(id1, false)
assertSkipTransform(id2, false)
}
}
/**
* Walks through various scenarios where files are compressed and uploaded.
*/
@Test
fun compressionAndUploads() {
// Matches after the first is compressed, skip transform properly set
test {
val id1 = insertWithData(DATA_A)
compress(id1, DATA_A_COMPRESSED)
val id2 = insertWithData(DATA_A)
assertDataFilesAreTheSame(id1, id2)
assertDataHashStartMatches(id1, id2)
assertSkipTransform(id1, true)
assertSkipTransform(id2, true)
}
// Matches after the first is uploaded, skip transform and ending hash properly set
test {
val id1 = insertWithData(DATA_A)
compress(id1, DATA_A_COMPRESSED)
upload(id1)
val id2 = insertWithData(DATA_A)
assertDataFilesAreTheSame(id1, id2)
assertDataHashStartMatches(id1, id2)
assertDataHashEndMatches(id1, id2)
assertSkipTransform(id1, true)
assertSkipTransform(id2, true)
}
// Mimics sending two files at once. Ensures all fields are kept in sync as we compress and upload.
test {
val id1 = insertWithData(DATA_A)
val id2 = insertWithData(DATA_A)
assertDataFilesAreTheSame(id1, id2)
assertDataHashStartMatches(id1, id2)
assertSkipTransform(id1, false)
assertSkipTransform(id2, false)
compress(id1, DATA_A_COMPRESSED)
assertDataFilesAreTheSame(id1, id2)
assertDataHashStartMatches(id1, id2)
assertSkipTransform(id1, true)
assertSkipTransform(id2, true)
upload(id1)
assertDataFilesAreTheSame(id1, id2)
assertDataHashStartMatches(id1, id2)
assertDataHashEndMatches(id1, id2)
assertRemoteFieldsMatch(id1, id2)
}
// Re-use the upload when uploaded recently
test {
val id1 = insertWithData(DATA_A)
compress(id1, DATA_A_COMPRESSED)
upload(id1, uploadTimestamp = System.currentTimeMillis())
val id2 = insertWithData(DATA_A)
assertDataFilesAreTheSame(id1, id2)
assertDataHashStartMatches(id1, id2)
assertDataHashEndMatches(id1, id2)
assertRemoteFieldsMatch(id1, id2)
assertSkipTransform(id1, true)
assertSkipTransform(id2, true)
}
// Do not re-use old uploads
test {
val id1 = insertWithData(DATA_A)
compress(id1, DATA_A_COMPRESSED)
upload(id1, uploadTimestamp = System.currentTimeMillis() - 100.days.inWholeMilliseconds)
val id2 = insertWithData(DATA_A)
assertDataFilesAreTheSame(id1, id2)
assertDataHashStartMatches(id1, id2)
assertDataHashEndMatches(id1, id2)
assertSkipTransform(id1, true)
assertSkipTransform(id2, true)
assertDoesNotHaveRemoteFields(id2)
}
// This isn't so much "desirable behavior" as it is documenting how things work.
// If an attachment is compressed but not uploaded yet, it will have a DATA_HASH_START that doesn't match the actual file content.
// This means that if we insert a new attachment with data that matches the compressed data, we won't find a match.
// This is ok because we don't allow forwarding unsent messages, so the chances of the user somehow sending a file that matches data we compressed are very low.
// What *is* more common is that the user may send DATA_A again, and in this case we will still catch the dedupe (which is already tested above).
test {
val id1 = insertWithData(DATA_A)
compress(id1, DATA_A_COMPRESSED)
val id2 = insertWithData(DATA_A_COMPRESSED)
assertDataFilesAreDifferent(id1, id2)
}
// This represents what would happen if you forward an already-send compressed attachment. We should match, skip transform, and skip upload.
test {
val id1 = insertWithData(DATA_A)
compress(id1, DATA_A_COMPRESSED)
upload(id1, uploadTimestamp = System.currentTimeMillis())
val id2 = insertWithData(DATA_A_COMPRESSED)
assertDataFilesAreTheSame(id1, id2)
assertDataHashEndMatches(id1, id2)
assertSkipTransform(id1, true)
assertSkipTransform(id1, true)
assertRemoteFieldsMatch(id1, id2)
}
// This represents what would happen if you edited a video, sent it, then forwarded it. We should match, skip transform, and skip upload.
test {
val id1 = insertWithData(DATA_A, TransformProperties(videoTrim = true, videoTrimStartTimeUs = 1, videoTrimEndTimeUs = 2))
compress(id1, DATA_A_COMPRESSED)
upload(id1, uploadTimestamp = System.currentTimeMillis())
val id2 = insertWithData(DATA_A_COMPRESSED)
assertDataFilesAreTheSame(id1, id2)
assertDataHashEndMatches(id1, id2)
assertSkipTransform(id1, true)
assertSkipTransform(id1, true)
assertRemoteFieldsMatch(id1, id2)
}
// This represents what would happen if you edited a video, sent it, then forwarded it, but *edited the forwarded video*. We should not dedupe.
test {
val id1 = insertWithData(DATA_A, TransformProperties(videoTrim = true, videoTrimStartTimeUs = 1, videoTrimEndTimeUs = 2))
compress(id1, DATA_A_COMPRESSED)
upload(id1, uploadTimestamp = System.currentTimeMillis())
val id2 = insertWithData(DATA_A_COMPRESSED, TransformProperties(videoTrim = true, videoTrimStartTimeUs = 1, videoTrimEndTimeUs = 2))
assertDataFilesAreDifferent(id1, id2)
assertSkipTransform(id1, true)
assertSkipTransform(id2, false)
assertDoesNotHaveRemoteFields(id2)
}
// This represents what would happen if you sent an image using standard quality, then forwarded it using high quality.
// Since you're forwarding, it doesn't matter if the new thing has a higher quality, we should still match and skip transform.
test {
val id1 = insertWithData(DATA_A, TransformProperties(sentMediaQuality = SentMediaQuality.STANDARD.code))
compress(id1, DATA_A_COMPRESSED)
upload(id1, uploadTimestamp = System.currentTimeMillis())
val id2 = insertWithData(DATA_A_COMPRESSED, TransformProperties(sentMediaQuality = SentMediaQuality.HIGH.code))
assertDataFilesAreTheSame(id1, id2)
assertDataHashEndMatches(id1, id2)
assertSkipTransform(id1, true)
assertSkipTransform(id1, true)
assertRemoteFieldsMatch(id1, id2)
}
// This represents what would happen if you sent an image using high quality, then forwarded it using standard quality.
// Since you're forwarding, it doesn't matter if the new thing has a lower quality, we should still match and skip transform.
test {
val id1 = insertWithData(DATA_A, TransformProperties(sentMediaQuality = SentMediaQuality.HIGH.code))
compress(id1, DATA_A_COMPRESSED)
upload(id1, uploadTimestamp = System.currentTimeMillis())
val id2 = insertWithData(DATA_A_COMPRESSED, TransformProperties(sentMediaQuality = SentMediaQuality.STANDARD.code))
assertDataFilesAreTheSame(id1, id2)
assertDataHashEndMatches(id1, id2)
assertSkipTransform(id1, true)
assertSkipTransform(id1, true)
assertRemoteFieldsMatch(id1, id2)
}
// Make sure that files marked as unhashable are all updated together
test {
val id1 = insertWithData(DATA_A)
val id2 = insertWithData(DATA_A)
upload(id1)
upload(id2)
clearHashes(id1)
clearHashes(id2)
val file = dataFile(id1)
SignalDatabase.attachments.markDataFileAsUnhashable(file)
assertDataFilesAreTheSame(id1, id2)
assertDataHashEndMatches(id1, id2)
val dataFileInfo = SignalDatabase.attachments.getDataFileInfo(id1)!!
assertTrue(dataFileInfo.hashEnd!!.startsWith("UNHASHABLE-"))
}
}
/**
* Various deletion scenarios to ensure that duped files don't deleted while there's still references.
*/
@Test
fun deletions() {
// Delete original then dupe
test {
val id1 = insertWithData(DATA_A)
val id2 = insertWithData(DATA_A)
val dataFile = dataFile(id1)
assertDataFilesAreTheSame(id1, id2)
delete(id1)
assertDeleted(id1)
assertRowAndFileExists(id2)
assertTrue(dataFile.exists())
delete(id2)
assertDeleted(id2)
assertFalse(dataFile.exists())
}
// Delete dupe then original
test {
val id1 = insertWithData(DATA_A)
val id2 = insertWithData(DATA_A)
val dataFile = dataFile(id1)
assertDataFilesAreTheSame(id1, id2)
delete(id2)
assertDeleted(id2)
assertRowAndFileExists(id1)
assertTrue(dataFile.exists())
delete(id1)
assertDeleted(id1)
assertFalse(dataFile.exists())
}
// Delete original after it was compressed
test {
val id1 = insertWithData(DATA_A)
compress(id1, DATA_A_COMPRESSED)
val id2 = insertWithData(DATA_A)
delete(id1)
assertDeleted(id1)
assertRowAndFileExists(id2)
assertSkipTransform(id2, true)
}
// Quotes are weak references and should not prevent us from deleting the file
test {
val id1 = insertWithData(DATA_A)
val id2 = insertQuote(id1)
val dataFile = dataFile(id1)
delete(id1)
assertDeleted(id1)
assertRowExists(id2)
assertFalse(dataFile.exists())
}
}
@Test
fun quotes() {
// Basic quote deduping
test {
val id1 = insertWithData(DATA_A)
val id2 = insertQuote(id1)
assertDataFilesAreTheSame(id1, id2)
assertDataHashStartMatches(id1, id2)
}
// Making sure remote fields carry
test {
val id1 = insertWithData(DATA_A)
val id2 = insertQuote(id1)
upload(id1)
assertDataFilesAreTheSame(id1, id2)
assertDataHashStartMatches(id1, id2)
assertDataHashEndMatches(id1, id2)
assertRemoteFieldsMatch(id1, id2)
}
// Making sure things work for quotes of videos, which have trickier transform properties
test {
val id1 = insertWithData(DATA_A, transformProperties = TransformProperties.forVideoTrim(1, 2))
compress(id1, DATA_A_COMPRESSED)
upload(id1)
val id2 = insertQuote(id1)
assertDataFilesAreTheSame(id1, id2)
assertDataHashEndMatches(id1, id2)
assertRemoteFieldsMatch(id1, id2)
}
}
/**
* Suite of tests around the migration where we hash all of the attachments and potentially dedupe them.
*/
@Test
fun migration() {
// Verifying that getUnhashedDataFile only returns if there's actually missing hashes
test {
val id = insertWithData(DATA_A)
upload(id)
assertNull(SignalDatabase.attachments.getUnhashedDataFile())
}
// Verifying that getUnhashedDataFile finds the missing hash
test {
val id = insertWithData(DATA_A)
upload(id)
clearHashes(id)
assertNotNull(SignalDatabase.attachments.getUnhashedDataFile())
}
// Verifying that getUnhashedDataFile doesn't return if the file isn't done downloading
test {
val id = insertWithData(DATA_A)
upload(id)
setTransferState(id, AttachmentTable.TRANSFER_PROGRESS_PENDING)
clearHashes(id)
assertNull(SignalDatabase.attachments.getUnhashedDataFile())
}
// If two attachments share the same file, when we backfill the hash, make sure both get their hashes set
test {
val id1 = insertWithData(DATA_A)
val id2 = insertWithData(DATA_A)
upload(id1)
upload(id2)
clearHashes(id1)
clearHashes(id2)
val file = dataFile(id1)
SignalDatabase.attachments.setHashForDataFile(file, DATA_A_HASH)
assertDataHashEnd(id1, DATA_A_HASH)
assertDataHashEndMatches(id1, id2)
}
// Creates a situation where two different attachments have the same data but wrote to different files, and verifies the migration dedupes it
test {
val id1 = insertWithData(DATA_A)
upload(id1)
clearHashes(id1)
val id2 = insertWithData(DATA_A)
upload(id2)
clearHashes(id2)
assertDataFilesAreDifferent(id1, id2)
val file1 = dataFile(id1)
SignalDatabase.attachments.setHashForDataFile(file1, DATA_A_HASH)
assertDataHashEnd(id1, DATA_A_HASH)
assertDataFilesAreDifferent(id1, id2)
val file2 = dataFile(id2)
SignalDatabase.attachments.setHashForDataFile(file2, DATA_A_HASH)
assertDataFilesAreTheSame(id1, id2)
assertDataHashEndMatches(id1, id2)
assertFalse(file2.exists())
}
// We've got three files now with the same data, with two of them sharing a file. We want to make sure *both* entries that share the same file get deduped.
test {
val id1 = insertWithData(DATA_A)
upload(id1)
clearHashes(id1)
val id2 = insertWithData(DATA_A)
val id3 = insertWithData(DATA_A)
upload(id2)
upload(id3)
clearHashes(id2)
clearHashes(id3)
assertDataFilesAreDifferent(id1, id2)
assertDataFilesAreTheSame(id2, id3)
val file1 = dataFile(id1)
SignalDatabase.attachments.setHashForDataFile(file1, DATA_A_HASH)
assertDataHashEnd(id1, DATA_A_HASH)
val file2 = dataFile(id2)
SignalDatabase.attachments.setHashForDataFile(file2, DATA_A_HASH)
assertDataFilesAreTheSame(id1, id2)
assertDataHashEndMatches(id1, id2)
assertDataHashEndMatches(id2, id3)
assertFalse(file2.exists())
}
// We don't want to mess with files that are still downloading, so this makes sure that even if data matches, we don't dedupe and don't delete the file
test {
val id1 = insertWithData(DATA_A)
upload(id1)
clearHashes(id1)
val id2 = insertWithData(DATA_A)
// *not* uploaded
clearHashes(id2)
assertDataFilesAreDifferent(id1, id2)
val file1 = dataFile(id1)
SignalDatabase.attachments.setHashForDataFile(file1, DATA_A_HASH)
assertDataHashEnd(id1, DATA_A_HASH)
val file2 = dataFile(id2)
SignalDatabase.attachments.setHashForDataFile(file2, DATA_A_HASH)
assertDataFilesAreDifferent(id1, id2)
assertTrue(file2.exists())
}
}
private class TestContext {
fun insertWithData(data: ByteArray, transformProperties: TransformProperties = TransformProperties.empty()): AttachmentId {
val uri = BlobProvider.getInstance().forData(data).createForSingleSessionInMemory()
val attachment = UriAttachmentBuilder.build(
id = Random.nextLong(),
uri = uri,
contentType = MediaUtil.IMAGE_JPEG,
transformProperties = transformProperties
)
return SignalDatabase.attachments.insertAttachmentForPreUpload(attachment).attachmentId
}
fun insertQuote(attachmentId: AttachmentId): AttachmentId {
val originalAttachment = SignalDatabase.attachments.getAttachment(attachmentId)!!
val threadId = SignalDatabase.threads.getOrCreateThreadIdFor(Recipient.self())
val messageId = SignalDatabase.messages.insertMessageOutbox(
message = OutgoingMessage(
threadRecipient = Recipient.self(),
sentTimeMillis = System.currentTimeMillis(),
body = "some text",
outgoingQuote = QuoteModel(
id = 123,
author = Recipient.self().id,
text = "Some quote text",
isOriginalMissing = false,
attachments = listOf(originalAttachment),
mentions = emptyList(),
type = QuoteModel.Type.NORMAL,
bodyRanges = null
)
),
threadId = threadId,
forceSms = false,
insertListener = null
)
val attachments = SignalDatabase.attachments.getAttachmentsForMessage(messageId)
return attachments[0].attachmentId
}
fun compress(attachmentId: AttachmentId, newData: ByteArray, mp4FastStart: Boolean = false) {
val databaseAttachment = SignalDatabase.attachments.getAttachment(attachmentId)!!
SignalDatabase.attachments.updateAttachmentData(databaseAttachment, newData.asMediaStream())
SignalDatabase.attachments.markAttachmentAsTransformed(attachmentId, withFastStart = mp4FastStart)
}
fun upload(attachmentId: AttachmentId, uploadTimestamp: Long = System.currentTimeMillis()) {
SignalDatabase.attachments.finalizeAttachmentAfterUpload(attachmentId, createPointerAttachment(attachmentId, uploadTimestamp), uploadTimestamp)
}
fun delete(attachmentId: AttachmentId) {
SignalDatabase.attachments.deleteAttachment(attachmentId)
}
fun dataFile(attachmentId: AttachmentId): File {
return SignalDatabase.attachments.getDataFileInfo(attachmentId)!!.file
}
fun setTransferState(attachmentId: AttachmentId, transferState: Int) {
// messageId doesn't actually matter -- that's for notifying listeners
SignalDatabase.attachments.setTransferState(messageId = -1, attachmentId = attachmentId, transferState = transferState)
}
fun clearHashes(id: AttachmentId) {
SignalDatabase.attachments.writableDatabase
.update(AttachmentTable.TABLE_NAME)
.values(
AttachmentTable.DATA_HASH_START to null,
AttachmentTable.DATA_HASH_END to null
)
.where("${AttachmentTable.ID} = ?", id)
.run()
}
fun assertDeleted(attachmentId: AttachmentId) {
assertNull("$attachmentId exists, but it shouldn't!", SignalDatabase.attachments.getAttachment(attachmentId))
}
fun assertRowAndFileExists(attachmentId: AttachmentId) {
val databaseAttachment = SignalDatabase.attachments.getAttachment(attachmentId)
assertNotNull("$attachmentId does not exist!", databaseAttachment)
val dataFileInfo = SignalDatabase.attachments.getDataFileInfo(attachmentId)
assertTrue("The file for $attachmentId does not exist!", dataFileInfo!!.file.exists())
}
fun assertRowExists(attachmentId: AttachmentId) {
val databaseAttachment = SignalDatabase.attachments.getAttachment(attachmentId)
assertNotNull("$attachmentId does not exist!", databaseAttachment)
}
fun assertDataFilesAreTheSame(lhs: AttachmentId, rhs: AttachmentId) {
val lhsInfo = SignalDatabase.attachments.getDataFileInfo(lhs)!!
val rhsInfo = SignalDatabase.attachments.getDataFileInfo(rhs)!!
assert(lhsInfo.file.exists())
assert(rhsInfo.file.exists())
assertEquals(lhsInfo.file, rhsInfo.file)
assertEquals(lhsInfo.length, rhsInfo.length)
assertArrayEquals(lhsInfo.random, rhsInfo.random)
}
fun assertDataFilesAreDifferent(lhs: AttachmentId, rhs: AttachmentId) {
val lhsInfo = SignalDatabase.attachments.getDataFileInfo(lhs)!!
val rhsInfo = SignalDatabase.attachments.getDataFileInfo(rhs)!!
assert(lhsInfo.file.exists())
assert(rhsInfo.file.exists())
assertNotEquals(lhsInfo.file, rhsInfo.file)
}
fun assertDataHashStartMatches(lhs: AttachmentId, rhs: AttachmentId) {
val lhsInfo = SignalDatabase.attachments.getDataFileInfo(lhs)!!
val rhsInfo = SignalDatabase.attachments.getDataFileInfo(rhs)!!
assertNotNull(lhsInfo.hashStart)
assertEquals("DATA_HASH_START's did not match!", lhsInfo.hashStart, rhsInfo.hashStart)
}
fun assertDataHashEndMatches(lhs: AttachmentId, rhs: AttachmentId) {
val lhsInfo = SignalDatabase.attachments.getDataFileInfo(lhs)!!
val rhsInfo = SignalDatabase.attachments.getDataFileInfo(rhs)!!
assertNotNull(lhsInfo.hashEnd)
assertEquals("DATA_HASH_END's did not match!", lhsInfo.hashEnd, rhsInfo.hashEnd)
}
fun assertDataHashEnd(id: AttachmentId, byteArray: ByteArray) {
val dataFileInfo = SignalDatabase.attachments.getDataFileInfo(id)!!
assertArrayEquals(byteArray, Base64.decode(dataFileInfo.hashEnd!!))
}
fun assertRemoteFieldsMatch(lhs: AttachmentId, rhs: AttachmentId) {
val lhsAttachment = SignalDatabase.attachments.getAttachment(lhs)!!
val rhsAttachment = SignalDatabase.attachments.getAttachment(rhs)!!
assertEquals(lhsAttachment.remoteLocation, rhsAttachment.remoteLocation)
assertEquals(lhsAttachment.remoteKey, rhsAttachment.remoteKey)
assertArrayEquals(lhsAttachment.remoteDigest, rhsAttachment.remoteDigest)
assertArrayEquals(lhsAttachment.incrementalDigest, rhsAttachment.incrementalDigest)
assertEquals(lhsAttachment.incrementalMacChunkSize, rhsAttachment.incrementalMacChunkSize)
assertEquals(lhsAttachment.cdnNumber, rhsAttachment.cdnNumber)
}
fun assertDoesNotHaveRemoteFields(attachmentId: AttachmentId) {
val databaseAttachment = SignalDatabase.attachments.getAttachment(attachmentId)!!
assertEquals(0, databaseAttachment.uploadTimestamp)
assertNull(databaseAttachment.remoteLocation)
assertNull(databaseAttachment.remoteDigest)
assertNull(databaseAttachment.remoteKey)
assertEquals(0, databaseAttachment.cdnNumber)
}
fun assertSkipTransform(attachmentId: AttachmentId, state: Boolean) {
val transformProperties = SignalDatabase.attachments.getTransformProperties(attachmentId)!!
assertEquals("Incorrect skipTransform!", transformProperties.skipTransform, state)
}
private fun ByteArray.asMediaStream(): MediaStream {
return MediaStream(this.inputStream(), MediaUtil.IMAGE_JPEG, 2, 2)
}
private fun createPointerAttachment(attachmentId: AttachmentId, uploadTimestamp: Long = System.currentTimeMillis()): PointerAttachment {
val location = "somewhere-${Random.nextLong()}"
val key = "somekey-${Random.nextLong()}"
val digest = Random.nextBytes(32)
val incrementalDigest = Random.nextBytes(16)
val databaseAttachment = SignalDatabase.attachments.getAttachment(attachmentId)!!
return PointerAttachment(
"image/jpeg",
AttachmentTable.TRANSFER_PROGRESS_DONE,
databaseAttachment.size, // size
null,
3, // cdnNumber
location,
key,
digest,
incrementalDigest,
5, // incrementalMacChunkSize
null,
databaseAttachment.voiceNote,
databaseAttachment.borderless,
databaseAttachment.videoGif,
databaseAttachment.width,
databaseAttachment.height,
uploadTimestamp,
databaseAttachment.caption,
databaseAttachment.stickerLocator,
databaseAttachment.blurHash
)
}
}
private fun test(content: TestContext.() -> Unit) {
SignalDatabase.attachments.deleteAllAttachments()
val context = TestContext()
context.content()
}
}

View File

@@ -0,0 +1,102 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.database
import androidx.test.ext.junit.runners.AndroidJUnit4
import junit.framework.Assert.assertEquals
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.thoughtcrime.securesms.calls.log.CallLogFilter
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.service.webrtc.links.CallLinkCredentials
import org.thoughtcrime.securesms.service.webrtc.links.CallLinkRoomId
import org.thoughtcrime.securesms.service.webrtc.links.SignalCallLinkState
import org.thoughtcrime.securesms.testing.SignalActivityRule
@RunWith(AndroidJUnit4::class)
class CallLinkTableTest {
companion object {
private val ROOM_ID_A = byteArrayOf(1, 2, 3, 4)
private val ROOM_ID_B = byteArrayOf(2, 2, 3, 4)
private const val TIMESTAMP_A = 1000L
private const val TIMESTAMP_B = 2000L
}
@get:Rule
val harness = SignalActivityRule(createGroup = true)
@Test
fun givenTwoNonAdminCallLinks_whenIDeleteBeforeFirst_thenIExpectNeitherDeleted() {
insertTwoNonAdminCallLinksWithEvents()
SignalDatabase.callLinks.deleteNonAdminCallLinksOnOrBefore(TIMESTAMP_A - 500)
val callEvents = SignalDatabase.calls.getCalls(0, 2, "", CallLogFilter.ALL)
assertEquals(2, callEvents.size)
}
@Test
fun givenTwoNonAdminCallLinks_whenIDeleteOnFirst_thenIExpectFirstDeleted() {
insertTwoNonAdminCallLinksWithEvents()
SignalDatabase.callLinks.deleteNonAdminCallLinksOnOrBefore(TIMESTAMP_A)
val callEvents = SignalDatabase.calls.getCalls(0, 2, "", CallLogFilter.ALL)
assertEquals(1, callEvents.size)
assertEquals(TIMESTAMP_B, callEvents.first().record.timestamp)
}
@Test
fun givenTwoNonAdminCallLinks_whenIDeleteAfterFirstAndBeforeSecond_thenIExpectFirstDeleted() {
insertTwoNonAdminCallLinksWithEvents()
SignalDatabase.callLinks.deleteNonAdminCallLinksOnOrBefore(TIMESTAMP_B - 500)
val callEvents = SignalDatabase.calls.getCalls(0, 2, "", CallLogFilter.ALL)
assertEquals(1, callEvents.size)
assertEquals(TIMESTAMP_B, callEvents.first().record.timestamp)
}
@Test
fun givenTwoNonAdminCallLinks_whenIDeleteOnSecond_thenIExpectBothDeleted() {
insertTwoNonAdminCallLinksWithEvents()
SignalDatabase.callLinks.deleteNonAdminCallLinksOnOrBefore(TIMESTAMP_B)
val callEvents = SignalDatabase.calls.getCalls(0, 2, "", CallLogFilter.ALL)
assertEquals(0, callEvents.size)
}
@Test
fun givenTwoNonAdminCallLinks_whenIDeleteAfterSecond_thenIExpectBothDeleted() {
insertTwoNonAdminCallLinksWithEvents()
SignalDatabase.callLinks.deleteNonAdminCallLinksOnOrBefore(TIMESTAMP_B + 500)
val callEvents = SignalDatabase.calls.getCalls(0, 2, "", CallLogFilter.ALL)
assertEquals(0, callEvents.size)
}
private fun insertTwoNonAdminCallLinksWithEvents() {
insertCallLinkWithEvent(ROOM_ID_A, 1000)
insertCallLinkWithEvent(ROOM_ID_B, 2000)
}
private fun insertCallLinkWithEvent(roomId: ByteArray, timestamp: Long) {
SignalDatabase.callLinks.insertCallLink(
CallLinkTable.CallLink(
recipientId = RecipientId.UNKNOWN,
roomId = CallLinkRoomId.fromBytes(roomId),
credentials = CallLinkCredentials(
linkKeyBytes = roomId,
adminPassBytes = null
),
state = SignalCallLinkState()
)
)
val callLinkRecipient = SignalDatabase.recipients.getByCallLinkRoomId(CallLinkRoomId.fromBytes(roomId)).get()
SignalDatabase.calls.insertAcceptedGroupCall(
1,
callLinkRecipient,
CallTable.Direction.INCOMING,
timestamp
)
}
}

View File

@@ -0,0 +1,994 @@
package org.thoughtcrime.securesms.database
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.signal.ringrtc.CallId
import org.signal.ringrtc.CallManager
import org.thoughtcrime.securesms.calls.log.CallLogFilter
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.testing.SignalActivityRule
@RunWith(AndroidJUnit4::class)
class CallTableTest {
@get:Rule
val harness = SignalActivityRule(createGroup = true)
private val groupRecipientId: RecipientId
get() = harness.group!!.recipientId
@Test
fun givenACall_whenISetTimestamp_thenIExpectUpdatedTimestamp() {
val callId = 1L
val now = System.currentTimeMillis()
SignalDatabase.calls.insertAcceptedGroupCall(
callId,
groupRecipientId,
CallTable.Direction.INCOMING,
now
)
SignalDatabase.calls.setTimestamp(callId, groupRecipientId, -1L)
val call = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertNotNull(call)
assertEquals(-1L, call?.timestamp)
val messageRecord = SignalDatabase.messages.getMessageRecord(call!!.messageId!!)
assertEquals(-1L, messageRecord.dateReceived)
assertEquals(-1L, messageRecord.dateSent)
}
@Test
fun givenPreExistingEvent_whenIDeleteGroupCall_thenIMarkDeletedAndSetTimestamp() {
val callId = 1L
val now = System.currentTimeMillis()
SignalDatabase.calls.insertAcceptedGroupCall(
callId,
groupRecipientId,
CallTable.Direction.INCOMING,
now
)
val call = SignalDatabase.calls.getCallById(callId, groupRecipientId)
SignalDatabase.calls.deleteGroupCall(call!!)
val deletedCall = SignalDatabase.calls.getCallById(callId, groupRecipientId)
val oldestDeletionTimestamp = SignalDatabase.calls.getOldestDeletionTimestamp()
assertEquals(CallTable.Event.DELETE, deletedCall?.event)
assertNotEquals(0L, oldestDeletionTimestamp)
assertNull(deletedCall!!.messageId)
}
@Test
fun givenNoPreExistingEvent_whenIDeleteGroupCall_thenIInsertAndMarkCallDeleted() {
val callId = 1L
SignalDatabase.calls.insertDeletedGroupCallFromSyncEvent(
callId,
groupRecipientId,
CallTable.Direction.OUTGOING,
System.currentTimeMillis()
)
val call = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertNotNull(call)
val oldestDeletionTimestamp = SignalDatabase.calls.getOldestDeletionTimestamp()
assertEquals(CallTable.Event.DELETE, call?.event)
assertNotEquals(oldestDeletionTimestamp, 0)
assertNull(call?.messageId)
}
@Test
fun givenNoPriorEvent_whenIInsertAcceptedOutgoingGroupCall_thenIExpectLocalRingerAndOutgoingRing() {
val callId = 1L
SignalDatabase.calls.insertAcceptedGroupCall(
callId,
groupRecipientId,
CallTable.Direction.OUTGOING,
System.currentTimeMillis()
)
val call = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertNotNull(call)
assertEquals(CallTable.Event.OUTGOING_RING, call?.event)
assertEquals(harness.self.id, call?.ringerRecipient)
assertNotNull(call?.messageId)
}
@Test
fun givenNoPriorEvent_whenIInsertAcceptedIncomingGroupCall_thenIExpectJoined() {
val callId = 1L
SignalDatabase.calls.insertAcceptedGroupCall(
callId,
groupRecipientId,
CallTable.Direction.INCOMING,
System.currentTimeMillis()
)
val call = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertNotNull(call)
assertEquals(CallTable.Event.JOINED, call?.event)
assertNull(call?.ringerRecipient)
assertNotNull(call?.messageId)
}
@Test
fun givenARingingCall_whenIAcceptedIncomingGroupCall_thenIExpectAccepted() {
val callId = 1L
SignalDatabase.calls.insertOrUpdateGroupCallFromRingState(
ringId = callId,
groupRecipientId = groupRecipientId,
ringerRecipient = harness.others[1],
dateReceived = System.currentTimeMillis(),
ringState = CallManager.RingUpdate.REQUESTED
)
val call = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertNotNull(call)
assertEquals(CallTable.Event.RINGING, call?.event)
SignalDatabase.calls.acceptIncomingGroupCall(
call!!
)
val acceptedCall = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertEquals(CallTable.Event.ACCEPTED, acceptedCall?.event)
}
@Test
fun givenAMissedCall_whenIAcceptedIncomingGroupCall_thenIExpectAccepted() {
val callId = 1L
SignalDatabase.calls.insertOrUpdateGroupCallFromRingState(
ringId = callId,
groupRecipientId = groupRecipientId,
ringerRecipient = harness.others[1],
dateReceived = System.currentTimeMillis(),
ringState = CallManager.RingUpdate.EXPIRED_REQUEST
)
val call = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertNotNull(call)
assertEquals(CallTable.Event.MISSED, call?.event)
SignalDatabase.calls.acceptIncomingGroupCall(
call!!
)
val acceptedCall = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertEquals(CallTable.Event.ACCEPTED, acceptedCall?.event)
}
@Test
fun givenADeclinedCall_whenIAcceptedIncomingGroupCall_thenIExpectAccepted() {
val callId = 1L
SignalDatabase.calls.insertOrUpdateGroupCallFromRingState(
ringId = callId,
groupRecipientId = groupRecipientId,
ringerRecipient = harness.others[1],
dateReceived = System.currentTimeMillis(),
ringState = CallManager.RingUpdate.DECLINED_ON_ANOTHER_DEVICE
)
val call = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertNotNull(call)
assertEquals(CallTable.Event.DECLINED, call?.event)
SignalDatabase.calls.acceptIncomingGroupCall(
call!!
)
val acceptedCall = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertEquals(CallTable.Event.ACCEPTED, acceptedCall?.event)
}
@Test
fun givenAGenericGroupCall_whenIAcceptedIncomingGroupCall_thenIExpectAccepted() {
val era = "aaa"
val callId = CallId.fromEra(era).longValue()
SignalDatabase.calls.insertOrUpdateGroupCallFromLocalEvent(
groupRecipientId = groupRecipientId,
sender = harness.others[1],
timestamp = System.currentTimeMillis(),
peekGroupCallEraId = "aaa",
peekJoinedUuids = emptyList(),
isCallFull = false
)
val call = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertNotNull(call)
assertEquals(CallTable.Event.GENERIC_GROUP_CALL, call?.event)
SignalDatabase.calls.acceptIncomingGroupCall(
call!!
)
val acceptedCall = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertEquals(CallTable.Event.JOINED, acceptedCall?.event)
}
@Test
fun givenAnOutgoingRingCall_whenIAcceptedOutgoingGroupCall_thenIExpectOutgoingRing() {
val callId = 1L
SignalDatabase.calls.insertAcceptedGroupCall(
callId = callId,
recipientId = groupRecipientId,
direction = CallTable.Direction.OUTGOING,
timestamp = 1
)
val call = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertNotNull(call)
assertEquals(CallTable.Event.OUTGOING_RING, call?.event)
SignalDatabase.calls.acceptOutgoingGroupCall(
call!!
)
val acceptedCall = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertEquals(CallTable.Event.OUTGOING_RING, acceptedCall?.event)
}
@Test
fun givenARingingCall_whenIAcceptedOutgoingGroupCall_thenIExpectAccepted() {
val callId = 1L
SignalDatabase.calls.insertOrUpdateGroupCallFromRingState(
ringId = callId,
groupRecipientId = groupRecipientId,
ringerRecipient = harness.others[1],
dateReceived = System.currentTimeMillis(),
ringState = CallManager.RingUpdate.REQUESTED
)
val call = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertNotNull(call)
assertEquals(CallTable.Event.RINGING, call?.event)
SignalDatabase.calls.acceptOutgoingGroupCall(
call!!
)
val acceptedCall = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertEquals(CallTable.Event.ACCEPTED, acceptedCall?.event)
}
@Test
fun givenAMissedCall_whenIAcceptedOutgoingGroupCall_thenIExpectAccepted() {
val callId = 1L
SignalDatabase.calls.insertOrUpdateGroupCallFromRingState(
ringId = callId,
groupRecipientId = groupRecipientId,
ringerRecipient = harness.others[1],
dateReceived = System.currentTimeMillis(),
ringState = CallManager.RingUpdate.EXPIRED_REQUEST
)
val call = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertNotNull(call)
assertEquals(CallTable.Event.MISSED, call?.event)
SignalDatabase.calls.acceptOutgoingGroupCall(
call!!
)
val acceptedCall = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertEquals(CallTable.Event.ACCEPTED, acceptedCall?.event)
}
@Test
fun givenADeclinedCall_whenIAcceptedOutgoingGroupCall_thenIExpectAccepted() {
val callId = 1L
SignalDatabase.calls.insertOrUpdateGroupCallFromRingState(
ringId = callId,
groupRecipientId = groupRecipientId,
ringerRecipient = harness.others[1],
dateReceived = System.currentTimeMillis(),
ringState = CallManager.RingUpdate.DECLINED_ON_ANOTHER_DEVICE
)
val call = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertNotNull(call)
assertEquals(CallTable.Event.DECLINED, call?.event)
SignalDatabase.calls.acceptOutgoingGroupCall(
call!!
)
val acceptedCall = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertEquals(CallTable.Event.ACCEPTED, acceptedCall?.event)
}
@Test
fun givenAnAcceptedCall_whenIAcceptedOutgoingGroupCall_thenIExpectAccepted() {
val callId = 1L
SignalDatabase.calls.insertOrUpdateGroupCallFromRingState(
ringId = callId,
groupRecipientId = groupRecipientId,
ringerRecipient = harness.others[1],
dateReceived = System.currentTimeMillis(),
ringState = CallManager.RingUpdate.REQUESTED
)
val call = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertNotNull(call)
assertEquals(CallTable.Event.RINGING, call?.event)
SignalDatabase.calls.acceptIncomingGroupCall(
call!!
)
SignalDatabase.calls.acceptOutgoingGroupCall(
SignalDatabase.calls.getCallById(callId, groupRecipientId)!!
)
val acceptedCall = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertEquals(CallTable.Event.ACCEPTED, acceptedCall?.event)
}
@Test
fun givenAGenericGroupCall_whenIAcceptedOutgoingGroupCall_thenIExpectOutgoingRing() {
val era = "aaa"
val callId = CallId.fromEra(era).longValue()
SignalDatabase.calls.insertOrUpdateGroupCallFromLocalEvent(
groupRecipientId = groupRecipientId,
sender = harness.others[1],
timestamp = System.currentTimeMillis(),
peekGroupCallEraId = "aaa",
peekJoinedUuids = emptyList(),
isCallFull = false
)
val call = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertNotNull(call)
assertEquals(CallTable.Event.GENERIC_GROUP_CALL, call?.event)
SignalDatabase.calls.acceptOutgoingGroupCall(
call!!
)
val acceptedCall = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertEquals(CallTable.Event.OUTGOING_RING, acceptedCall?.event)
}
@Test
fun givenAJoinedGroupCall_whenIAcceptedOutgoingGroupCall_thenIExpectOutgoingRing() {
val era = "aaa"
val callId = CallId.fromEra(era).longValue()
SignalDatabase.calls.insertOrUpdateGroupCallFromLocalEvent(
groupRecipientId = groupRecipientId,
sender = harness.others[1],
timestamp = System.currentTimeMillis(),
peekGroupCallEraId = "aaa",
peekJoinedUuids = emptyList(),
isCallFull = false
)
val call = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertNotNull(call)
assertEquals(CallTable.Event.GENERIC_GROUP_CALL, call?.event)
SignalDatabase.calls.acceptIncomingGroupCall(
call!!
)
SignalDatabase.calls.acceptOutgoingGroupCall(SignalDatabase.calls.getCallById(callId, groupRecipientId)!!)
val acceptedCall = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertEquals(CallTable.Event.OUTGOING_RING, acceptedCall?.event)
}
@Test
fun givenNoPriorCallEvent_whenIReceiveAGroupCallUpdateMessage_thenIExpectAGenericGroupCall() {
val era = "aaa"
val callId = CallId.fromEra(era).longValue()
SignalDatabase.calls.insertOrUpdateGroupCallFromLocalEvent(
groupRecipientId = groupRecipientId,
sender = harness.others[1],
timestamp = System.currentTimeMillis(),
peekGroupCallEraId = "aaa",
peekJoinedUuids = emptyList(),
isCallFull = false
)
val call = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertNotNull(call)
assertEquals(CallTable.Event.GENERIC_GROUP_CALL, call?.event)
}
@Test
fun givenAPriorCallEventWithNewerTimestamp_whenIReceiveAGroupCallUpdateMessage_thenIExpectAnUpdatedTimestamp() {
val era = "aaa"
val callId = CallId.fromEra(era).longValue()
val now = System.currentTimeMillis()
SignalDatabase.calls.insertOrUpdateGroupCallFromLocalEvent(
groupRecipientId = groupRecipientId,
sender = harness.others[1],
timestamp = now,
peekGroupCallEraId = "aaa",
peekJoinedUuids = emptyList(),
isCallFull = false
)
SignalDatabase.calls.getCallById(callId, groupRecipientId).let {
assertNotNull(it)
assertEquals(now, it?.timestamp)
}
SignalDatabase.calls.insertOrUpdateGroupCallFromLocalEvent(
groupRecipientId = groupRecipientId,
sender = harness.others[1],
timestamp = 1L,
peekGroupCallEraId = "aaa",
peekJoinedUuids = emptyList(),
isCallFull = false
)
val call = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertNotNull(call)
assertEquals(CallTable.Event.GENERIC_GROUP_CALL, call?.event)
assertEquals(1L, call?.timestamp)
}
@Test
fun givenADeletedCallEvent_whenIReceiveARingUpdate_thenIIgnoreTheRingUpdate() {
val callId = 1L
SignalDatabase.calls.insertDeletedGroupCallFromSyncEvent(
callId = callId,
recipientId = groupRecipientId,
direction = CallTable.Direction.INCOMING,
timestamp = System.currentTimeMillis()
)
SignalDatabase.calls.insertOrUpdateGroupCallFromRingState(
ringId = callId,
groupRecipientId = groupRecipientId,
ringerRecipient = harness.others[1],
dateReceived = System.currentTimeMillis(),
ringState = CallManager.RingUpdate.REQUESTED
)
val call = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertNotNull(call)
assertEquals(CallTable.Event.DELETE, call?.event)
}
@Test
fun givenAGenericCallEvent_whenRingRequested_thenISetRingerAndMoveToRingingState() {
val era = "aaa"
val callId = CallId.fromEra(era).longValue()
val now = System.currentTimeMillis()
SignalDatabase.calls.insertOrUpdateGroupCallFromLocalEvent(
groupRecipientId = groupRecipientId,
sender = harness.others[1],
timestamp = now,
peekGroupCallEraId = "aaa",
peekJoinedUuids = emptyList(),
isCallFull = false
)
SignalDatabase.calls.insertOrUpdateGroupCallFromRingState(
callId,
groupRecipientId,
harness.others[1],
System.currentTimeMillis(),
CallManager.RingUpdate.REQUESTED
)
val call = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertNotNull(call)
assertEquals(CallTable.Event.RINGING, call?.event)
assertEquals(harness.others[1], call?.ringerRecipient)
}
@Test
fun givenAJoinedCallEvent_whenRingRequested_thenISetRingerAndMoveToRingingState() {
val era = "aaa"
val callId = CallId.fromEra(era).longValue()
val now = System.currentTimeMillis()
SignalDatabase.calls.insertAcceptedGroupCall(
callId,
groupRecipientId,
CallTable.Direction.INCOMING,
now
)
SignalDatabase.calls.insertOrUpdateGroupCallFromRingState(
callId,
groupRecipientId,
harness.others[1],
System.currentTimeMillis(),
CallManager.RingUpdate.REQUESTED
)
val call = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertNotNull(call)
assertEquals(CallTable.Event.ACCEPTED, call?.event)
assertEquals(harness.others[1], call?.ringerRecipient)
}
@Test
fun givenAGenericCallEvent_whenRingExpired_thenISetRingerAndMoveToMissedState() {
val era = "aaa"
val callId = CallId.fromEra(era).longValue()
val now = System.currentTimeMillis()
SignalDatabase.calls.insertOrUpdateGroupCallFromLocalEvent(
groupRecipientId = groupRecipientId,
sender = harness.others[1],
timestamp = now,
peekGroupCallEraId = "aaa",
peekJoinedUuids = emptyList(),
isCallFull = false
)
SignalDatabase.calls.insertOrUpdateGroupCallFromRingState(
callId,
groupRecipientId,
harness.others[1],
System.currentTimeMillis(),
CallManager.RingUpdate.EXPIRED_REQUEST
)
val call = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertNotNull(call)
assertEquals(CallTable.Event.MISSED, call?.event)
assertEquals(harness.others[1], call?.ringerRecipient)
}
@Test
fun givenARingingCallEvent_whenRingExpired_thenISetRingerAndMoveToMissedState() {
val era = "aaa"
val callId = CallId.fromEra(era).longValue()
val now = System.currentTimeMillis()
SignalDatabase.calls.insertOrUpdateGroupCallFromLocalEvent(
groupRecipientId = groupRecipientId,
sender = harness.others[1],
timestamp = now,
peekGroupCallEraId = "aaa",
peekJoinedUuids = emptyList(),
isCallFull = false
)
SignalDatabase.calls.insertOrUpdateGroupCallFromRingState(
callId,
groupRecipientId,
harness.others[1],
System.currentTimeMillis(),
CallManager.RingUpdate.REQUESTED
)
SignalDatabase.calls.insertOrUpdateGroupCallFromRingState(
callId,
groupRecipientId,
harness.others[1],
System.currentTimeMillis(),
CallManager.RingUpdate.EXPIRED_REQUEST
)
val call = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertNotNull(call)
assertEquals(CallTable.Event.MISSED, call?.event)
assertEquals(harness.others[1], call?.ringerRecipient)
}
@Test
fun givenAJoinedCallEvent_whenRingIsCancelledBecauseUserIsBusyLocally_thenIMoveToAcceptedState() {
val era = "aaa"
val callId = CallId.fromEra(era).longValue()
val now = System.currentTimeMillis()
SignalDatabase.calls.insertAcceptedGroupCall(
callId,
groupRecipientId,
CallTable.Direction.INCOMING,
now
)
SignalDatabase.calls.insertOrUpdateGroupCallFromRingState(
callId,
groupRecipientId,
harness.others[1],
System.currentTimeMillis(),
CallManager.RingUpdate.BUSY_LOCALLY
)
val call = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertNotNull(call)
assertEquals(CallTable.Event.ACCEPTED, call?.event)
}
@Test
fun givenAJoinedCallEvent_whenRingIsCancelledBecauseUserIsBusyOnAnotherDevice_thenIMoveToAcceptedState() {
val era = "aaa"
val callId = CallId.fromEra(era).longValue()
val now = System.currentTimeMillis()
SignalDatabase.calls.insertAcceptedGroupCall(
callId,
groupRecipientId,
CallTable.Direction.INCOMING,
now
)
SignalDatabase.calls.insertOrUpdateGroupCallFromRingState(
callId,
groupRecipientId,
harness.others[1],
System.currentTimeMillis(),
CallManager.RingUpdate.BUSY_ON_ANOTHER_DEVICE
)
val call = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertNotNull(call)
assertEquals(CallTable.Event.ACCEPTED, call?.event)
}
@Test
fun givenARingingCallEvent_whenRingCancelledBecauseUserIsBusyLocally_thenIMoveToMissedState() {
val era = "aaa"
val callId = CallId.fromEra(era).longValue()
val now = System.currentTimeMillis()
SignalDatabase.calls.insertOrUpdateGroupCallFromLocalEvent(
groupRecipientId = groupRecipientId,
sender = harness.others[1],
timestamp = now,
peekGroupCallEraId = "aaa",
peekJoinedUuids = emptyList(),
isCallFull = false
)
SignalDatabase.calls.insertOrUpdateGroupCallFromRingState(
callId,
groupRecipientId,
harness.others[1],
System.currentTimeMillis(),
CallManager.RingUpdate.REQUESTED
)
SignalDatabase.calls.insertOrUpdateGroupCallFromRingState(
callId,
groupRecipientId,
harness.others[1],
System.currentTimeMillis(),
CallManager.RingUpdate.BUSY_LOCALLY
)
val call = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertNotNull(call)
assertEquals(CallTable.Event.MISSED, call?.event)
}
@Test
fun givenARingingCallEvent_whenRingCancelledBecauseUserIsBusyOnAnotherDevice_thenIMoveToMissedState() {
val era = "aaa"
val callId = CallId.fromEra(era).longValue()
val now = System.currentTimeMillis()
SignalDatabase.calls.insertOrUpdateGroupCallFromLocalEvent(
groupRecipientId = groupRecipientId,
sender = harness.others[1],
timestamp = now,
peekGroupCallEraId = "aaa",
peekJoinedUuids = emptyList(),
isCallFull = false
)
SignalDatabase.calls.insertOrUpdateGroupCallFromRingState(
callId,
groupRecipientId,
harness.others[1],
System.currentTimeMillis(),
CallManager.RingUpdate.REQUESTED
)
SignalDatabase.calls.insertOrUpdateGroupCallFromRingState(
callId,
groupRecipientId,
harness.others[1],
System.currentTimeMillis(),
CallManager.RingUpdate.BUSY_ON_ANOTHER_DEVICE
)
val call = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertNotNull(call)
assertEquals(CallTable.Event.MISSED, call?.event)
}
@Test
fun givenACallEvent_whenRingIsAcceptedOnAnotherDevice_thenIMoveToAcceptedState() {
val era = "aaa"
val callId = CallId.fromEra(era).longValue()
val now = System.currentTimeMillis()
SignalDatabase.calls.insertOrUpdateGroupCallFromLocalEvent(
groupRecipientId = groupRecipientId,
sender = harness.others[1],
timestamp = now,
peekGroupCallEraId = "aaa",
peekJoinedUuids = emptyList(),
isCallFull = false
)
SignalDatabase.calls.insertOrUpdateGroupCallFromRingState(
callId,
groupRecipientId,
harness.others[1],
System.currentTimeMillis(),
CallManager.RingUpdate.ACCEPTED_ON_ANOTHER_DEVICE
)
val call = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertNotNull(call)
assertEquals(CallTable.Event.ACCEPTED, call?.event)
}
@Test
fun givenARingingCallEvent_whenRingDeclinedOnAnotherDevice_thenIMoveToDeclinedState() {
val era = "aaa"
val callId = CallId.fromEra(era).longValue()
SignalDatabase.calls.insertOrUpdateGroupCallFromRingState(
callId,
groupRecipientId,
harness.others[1],
System.currentTimeMillis(),
CallManager.RingUpdate.REQUESTED
)
SignalDatabase.calls.insertOrUpdateGroupCallFromRingState(
callId,
groupRecipientId,
harness.others[1],
System.currentTimeMillis(),
CallManager.RingUpdate.DECLINED_ON_ANOTHER_DEVICE
)
val call = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertNotNull(call)
assertEquals(CallTable.Event.DECLINED, call?.event)
}
@Test
fun givenAMissedCallEvent_whenRingDeclinedOnAnotherDevice_thenIMoveToDeclinedState() {
val era = "aaa"
val callId = CallId.fromEra(era).longValue()
SignalDatabase.calls.insertOrUpdateGroupCallFromRingState(
callId,
groupRecipientId,
harness.others[1],
System.currentTimeMillis(),
CallManager.RingUpdate.EXPIRED_REQUEST
)
SignalDatabase.calls.insertOrUpdateGroupCallFromRingState(
callId,
groupRecipientId,
harness.others[1],
System.currentTimeMillis(),
CallManager.RingUpdate.DECLINED_ON_ANOTHER_DEVICE
)
val call = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertNotNull(call)
assertEquals(CallTable.Event.DECLINED, call?.event)
}
@Test
fun givenAnOutgoingRingCallEvent_whenRingDeclinedOnAnotherDevice_thenIDoNotChangeState() {
val era = "aaa"
val callId = CallId.fromEra(era).longValue()
SignalDatabase.calls.insertAcceptedGroupCall(
callId,
groupRecipientId,
CallTable.Direction.OUTGOING,
System.currentTimeMillis()
)
SignalDatabase.calls.insertOrUpdateGroupCallFromRingState(
callId,
groupRecipientId,
harness.others[1],
System.currentTimeMillis(),
CallManager.RingUpdate.DECLINED_ON_ANOTHER_DEVICE
)
val call = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertNotNull(call)
assertEquals(CallTable.Event.OUTGOING_RING, call?.event)
}
@Test
fun givenNoPriorEvent_whenRingRequested_thenICreateAnEventInTheRingingStateAndSetRinger() {
val callId = 1L
SignalDatabase.calls.insertOrUpdateGroupCallFromRingState(
callId,
groupRecipientId,
harness.others[1],
System.currentTimeMillis(),
CallManager.RingUpdate.REQUESTED
)
val call = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertNotNull(call)
assertEquals(CallTable.Event.RINGING, call?.event)
assertEquals(harness.others[1], call?.ringerRecipient)
assertNotNull(call?.messageId)
}
@Test
fun givenNoPriorEvent_whenRingExpired_thenICreateAnEventInTheMissedStateAndSetRinger() {
val callId = 1L
SignalDatabase.calls.insertOrUpdateGroupCallFromRingState(
callId,
groupRecipientId,
harness.others[1],
System.currentTimeMillis(),
CallManager.RingUpdate.EXPIRED_REQUEST
)
val call = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertNotNull(call)
assertEquals(CallTable.Event.MISSED, call?.event)
assertEquals(harness.others[1], call?.ringerRecipient)
assertNotNull(call?.messageId)
}
@Test
fun givenNoPriorEvent_whenRingCancelledByRinger_thenICreateAnEventInTheMissedStateAndSetRinger() {
val callId = 1L
SignalDatabase.calls.insertOrUpdateGroupCallFromRingState(
callId,
groupRecipientId,
harness.others[1],
System.currentTimeMillis(),
CallManager.RingUpdate.CANCELLED_BY_RINGER
)
val call = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertNotNull(call)
assertEquals(CallTable.Event.MISSED, call?.event)
assertEquals(harness.others[1], call?.ringerRecipient)
assertNotNull(call?.messageId)
}
@Test
fun givenNoPriorEvent_whenRingCancelledBecauseUserIsBusyLocally_thenICreateAnEventInTheMissedState() {
val callId = 1L
SignalDatabase.calls.insertOrUpdateGroupCallFromRingState(
callId,
groupRecipientId,
harness.others[1],
System.currentTimeMillis(),
CallManager.RingUpdate.BUSY_LOCALLY
)
val call = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertNotNull(call)
assertEquals(CallTable.Event.MISSED, call?.event)
assertNotNull(call?.messageId)
}
@Test
fun givenNoPriorEvent_whenRingCancelledBecauseUserIsBusyOnAnotherDevice_thenICreateAnEventInTheMissedState() {
val callId = 1L
SignalDatabase.calls.insertOrUpdateGroupCallFromRingState(
callId,
groupRecipientId,
harness.others[1],
System.currentTimeMillis(),
CallManager.RingUpdate.BUSY_ON_ANOTHER_DEVICE
)
val call = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertNotNull(call)
assertEquals(CallTable.Event.MISSED, call?.event)
assertNotNull(call?.messageId)
}
@Test
fun givenNoPriorEvent_whenRingAcceptedOnAnotherDevice_thenICreateAnEventInTheAcceptedState() {
val callId = 1L
SignalDatabase.calls.insertOrUpdateGroupCallFromRingState(
callId,
groupRecipientId,
harness.others[1],
System.currentTimeMillis(),
CallManager.RingUpdate.ACCEPTED_ON_ANOTHER_DEVICE
)
val call = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertNotNull(call)
assertEquals(CallTable.Event.ACCEPTED, call?.event)
assertNotNull(call?.messageId)
}
@Test
fun givenNoPriorEvent_whenRingDeclinedOnAnotherDevice_thenICreateAnEventInTheDeclinedState() {
val callId = 1L
SignalDatabase.calls.insertOrUpdateGroupCallFromRingState(
callId,
groupRecipientId,
harness.others[1],
System.currentTimeMillis(),
CallManager.RingUpdate.DECLINED_ON_ANOTHER_DEVICE
)
val call = SignalDatabase.calls.getCallById(callId, groupRecipientId)
assertNotNull(call)
assertEquals(CallTable.Event.DECLINED, call?.event)
assertNotNull(call?.messageId)
}
@Test
fun givenTwoCalls_whenIDeleteBeforeCallB_thenOnlyDeleteCallA() {
insertTwoCallEvents()
SignalDatabase.calls.deleteNonAdHocCallEventsOnOrBefore(1500)
val allCallEvents = SignalDatabase.calls.getCalls(0, 2, null, CallLogFilter.ALL)
assertEquals(1, allCallEvents.size)
assertEquals(2, allCallEvents.first().record.callId)
}
@Test
fun givenTwoCalls_whenIDeleteBeforeCallA_thenIDoNotDeleteAnyCalls() {
insertTwoCallEvents()
SignalDatabase.calls.deleteNonAdHocCallEventsOnOrBefore(500)
val allCallEvents = SignalDatabase.calls.getCalls(0, 2, null, CallLogFilter.ALL)
assertEquals(2, allCallEvents.size)
assertEquals(2, allCallEvents[0].record.callId)
assertEquals(1, allCallEvents[1].record.callId)
}
@Test
fun givenTwoCalls_whenIDeleteOnCallA_thenIOnlyDeleteCallA() {
insertTwoCallEvents()
SignalDatabase.calls.deleteNonAdHocCallEventsOnOrBefore(1000)
val allCallEvents = SignalDatabase.calls.getCalls(0, 2, null, CallLogFilter.ALL)
assertEquals(1, allCallEvents.size)
assertEquals(2, allCallEvents.first().record.callId)
}
@Test
fun givenTwoCalls_whenIDeleteOnCallB_thenIDeleteBothCalls() {
insertTwoCallEvents()
SignalDatabase.calls.deleteNonAdHocCallEventsOnOrBefore(2000)
val allCallEvents = SignalDatabase.calls.getCalls(0, 2, null, CallLogFilter.ALL)
assertEquals(0, allCallEvents.size)
}
@Test
fun givenTwoCalls_whenIDeleteAfterCallB_thenIDeleteBothCalls() {
insertTwoCallEvents()
SignalDatabase.calls.deleteNonAdHocCallEventsOnOrBefore(2500)
val allCallEvents = SignalDatabase.calls.getCalls(0, 2, null, CallLogFilter.ALL)
assertEquals(0, allCallEvents.size)
}
private fun insertTwoCallEvents() {
SignalDatabase.calls.insertAcceptedGroupCall(
1,
groupRecipientId,
CallTable.Direction.INCOMING,
1000
)
SignalDatabase.calls.insertAcceptedGroupCall(
2,
groupRecipientId,
CallTable.Direction.OUTGOING,
2000
)
}
}

View File

@@ -0,0 +1,539 @@
package org.thoughtcrime.securesms.database
import android.app.Application
import androidx.test.ext.junit.runners.AndroidJUnit4
import junit.framework.TestCase.assertTrue
import net.zetetic.database.sqlcipher.SQLiteDatabase
import net.zetetic.database.sqlcipher.SQLiteOpenHelper
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.signal.core.util.ForeignKeyConstraint
import org.signal.core.util.Index
import org.signal.core.util.getForeignKeys
import org.signal.core.util.getIndexes
import org.signal.core.util.readToList
import org.signal.core.util.requireNonNullString
import org.thoughtcrime.securesms.database.helpers.SignalDatabaseMigrations
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.testing.SignalActivityRule
/**
* A test that guarantees that a freshly-created database looks the same as one that went through the upgrade path.
*/
@RunWith(AndroidJUnit4::class)
class DatabaseConsistencyTest {
@get:Rule
val harness = SignalActivityRule()
@Test
fun testUpgradeConsistency() {
val currentVersionStatements = SignalDatabase.rawDatabase.getAllCreateStatements()
val testHelper = InMemoryTestHelper(ApplicationDependencies.getApplication()).also {
it.onUpgrade(it.writableDatabase, 181, SignalDatabaseMigrations.DATABASE_VERSION)
}
val upgradedStatements = testHelper.readableDatabase.getAllCreateStatements()
if (currentVersionStatements != upgradedStatements) {
var message = "\n"
val currentByName = currentVersionStatements.associateBy { it.name }
val upgradedByName = upgradedStatements.associateBy { it.name }
if (currentByName.keys != upgradedByName.keys) {
val exclusiveToCurrent = currentByName.keys - upgradedByName.keys
val exclusiveToUpgrade = upgradedByName.keys - currentByName.keys
message += "SQL entities exclusive to the newly-created database: $exclusiveToCurrent\n"
message += "SQL entities exclusive to the upgraded database: $exclusiveToUpgrade\n\n"
} else {
for (currentEntry in currentByName) {
val upgradedValue: Statement = upgradedByName[currentEntry.key]!!
if (upgradedValue.sql != currentEntry.value.sql) {
message += "Statement differed:\n"
message += "newly-created:\n"
message += "${currentEntry.value.sql}\n\n"
message += "upgraded:\n"
message += "${upgradedValue.sql}\n\n"
}
}
}
assertTrue(message, false)
}
}
@Test
fun testForeignKeyIndexCoverage() {
/** We may deem certain indexes non-critical if deletion frequency is low or table size is small. */
val ignoredColumns: List<Pair<String, String>> = listOf(
StorySendTable.TABLE_NAME to StorySendTable.DISTRIBUTION_ID
)
val foreignKeys: List<ForeignKeyConstraint> = SignalDatabase.rawDatabase.getForeignKeys()
val indexesByFirstColumn: List<Index> = SignalDatabase.rawDatabase.getIndexes()
val notFound: List<Pair<String, String>> = foreignKeys
.filterNot { ignoredColumns.contains(it.table to it.column) }
.filterNot { foreignKey ->
indexesByFirstColumn.hasPrimaryIndexFor(foreignKey.table, foreignKey.column)
}
.map { it.table to it.column }
assertTrue("Missing indexes to cover: $notFound", notFound.isEmpty())
}
private fun List<Index>.hasPrimaryIndexFor(table: String, column: String): Boolean {
return this.any { index -> index.table == table && index.columns[0] == column }
}
private data class Statement(
val name: String,
val sql: String
)
private fun SQLiteDatabase.getAllCreateStatements(): List<Statement> {
return this.rawQuery("SELECT name, sql FROM sqlite_schema WHERE sql NOT NULL AND name != 'sqlite_sequence'")
.readToList { cursor ->
Statement(
name = cursor.requireNonNullString("name"),
sql = cursor.requireNonNullString("sql").normalizeSql()
)
}
.filterNot { it.name.startsWith("sqlite_stat") }
.sortedBy { it.name }
}
private fun String.normalizeSql(): String {
return this
.split("\n")
.map { it.trim() }
.joinToString(separator = " ")
.replace(Regex.fromLiteral(" ,"), ",")
.replace(Regex("\\s+"), " ")
.replace(Regex.fromLiteral("( "), "(")
.replace(Regex.fromLiteral(" )"), ")")
.replace(Regex("CREATE TABLE \"([a-zA-Z_]+)\""), "CREATE TABLE $1") // for some reason SQLite will wrap table names in quotes for upgraded tables. This unwraps them.
}
private class InMemoryTestHelper(private val application: Application) : SQLiteOpenHelper(application, null, null, 1) {
override fun onCreate(db: SQLiteDatabase) {
for (statement in SNAPSHOT_V181) {
db.execSQL(statement.sql)
}
}
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
SignalDatabaseMigrations.migrate(application, db, 181, SignalDatabaseMigrations.DATABASE_VERSION)
}
/**
* This is the list of statements that existed at version 181. Never change this.
*/
private val SNAPSHOT_V181 = listOf(
Statement(
name = "message",
sql = "CREATE TABLE message (\n _id INTEGER PRIMARY KEY AUTOINCREMENT,\n date_sent INTEGER NOT NULL,\n date_received INTEGER NOT NULL,\n date_server INTEGER DEFAULT -1,\n thread_id INTEGER NOT NULL REFERENCES thread (_id) ON DELETE CASCADE,\n recipient_id INTEGER NOT NULL REFERENCES recipient (_id) ON DELETE CASCADE,\n recipient_device_id INTEGER,\n type INTEGER NOT NULL,\n body TEXT,\n read INTEGER DEFAULT 0,\n ct_l TEXT,\n exp INTEGER,\n m_type INTEGER,\n m_size INTEGER,\n st INTEGER,\n tr_id TEXT,\n subscription_id INTEGER DEFAULT -1, \n receipt_timestamp INTEGER DEFAULT -1, \n delivery_receipt_count INTEGER DEFAULT 0, \n read_receipt_count INTEGER DEFAULT 0, \n viewed_receipt_count INTEGER DEFAULT 0,\n mismatched_identities TEXT DEFAULT NULL,\n network_failures TEXT DEFAULT NULL,\n expires_in INTEGER DEFAULT 0,\n expire_started INTEGER DEFAULT 0,\n notified INTEGER DEFAULT 0,\n quote_id INTEGER DEFAULT 0,\n quote_author INTEGER DEFAULT 0,\n quote_body TEXT DEFAULT NULL,\n quote_missing INTEGER DEFAULT 0,\n quote_mentions BLOB DEFAULT NULL,\n quote_type INTEGER DEFAULT 0,\n shared_contacts TEXT DEFAULT NULL,\n unidentified INTEGER DEFAULT 0,\n link_previews TEXT DEFAULT NULL,\n view_once INTEGER DEFAULT 0,\n reactions_unread INTEGER DEFAULT 0,\n reactions_last_seen INTEGER DEFAULT -1,\n remote_deleted INTEGER DEFAULT 0,\n mentions_self INTEGER DEFAULT 0,\n notified_timestamp INTEGER DEFAULT 0,\n server_guid TEXT DEFAULT NULL,\n message_ranges BLOB DEFAULT NULL,\n story_type INTEGER DEFAULT 0,\n parent_story_id INTEGER DEFAULT 0,\n export_state BLOB DEFAULT NULL,\n exported INTEGER DEFAULT 0,\n scheduled_date INTEGER DEFAULT -1\n )"
),
Statement(
name = "part",
sql = "CREATE TABLE part (_id INTEGER PRIMARY KEY, mid INTEGER, seq INTEGER DEFAULT 0, ct TEXT, name TEXT, chset INTEGER, cd TEXT, fn TEXT, cid TEXT, cl TEXT, ctt_s INTEGER, ctt_t TEXT, encrypted INTEGER, pending_push INTEGER, _data TEXT, data_size INTEGER, file_name TEXT, unique_id INTEGER NOT NULL, digest BLOB, fast_preflight_id TEXT, voice_note INTEGER DEFAULT 0, borderless INTEGER DEFAULT 0, video_gif INTEGER DEFAULT 0, data_random BLOB, quote INTEGER DEFAULT 0, width INTEGER DEFAULT 0, height INTEGER DEFAULT 0, caption TEXT DEFAULT NULL, sticker_pack_id TEXT DEFAULT NULL, sticker_pack_key DEFAULT NULL, sticker_id INTEGER DEFAULT -1, sticker_emoji STRING DEFAULT NULL, data_hash TEXT DEFAULT NULL, blur_hash TEXT DEFAULT NULL, transform_properties TEXT DEFAULT NULL, transfer_file TEXT DEFAULT NULL, display_order INTEGER DEFAULT 0, upload_timestamp INTEGER DEFAULT 0, cdn_number INTEGER DEFAULT 0)"
),
Statement(
name = "thread",
sql = "CREATE TABLE thread (\n _id INTEGER PRIMARY KEY AUTOINCREMENT, \n date INTEGER DEFAULT 0, \n meaningful_messages INTEGER DEFAULT 0,\n recipient_id INTEGER NOT NULL UNIQUE REFERENCES recipient (_id) ON DELETE CASCADE,\n read INTEGER DEFAULT 1, \n type INTEGER DEFAULT 0, \n error INTEGER DEFAULT 0, \n snippet TEXT, \n snippet_type INTEGER DEFAULT 0, \n snippet_uri TEXT DEFAULT NULL, \n snippet_content_type TEXT DEFAULT NULL, \n snippet_extras TEXT DEFAULT NULL, \n unread_count INTEGER DEFAULT 0, \n archived INTEGER DEFAULT 0, \n status INTEGER DEFAULT 0, \n delivery_receipt_count INTEGER DEFAULT 0, \n read_receipt_count INTEGER DEFAULT 0, \n expires_in INTEGER DEFAULT 0, \n last_seen INTEGER DEFAULT 0, \n has_sent INTEGER DEFAULT 0, \n last_scrolled INTEGER DEFAULT 0, \n pinned INTEGER DEFAULT 0, \n unread_self_mention_count INTEGER DEFAULT 0\n)"
),
Statement(
name = "identities",
sql = "CREATE TABLE identities (\n _id INTEGER PRIMARY KEY AUTOINCREMENT, \n address INTEGER UNIQUE, \n identity_key TEXT, \n first_use INTEGER DEFAULT 0, \n timestamp INTEGER DEFAULT 0, \n verified INTEGER DEFAULT 0, \n nonblocking_approval INTEGER DEFAULT 0\n )"
),
Statement(
name = "drafts",
sql = "CREATE TABLE drafts (\n _id INTEGER PRIMARY KEY, \n thread_id INTEGER, \n type TEXT, \n value TEXT\n )"
),
Statement(
name = "push",
sql = "CREATE TABLE push (_id INTEGER PRIMARY KEY, type INTEGER, source TEXT, source_uuid TEXT, device_id INTEGER, body TEXT, content TEXT, timestamp INTEGER, server_timestamp INTEGER DEFAULT 0, server_delivered_timestamp INTEGER DEFAULT 0, server_guid TEXT DEFAULT NULL)"
),
Statement(
name = "groups",
sql = "CREATE TABLE groups (\n _id INTEGER PRIMARY KEY, \n group_id TEXT, \n recipient_id INTEGER,\n title TEXT,\n avatar_id INTEGER, \n avatar_key BLOB,\n avatar_content_type TEXT, \n avatar_relay TEXT,\n timestamp INTEGER,\n active INTEGER DEFAULT 1,\n avatar_digest BLOB, \n mms INTEGER DEFAULT 0, \n master_key BLOB, \n revision BLOB, \n decrypted_group BLOB, \n expected_v2_id TEXT DEFAULT NULL, \n former_v1_members TEXT DEFAULT NULL, \n distribution_id TEXT DEFAULT NULL, \n display_as_story INTEGER DEFAULT 0, \n auth_service_id TEXT DEFAULT NULL, \n last_force_update_timestamp INTEGER DEFAULT 0\n )"
),
Statement(
name = "group_membership",
sql = "CREATE TABLE group_membership ( _id INTEGER PRIMARY KEY, group_id TEXT NOT NULL, recipient_id INTEGER NOT NULL, UNIQUE(group_id, recipient_id) )"
),
Statement(
name = "recipient",
sql = "CREATE TABLE recipient (\n _id INTEGER PRIMARY KEY AUTOINCREMENT,\n uuid TEXT UNIQUE DEFAULT NULL,\n username TEXT UNIQUE DEFAULT NULL,\n phone TEXT UNIQUE DEFAULT NULL,\n email TEXT UNIQUE DEFAULT NULL,\n group_id TEXT UNIQUE DEFAULT NULL,\n group_type INTEGER DEFAULT 0,\n blocked INTEGER DEFAULT 0,\n message_ringtone TEXT DEFAULT NULL, \n message_vibrate INTEGER DEFAULT 0, \n call_ringtone TEXT DEFAULT NULL, \n call_vibrate INTEGER DEFAULT 0, \n notification_channel TEXT DEFAULT NULL, \n mute_until INTEGER DEFAULT 0, \n color TEXT DEFAULT NULL, \n seen_invite_reminder INTEGER DEFAULT 0,\n default_subscription_id INTEGER DEFAULT -1,\n message_expiration_time INTEGER DEFAULT 0,\n registered INTEGER DEFAULT 0,\n system_given_name TEXT DEFAULT NULL, \n system_family_name TEXT DEFAULT NULL, \n system_display_name TEXT DEFAULT NULL, \n system_photo_uri TEXT DEFAULT NULL, \n system_phone_label TEXT DEFAULT NULL, \n system_phone_type INTEGER DEFAULT -1, \n system_contact_uri TEXT DEFAULT NULL, \n system_info_pending INTEGER DEFAULT 0, \n profile_key TEXT DEFAULT NULL, \n profile_key_credential TEXT DEFAULT NULL, \n signal_profile_name TEXT DEFAULT NULL, \n profile_family_name TEXT DEFAULT NULL, \n profile_joined_name TEXT DEFAULT NULL, \n signal_profile_avatar TEXT DEFAULT NULL, \n profile_sharing INTEGER DEFAULT 0, \n last_profile_fetch INTEGER DEFAULT 0, \n unidentified_access_mode INTEGER DEFAULT 0, \n force_sms_selection INTEGER DEFAULT 0, \n storage_service_key TEXT UNIQUE DEFAULT NULL, \n mention_setting INTEGER DEFAULT 0, \n storage_proto TEXT DEFAULT NULL,\n capabilities INTEGER DEFAULT 0,\n last_session_reset BLOB DEFAULT NULL,\n wallpaper BLOB DEFAULT NULL,\n wallpaper_file TEXT DEFAULT NULL,\n about TEXT DEFAULT NULL,\n about_emoji TEXT DEFAULT NULL,\n extras BLOB DEFAULT NULL,\n groups_in_common INTEGER DEFAULT 0,\n chat_colors BLOB DEFAULT NULL,\n custom_chat_colors_id INTEGER DEFAULT 0,\n badges BLOB DEFAULT NULL,\n pni TEXT DEFAULT NULL,\n distribution_list_id INTEGER DEFAULT NULL,\n needs_pni_signature INTEGER DEFAULT 0,\n unregistered_timestamp INTEGER DEFAULT 0,\n hidden INTEGER DEFAULT 0,\n reporting_token BLOB DEFAULT NULL,\n system_nickname TEXT DEFAULT NULL\n)"
),
Statement(
name = "group_receipts",
sql = "CREATE TABLE group_receipts (\n _id INTEGER PRIMARY KEY, \n mms_id INTEGER, \n address INTEGER, \n status INTEGER, \n timestamp INTEGER, \n unidentified INTEGER DEFAULT 0\n )"
),
Statement(
name = "one_time_prekeys",
sql = "CREATE TABLE one_time_prekeys (\n _id INTEGER PRIMARY KEY,\n account_id TEXT NOT NULL,\n key_id INTEGER UNIQUE, \n public_key TEXT NOT NULL, \n private_key TEXT NOT NULL,\n UNIQUE(account_id, key_id)\n )"
),
Statement(
name = "signed_prekeys",
sql = "CREATE TABLE signed_prekeys (\n _id INTEGER PRIMARY KEY,\n account_id TEXT NOT NULL,\n key_id INTEGER UNIQUE, \n public_key TEXT NOT NULL,\n private_key TEXT NOT NULL,\n signature TEXT NOT NULL, \n timestamp INTEGER DEFAULT 0,\n UNIQUE(account_id, key_id)\n )"
),
Statement(
name = "sessions",
sql = "CREATE TABLE sessions (\n _id INTEGER PRIMARY KEY AUTOINCREMENT,\n account_id TEXT NOT NULL,\n address TEXT NOT NULL,\n device INTEGER NOT NULL,\n record BLOB NOT NULL,\n UNIQUE(account_id, address, device)\n )"
),
Statement(
name = "sender_keys",
sql = "CREATE TABLE sender_keys (\n _id INTEGER PRIMARY KEY AUTOINCREMENT, \n address TEXT NOT NULL, \n device INTEGER NOT NULL, \n distribution_id TEXT NOT NULL,\n record BLOB NOT NULL, \n created_at INTEGER NOT NULL, \n UNIQUE(address,device, distribution_id) ON CONFLICT REPLACE\n )"
),
Statement(
name = "sender_key_shared",
sql = "CREATE TABLE sender_key_shared (\n _id INTEGER PRIMARY KEY AUTOINCREMENT, \n distribution_id TEXT NOT NULL, \n address TEXT NOT NULL, \n device INTEGER NOT NULL, \n timestamp INTEGER DEFAULT 0, \n UNIQUE(distribution_id,address, device) ON CONFLICT REPLACE\n )"
),
Statement(
name = "pending_retry_receipts",
sql = "CREATE TABLE pending_retry_receipts(_id INTEGER PRIMARY KEY AUTOINCREMENT, author TEXT NOT NULL, device INTEGER NOT NULL, sent_timestamp INTEGER NOT NULL, received_timestamp TEXT NOT NULL, thread_id INTEGER NOT NULL, UNIQUE(author,sent_timestamp) ON CONFLICT REPLACE)"
),
Statement(
name = "sticker",
sql = "CREATE TABLE sticker (_id INTEGER PRIMARY KEY AUTOINCREMENT, pack_id TEXT NOT NULL, pack_key TEXT NOT NULL, pack_title TEXT NOT NULL, pack_author TEXT NOT NULL, sticker_id INTEGER, cover INTEGER, pack_order INTEGER, emoji TEXT NOT NULL, content_type TEXT DEFAULT NULL, last_used INTEGER, installed INTEGER,file_path TEXT NOT NULL, file_length INTEGER, file_random BLOB, UNIQUE(pack_id, sticker_id, cover) ON CONFLICT IGNORE)"
),
Statement(
name = "storage_key",
sql = "CREATE TABLE storage_key (_id INTEGER PRIMARY KEY AUTOINCREMENT, type INTEGER, key TEXT UNIQUE)"
),
Statement(
name = "mention",
sql = "CREATE TABLE mention(_id INTEGER PRIMARY KEY AUTOINCREMENT, thread_id INTEGER, message_id INTEGER, recipient_id INTEGER, range_start INTEGER, range_length INTEGER)"
),
Statement(
name = "payments",
sql = "CREATE TABLE payments(_id INTEGER PRIMARY KEY, uuid TEXT DEFAULT NULL, recipient INTEGER DEFAULT 0, recipient_address TEXT DEFAULT NULL, timestamp INTEGER, note TEXT DEFAULT NULL, direction INTEGER, state INTEGER, failure_reason INTEGER, amount BLOB NOT NULL, fee BLOB NOT NULL, transaction_record BLOB DEFAULT NULL, receipt BLOB DEFAULT NULL, payment_metadata BLOB DEFAULT NULL, receipt_public_key TEXT DEFAULT NULL, block_index INTEGER DEFAULT 0, block_timestamp INTEGER DEFAULT 0, seen INTEGER, UNIQUE(uuid) ON CONFLICT ABORT)"
),
Statement(
name = "chat_colors",
sql = "CREATE TABLE chat_colors (\n _id INTEGER PRIMARY KEY AUTOINCREMENT,\n chat_colors BLOB\n)"
),
Statement(
name = "emoji_search",
sql = "CREATE TABLE emoji_search (\n _id INTEGER PRIMARY KEY,\n label TEXT NOT NULL,\n emoji TEXT NOT NULL,\n rank INTEGER DEFAULT 2147483647 \n )"
),
Statement(
name = "avatar_picker",
sql = "CREATE TABLE avatar_picker (\n _id INTEGER PRIMARY KEY AUTOINCREMENT,\n last_used INTEGER DEFAULT 0,\n group_id TEXT DEFAULT NULL,\n avatar BLOB NOT NULL\n)"
),
Statement(
name = "group_call_ring",
sql = "CREATE TABLE group_call_ring (\n _id INTEGER PRIMARY KEY,\n ring_id INTEGER UNIQUE,\n date_received INTEGER,\n ring_state INTEGER\n)"
),
Statement(
name = "reaction",
sql = "CREATE TABLE reaction (\n _id INTEGER PRIMARY KEY,\n message_id INTEGER NOT NULL REFERENCES message (_id) ON DELETE CASCADE,\n author_id INTEGER NOT NULL REFERENCES recipient (_id) ON DELETE CASCADE,\n emoji TEXT NOT NULL,\n date_sent INTEGER NOT NULL,\n date_received INTEGER NOT NULL,\n UNIQUE(message_id, author_id) ON CONFLICT REPLACE\n)"
),
Statement(
name = "donation_receipt",
sql = "CREATE TABLE donation_receipt (\n _id INTEGER PRIMARY KEY AUTOINCREMENT,\n receipt_type TEXT NOT NULL,\n receipt_date INTEGER NOT NULL,\n amount TEXT NOT NULL,\n currency TEXT NOT NULL,\n subscription_level INTEGER NOT NULL\n)"
),
Statement(
name = "story_sends",
sql = "CREATE TABLE story_sends (\n _id INTEGER PRIMARY KEY,\n message_id INTEGER NOT NULL REFERENCES message (_id) ON DELETE CASCADE,\n recipient_id INTEGER NOT NULL REFERENCES recipient (_id) ON DELETE CASCADE,\n sent_timestamp INTEGER NOT NULL,\n allows_replies INTEGER NOT NULL,\n distribution_id TEXT NOT NULL REFERENCES distribution_list (distribution_id) ON DELETE CASCADE\n)"
),
Statement(
name = "cds",
sql = "CREATE TABLE cds (\n _id INTEGER PRIMARY KEY,\n e164 TEXT NOT NULL UNIQUE ON CONFLICT IGNORE,\n last_seen_at INTEGER DEFAULT 0\n )"
),
Statement(
name = "remote_megaphone",
sql = "CREATE TABLE remote_megaphone (\n _id INTEGER PRIMARY KEY,\n uuid TEXT UNIQUE NOT NULL,\n priority INTEGER NOT NULL,\n countries TEXT,\n minimum_version INTEGER NOT NULL,\n dont_show_before INTEGER NOT NULL,\n dont_show_after INTEGER NOT NULL,\n show_for_days INTEGER NOT NULL,\n conditional_id TEXT,\n primary_action_id TEXT,\n secondary_action_id TEXT,\n image_url TEXT,\n image_uri TEXT DEFAULT NULL,\n title TEXT NOT NULL,\n body TEXT NOT NULL,\n primary_action_text TEXT,\n secondary_action_text TEXT,\n shown_at INTEGER DEFAULT 0,\n finished_at INTEGER DEFAULT 0,\n primary_action_data TEXT DEFAULT NULL,\n secondary_action_data TEXT DEFAULT NULL,\n snoozed_at INTEGER DEFAULT 0,\n seen_count INTEGER DEFAULT 0\n)"
),
Statement(
name = "pending_pni_signature_message",
sql = "CREATE TABLE pending_pni_signature_message (\n _id INTEGER PRIMARY KEY,\n recipient_id INTEGER NOT NULL REFERENCES recipient (_id) ON DELETE CASCADE,\n sent_timestamp INTEGER NOT NULL,\n device_id INTEGER NOT NULL\n )"
),
Statement(
name = "call",
sql = "CREATE TABLE call (\n _id INTEGER PRIMARY KEY,\n call_id INTEGER NOT NULL UNIQUE,\n message_id INTEGER NOT NULL REFERENCES message (_id) ON DELETE CASCADE,\n peer INTEGER NOT NULL REFERENCES recipient (_id) ON DELETE CASCADE,\n type INTEGER NOT NULL,\n direction INTEGER NOT NULL,\n event INTEGER NOT NULL\n)"
),
Statement(
name = "message_fts",
sql = "CREATE VIRTUAL TABLE message_fts USING fts5(body, thread_id UNINDEXED, content=message, content_rowid=_id)"
),
Statement(
name = "remapped_recipients",
sql = "CREATE TABLE remapped_recipients (\n _id INTEGER PRIMARY KEY AUTOINCREMENT, \n old_id INTEGER UNIQUE, \n new_id INTEGER\n )"
),
Statement(
name = "remapped_threads",
sql = "CREATE TABLE remapped_threads (\n _id INTEGER PRIMARY KEY AUTOINCREMENT, \n old_id INTEGER UNIQUE, \n new_id INTEGER\n )"
),
Statement(
name = "msl_payload",
sql = "CREATE TABLE msl_payload (\n _id INTEGER PRIMARY KEY,\n date_sent INTEGER NOT NULL,\n content BLOB NOT NULL,\n content_hint INTEGER NOT NULL,\n urgent INTEGER NOT NULL DEFAULT 1\n )"
),
Statement(
name = "msl_recipient",
sql = "CREATE TABLE msl_recipient (\n _id INTEGER PRIMARY KEY,\n payload_id INTEGER NOT NULL REFERENCES msl_payload (_id) ON DELETE CASCADE,\n recipient_id INTEGER NOT NULL, \n device INTEGER NOT NULL\n )"
),
Statement(
name = "msl_message",
sql = "CREATE TABLE msl_message (\n _id INTEGER PRIMARY KEY,\n payload_id INTEGER NOT NULL REFERENCES msl_payload (_id) ON DELETE CASCADE,\n message_id INTEGER NOT NULL\n )"
),
Statement(
name = "notification_profile",
sql = "CREATE TABLE notification_profile (\n _id INTEGER PRIMARY KEY AUTOINCREMENT,\n name TEXT NOT NULL UNIQUE,\n emoji TEXT NOT NULL,\n color TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n allow_all_calls INTEGER NOT NULL DEFAULT 0,\n allow_all_mentions INTEGER NOT NULL DEFAULT 0\n)"
),
Statement(
name = "notification_profile_schedule",
sql = "CREATE TABLE notification_profile_schedule (\n _id INTEGER PRIMARY KEY AUTOINCREMENT,\n notification_profile_id INTEGER NOT NULL REFERENCES notification_profile (_id) ON DELETE CASCADE,\n enabled INTEGER NOT NULL DEFAULT 0,\n start INTEGER NOT NULL,\n end INTEGER NOT NULL,\n days_enabled TEXT NOT NULL\n)"
),
Statement(
name = "notification_profile_allowed_members",
sql = "CREATE TABLE notification_profile_allowed_members (\n _id INTEGER PRIMARY KEY AUTOINCREMENT,\n notification_profile_id INTEGER NOT NULL REFERENCES notification_profile (_id) ON DELETE CASCADE,\n recipient_id INTEGER NOT NULL,\n UNIQUE(notification_profile_id, recipient_id) ON CONFLICT REPLACE\n)"
),
Statement(
name = "distribution_list",
sql = "CREATE TABLE distribution_list (\n _id INTEGER PRIMARY KEY AUTOINCREMENT,\n name TEXT UNIQUE NOT NULL,\n distribution_id TEXT UNIQUE NOT NULL,\n recipient_id INTEGER UNIQUE REFERENCES recipient (_id),\n allows_replies INTEGER DEFAULT 1,\n deletion_timestamp INTEGER DEFAULT 0,\n is_unknown INTEGER DEFAULT 0,\n privacy_mode INTEGER DEFAULT 0\n )"
),
Statement(
name = "distribution_list_member",
sql = "CREATE TABLE distribution_list_member (\n _id INTEGER PRIMARY KEY AUTOINCREMENT,\n list_id INTEGER NOT NULL REFERENCES distribution_list (_id) ON DELETE CASCADE,\n recipient_id INTEGER NOT NULL REFERENCES recipient (_id),\n privacy_mode INTEGER DEFAULT 0\n )"
),
Statement(
name = "recipient_group_type_index",
sql = "CREATE INDEX recipient_group_type_index ON recipient (group_type)"
),
Statement(
name = "recipient_pni_index",
sql = "CREATE UNIQUE INDEX recipient_pni_index ON recipient (pni)"
),
Statement(
name = "recipient_service_id_profile_key",
sql = "CREATE INDEX recipient_service_id_profile_key ON recipient (uuid, profile_key) WHERE uuid NOT NULL AND profile_key NOT NULL"
),
Statement(
name = "mms_read_and_notified_and_thread_id_index",
sql = "CREATE INDEX mms_read_and_notified_and_thread_id_index ON message (read, notified, thread_id)"
),
Statement(
name = "mms_type_index",
sql = "CREATE INDEX mms_type_index ON message (type)"
),
Statement(
name = "mms_date_sent_index",
sql = "CREATE INDEX mms_date_sent_index ON message (date_sent, recipient_id, thread_id)"
),
Statement(
name = "mms_date_server_index",
sql = "CREATE INDEX mms_date_server_index ON message (date_server)"
),
Statement(
name = "mms_thread_date_index",
sql = "CREATE INDEX mms_thread_date_index ON message (thread_id, date_received)"
),
Statement(
name = "mms_reactions_unread_index",
sql = "CREATE INDEX mms_reactions_unread_index ON message (reactions_unread)"
),
Statement(
name = "mms_story_type_index",
sql = "CREATE INDEX mms_story_type_index ON message (story_type)"
),
Statement(
name = "mms_parent_story_id_index",
sql = "CREATE INDEX mms_parent_story_id_index ON message (parent_story_id)"
),
Statement(
name = "mms_thread_story_parent_story_scheduled_date_index",
sql = "CREATE INDEX mms_thread_story_parent_story_scheduled_date_index ON message (thread_id, date_received, story_type, parent_story_id, scheduled_date)"
),
Statement(
name = "message_quote_id_quote_author_scheduled_date_index",
sql = "CREATE INDEX message_quote_id_quote_author_scheduled_date_index ON message (quote_id, quote_author, scheduled_date)"
),
Statement(
name = "mms_exported_index",
sql = "CREATE INDEX mms_exported_index ON message (exported)"
),
Statement(
name = "mms_id_type_payment_transactions_index",
sql = "CREATE INDEX mms_id_type_payment_transactions_index ON message (_id,type) WHERE type & 12884901888 != 0"
),
Statement(
name = "part_mms_id_index",
sql = "CREATE INDEX part_mms_id_index ON part (mid)"
),
Statement(
name = "pending_push_index",
sql = "CREATE INDEX pending_push_index ON part (pending_push)"
),
Statement(
name = "part_sticker_pack_id_index",
sql = "CREATE INDEX part_sticker_pack_id_index ON part (sticker_pack_id)"
),
Statement(
name = "part_data_hash_index",
sql = "CREATE INDEX part_data_hash_index ON part (data_hash)"
),
Statement(
name = "part_data_index",
sql = "CREATE INDEX part_data_index ON part (_data)"
),
Statement(
name = "thread_recipient_id_index",
sql = "CREATE INDEX thread_recipient_id_index ON thread (recipient_id)"
),
Statement(
name = "archived_count_index",
sql = "CREATE INDEX archived_count_index ON thread (archived, meaningful_messages)"
),
Statement(
name = "thread_pinned_index",
sql = "CREATE INDEX thread_pinned_index ON thread (pinned)"
),
Statement(
name = "thread_read",
sql = "CREATE INDEX thread_read ON thread (read)"
),
Statement(
name = "draft_thread_index",
sql = "CREATE INDEX draft_thread_index ON drafts (thread_id)"
),
Statement(
name = "group_id_index",
sql = "CREATE UNIQUE INDEX group_id_index ON groups (group_id)"
),
Statement(
name = "group_recipient_id_index",
sql = "CREATE UNIQUE INDEX group_recipient_id_index ON groups (recipient_id)"
),
Statement(
name = "expected_v2_id_index",
sql = "CREATE UNIQUE INDEX expected_v2_id_index ON groups (expected_v2_id)"
),
Statement(
name = "group_distribution_id_index",
sql = "CREATE UNIQUE INDEX group_distribution_id_index ON groups(distribution_id)"
),
Statement(
name = "group_receipt_mms_id_index",
sql = "CREATE INDEX group_receipt_mms_id_index ON group_receipts (mms_id)"
),
Statement(
name = "sticker_pack_id_index",
sql = "CREATE INDEX sticker_pack_id_index ON sticker (pack_id)"
),
Statement(
name = "sticker_sticker_id_index",
sql = "CREATE INDEX sticker_sticker_id_index ON sticker (sticker_id)"
),
Statement(
name = "storage_key_type_index",
sql = "CREATE INDEX storage_key_type_index ON storage_key (type)"
),
Statement(
name = "mention_message_id_index",
sql = "CREATE INDEX mention_message_id_index ON mention (message_id)"
),
Statement(
name = "mention_recipient_id_thread_id_index",
sql = "CREATE INDEX mention_recipient_id_thread_id_index ON mention (recipient_id, thread_id)"
),
Statement(
name = "timestamp_direction_index",
sql = "CREATE INDEX timestamp_direction_index ON payments (timestamp, direction)"
),
Statement(
name = "timestamp_index",
sql = "CREATE INDEX timestamp_index ON payments (timestamp)"
),
Statement(
name = "receipt_public_key_index",
sql = "CREATE UNIQUE INDEX receipt_public_key_index ON payments (receipt_public_key)"
),
Statement(
name = "msl_payload_date_sent_index",
sql = "CREATE INDEX msl_payload_date_sent_index ON msl_payload (date_sent)"
),
Statement(
name = "msl_recipient_recipient_index",
sql = "CREATE INDEX msl_recipient_recipient_index ON msl_recipient (recipient_id, device, payload_id)"
),
Statement(
name = "msl_recipient_payload_index",
sql = "CREATE INDEX msl_recipient_payload_index ON msl_recipient (payload_id)"
),
Statement(
name = "msl_message_message_index",
sql = "CREATE INDEX msl_message_message_index ON msl_message (message_id, payload_id)"
),
Statement(
name = "date_received_index",
sql = "CREATE INDEX date_received_index on group_call_ring (date_received)"
),
Statement(
name = "notification_profile_schedule_profile_index",
sql = "CREATE INDEX notification_profile_schedule_profile_index ON notification_profile_schedule (notification_profile_id)"
),
Statement(
name = "notification_profile_allowed_members_profile_index",
sql = "CREATE INDEX notification_profile_allowed_members_profile_index ON notification_profile_allowed_members (notification_profile_id)"
),
Statement(
name = "donation_receipt_type_index",
sql = "CREATE INDEX donation_receipt_type_index ON donation_receipt (receipt_type)"
),
Statement(
name = "donation_receipt_date_index",
sql = "CREATE INDEX donation_receipt_date_index ON donation_receipt (receipt_date)"
),
Statement(
name = "story_sends_recipient_id_sent_timestamp_allows_replies_index",
sql = "CREATE INDEX story_sends_recipient_id_sent_timestamp_allows_replies_index ON story_sends (recipient_id, sent_timestamp, allows_replies)"
),
Statement(
name = "story_sends_message_id_distribution_id_index",
sql = "CREATE INDEX story_sends_message_id_distribution_id_index ON story_sends (message_id, distribution_id)"
),
Statement(
name = "distribution_list_member_list_id_recipient_id_privacy_mode_index",
sql = "CREATE UNIQUE INDEX distribution_list_member_list_id_recipient_id_privacy_mode_index ON distribution_list_member (list_id, recipient_id, privacy_mode)"
),
Statement(
name = "pending_pni_recipient_sent_device_index",
sql = "CREATE UNIQUE INDEX pending_pni_recipient_sent_device_index ON pending_pni_signature_message (recipient_id, sent_timestamp, device_id)"
),
Statement(
name = "call_call_id_index",
sql = "CREATE INDEX call_call_id_index ON call (call_id)"
),
Statement(
name = "call_message_id_index",
sql = "CREATE INDEX call_message_id_index ON call (message_id)"
),
Statement(
name = "message_ai",
sql = "CREATE TRIGGER message_ai AFTER INSERT ON message BEGIN\n INSERT INTO message_fts(rowid, body, thread_id) VALUES (new._id, new.body, new.thread_id);\n END"
),
Statement(
name = "message_ad",
sql = "CREATE TRIGGER message_ad AFTER DELETE ON message BEGIN\n INSERT INTO message_fts(message_fts, rowid, body, thread_id) VALUES('delete', old._id, old.body, old.thread_id);\n END"
),
Statement(
name = "message_au",
sql = "CREATE TRIGGER message_au AFTER UPDATE ON message BEGIN\n INSERT INTO message_fts(message_fts, rowid, body, thread_id) VALUES('delete', old._id, old.body, old.thread_id);\n INSERT INTO message_fts(rowid, body, thread_id) VALUES (new._id, new.body, new.thread_id);\n END"
),
Statement(
name = "msl_message_delete",
sql = "CREATE TRIGGER msl_message_delete AFTER DELETE ON message \n BEGIN \n \tDELETE FROM msl_payload WHERE _id IN (SELECT payload_id FROM msl_message WHERE message_id = old._id);\n END"
),
Statement(
name = "msl_attachment_delete",
sql = "CREATE TRIGGER msl_attachment_delete AFTER DELETE ON part\n BEGIN\n \tDELETE FROM msl_payload WHERE _id IN (SELECT payload_id FROM msl_message WHERE message_id = old.mid);\n END"
)
)
}
}

View File

@@ -7,7 +7,7 @@ import org.thoughtcrime.securesms.database.model.DistributionListId
import org.thoughtcrime.securesms.database.model.DistributionListRecord
import org.thoughtcrime.securesms.database.model.StoryType
import org.thoughtcrime.securesms.recipients.RecipientId
import org.whispersystems.signalservice.api.push.ACI
import org.whispersystems.signalservice.api.push.ServiceId.ACI
import java.util.UUID
class DistributionListTablesTest {

View File

@@ -0,0 +1,341 @@
package org.thoughtcrime.securesms.database
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotEquals
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.signal.core.util.deleteAll
import org.signal.core.util.readToList
import org.signal.core.util.requireLong
import org.signal.core.util.withinTransaction
import org.signal.libsignal.zkgroup.groups.GroupMasterKey
import org.signal.storageservice.protos.groups.Member
import org.signal.storageservice.protos.groups.local.DecryptedGroup
import org.signal.storageservice.protos.groups.local.DecryptedMember
import org.thoughtcrime.securesms.groups.GroupId
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.testing.SignalActivityRule
import java.security.SecureRandom
import kotlin.random.Random
class GroupTableTest {
@get:Rule
val harness = SignalActivityRule()
private lateinit var groupTable: GroupTable
@Before
fun setUp() {
groupTable = SignalDatabase.groups
groupTable.writableDatabase.deleteAll(GroupTable.TABLE_NAME)
groupTable.writableDatabase.deleteAll(GroupTable.MembershipTable.TABLE_NAME)
}
@Test
fun whenICreateGroupV2_thenIExpectMemberRowsPopulated() {
val groupId = insertPushGroup()
//language=sql
val members: List<RecipientId> = groupTable.writableDatabase.query(
"""
SELECT ${GroupTable.MembershipTable.RECIPIENT_ID}
FROM ${GroupTable.MembershipTable.TABLE_NAME}
WHERE ${GroupTable.MembershipTable.GROUP_ID} = "${groupId.serialize()}"
""".trimIndent()
).readToList {
RecipientId.from(it.requireLong(GroupTable.RECIPIENT_ID))
}
assertEquals(2, members.size)
}
@Test
fun givenAGroupV2_whenIGetGroupsContainingMember_thenIExpectGroup() {
val groupId = insertPushGroup()
insertThread(groupId)
val groups = groupTable.getGroupsContainingMember(harness.others[0], false)
assertEquals(1, groups.size)
assertEquals(groupId, groups[0].id)
}
@Test
fun givenAnMmsGroup_whenIGetMembers_thenIExpectAllMembers() {
val groupId = insertMmsGroup()
val groups = groupTable.getGroupMemberIds(groupId, GroupTable.MemberSet.FULL_MEMBERS_INCLUDING_SELF)
assertEquals(2, groups.size)
}
@Test
fun givenGroups_whenIQueryGroupsByMembership_thenIExpectBothGroups() {
insertPushGroup()
insertMmsGroup(members = listOf(harness.others[1]))
val groups = groupTable.queryGroupsByMembership(
setOf(harness.self.id, harness.others[1]),
includeInactive = false,
excludeV1 = false,
excludeMms = false
)
assertEquals(2, groups.cursor?.count)
}
@Test
fun givenGroups_whenIGetGroups_thenIExpectBothGroups() {
insertPushGroup()
insertMmsGroup(members = listOf(harness.others[1]))
val groups = groupTable.getGroups()
assertEquals(2, groups.cursor?.count)
}
@Test
fun givenAGroup_whenIGetGroup_thenIExpectGroup() {
val v2Group = insertPushGroup()
insertThread(v2Group)
val groupRecord = groupTable.getGroup(v2Group).get()
assertEquals(setOf(harness.self.id, harness.others[0]), groupRecord.members.toSet())
}
@Test
fun givenAGroupAndARemap_whenIGetGroup_thenIExpectRemap() {
val v2Group = insertPushGroup()
insertThread(v2Group)
groupTable.writableDatabase.withinTransaction {
RemappedRecords.getInstance().addRecipient(harness.others[0], harness.others[1])
}
val groupRecord = groupTable.getGroup(v2Group).get()
assertEquals(setOf(harness.self.id, harness.others[1]), groupRecord.members.toSet())
}
@Test
fun givenAGroup_whenIRemapRecipientsThatHaveAConflict_thenIExpectDeletion() {
val v2Group = insertPushGroupWithSelfAndOthers(
listOf(
harness.others[0],
harness.others[1]
)
)
insertThread(v2Group)
groupTable.remapRecipient(harness.others[0], harness.others[1])
val groupRecord = groupTable.getGroup(v2Group).get()
assertEquals(setOf(harness.self.id, harness.others[1]), groupRecord.members.toSet())
}
@Test
fun givenAGroup_whenIRemapRecipients_thenIExpectRemap() {
val v2Group = insertPushGroup()
insertThread(v2Group)
val newId = harness.others[1]
groupTable.remapRecipient(harness.others[0], newId)
val groupRecord = groupTable.getGroup(v2Group).get()
assertEquals(setOf(harness.self.id, newId), groupRecord.members.toSet())
}
@Test
fun givenAGroupAndMember_whenIIsCurrentMember_thenIExpectTrue() {
val v2Group = insertPushGroup()
val actual = groupTable.isCurrentMember(v2Group.requirePush(), harness.others[0])
assertTrue(actual)
}
@Test
fun givenAGroupAndMember_whenIRemove_thenIExpectNotAMember() {
val v2Group = insertPushGroup()
groupTable.remove(v2Group, harness.others[0])
val actual = groupTable.isCurrentMember(v2Group.requirePush(), harness.others[0])
assertFalse(actual)
}
@Test
fun givenAGroupAndNonMember_whenIIsCurrentMember_thenIExpectFalse() {
val v2Group = insertPushGroup()
val actual = groupTable.isCurrentMember(v2Group.requirePush(), harness.others[1])
assertFalse(actual)
}
@Test
fun givenAGroup_whenIUpdateMembers_thenIExpectUpdatedMembers() {
val v2Group = insertPushGroup()
groupTable.updateMembers(v2Group, listOf(harness.self.id, harness.others[1]))
val groupRecord = groupTable.getGroup(v2Group)
assertEquals(setOf(harness.self.id, harness.others[1]), groupRecord.get().members.toSet())
}
@Test
fun givenAnMmsGroup_whenIGetOrCreateMmsGroup_thenIExpectMyMmsGroup() {
val members: List<RecipientId> = listOf(harness.self.id, harness.others[0])
val other = insertMmsGroup(members + listOf(harness.others[1]))
val mmsGroup = insertMmsGroup(members)
val actual = groupTable.getOrCreateMmsGroupForMembers(members.toSet())
assertNotEquals(other, actual)
assertEquals(mmsGroup, actual)
}
@Test
fun givenMultipleMmsGroups_whenIGetOrCreateMmsGroup_thenIExpectMyMmsGroup() {
val group1Members: List<RecipientId> = listOf(harness.self.id, harness.others[0], harness.others[1])
val group2Members: List<RecipientId> = listOf(harness.self.id, harness.others[0], harness.others[2])
val group1: GroupId = insertMmsGroup(group1Members)
val group2: GroupId = insertMmsGroup(group2Members)
val group1Result: GroupId = groupTable.getOrCreateMmsGroupForMembers(group1Members.toSet())
val group2Result: GroupId = groupTable.getOrCreateMmsGroupForMembers(group2Members.toSet())
assertEquals(group1, group1Result)
assertEquals(group2, group2Result)
assertNotEquals(group1Result, group2Result)
}
@Test
fun givenMultipleMmsGroupsWithDifferentMemberOrders_whenIGetOrCreateMmsGroup_thenIExpectMyMmsGroup() {
val group1Members: List<RecipientId> = listOf(harness.self.id, harness.others[0], harness.others[1], harness.others[2]).shuffled()
val group2Members: List<RecipientId> = listOf(harness.self.id, harness.others[0], harness.others[2], harness.others[3]).shuffled()
val group1: GroupId = insertMmsGroup(group1Members)
val group2: GroupId = insertMmsGroup(group2Members)
val group1Result: GroupId = groupTable.getOrCreateMmsGroupForMembers(group1Members.shuffled().toSet())
val group2Result: GroupId = groupTable.getOrCreateMmsGroupForMembers(group2Members.shuffled().toSet())
assertEquals(group1, group1Result)
assertEquals(group2, group2Result)
assertNotEquals(group1Result, group2Result)
}
@Test
fun givenMmsGroupWithOneMember_whenIGetOrCreateMmsGroup_thenIExpectMyMmsGroup() {
val groupMembers: List<RecipientId> = listOf(harness.self.id)
val group: GroupId = insertMmsGroup(groupMembers)
val groupResult: GroupId = groupTable.getOrCreateMmsGroupForMembers(groupMembers.toSet())
assertEquals(group, groupResult)
}
@Test
fun givenTwoGroupsWithoutMembers_whenIQueryThem_thenIExpectEach() {
val g1 = insertPushGroup(listOf())
val g2 = insertPushGroup(listOf())
val gr1 = groupTable.getGroup(g1)
val gr2 = groupTable.getGroup(g2)
assertEquals(g1, gr1.get().id)
assertEquals(g2, gr2.get().id)
}
@Test
fun givenASharedActiveGroupWithoutAThread_whenISearchForRecipientsWithGroupsInCommon_thenIExpectThatGroup() {
val groupInCommon = insertPushGroup()
val expected = Recipient.resolved(harness.others[0])
SignalDatabase.recipients.setProfileSharing(expected.id, false)
SignalDatabase.recipients.queryGroupMemberContacts("Buddy")!!.use {
assertTrue(it.moveToFirst())
assertEquals(1, it.count)
assertEquals(expected.id.toLong(), it.requireLong(RecipientTable.ID))
}
val groups = groupTable.getPushGroupsContainingMember(expected.id)
assertEquals(1, groups.size)
assertEquals(groups[0].id, groupInCommon)
}
private fun insertThread(groupId: GroupId): Long {
val groupRecipient = SignalDatabase.recipients.getByGroupId(groupId).get()
return SignalDatabase.threads.getOrCreateThreadIdFor(Recipient.resolved(groupRecipient))
}
private fun insertMmsGroup(members: List<RecipientId> = listOf(harness.self.id, harness.others[0])): GroupId {
val id = GroupId.createMms(SecureRandom())
groupTable.create(
id,
null,
members.apply {
println("Creating a group with ${members.size} members")
}
)
return id
}
private fun insertPushGroup(
members: List<DecryptedMember> = listOf(
DecryptedMember.Builder()
.aciBytes(harness.self.requireAci().toByteString())
.joinedAtRevision(0)
.role(Member.Role.DEFAULT)
.build(),
DecryptedMember.Builder()
.aciBytes(Recipient.resolved(harness.others[0]).requireAci().toByteString())
.joinedAtRevision(0)
.role(Member.Role.DEFAULT)
.build()
)
): GroupId {
val groupMasterKey = GroupMasterKey(Random.nextBytes(GroupMasterKey.SIZE))
val decryptedGroupState = DecryptedGroup.Builder()
.members(members)
.revision(0)
.build()
return groupTable.create(groupMasterKey, decryptedGroupState)!!
}
private fun insertPushGroupWithSelfAndOthers(others: List<RecipientId>): GroupId {
val groupMasterKey = GroupMasterKey(Random.nextBytes(GroupMasterKey.SIZE))
val selfMember: DecryptedMember = DecryptedMember.Builder()
.aciBytes(harness.self.requireAci().toByteString())
.joinedAtRevision(0)
.role(Member.Role.DEFAULT)
.build()
val otherMembers: List<DecryptedMember> = others.map { id ->
DecryptedMember.Builder()
.aciBytes(Recipient.resolved(id).requireAci().toByteString())
.joinedAtRevision(0)
.role(Member.Role.DEFAULT)
.build()
}
val decryptedGroupState = DecryptedGroup.Builder()
.members(listOf(selfMember) + otherMembers)
.revision(0)
.build()
return groupTable.create(groupMasterKey, decryptedGroupState)!!
}
}

View File

@@ -0,0 +1,176 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.database
import junit.framework.TestCase.assertEquals
import junit.framework.TestCase.assertNotNull
import junit.framework.TestCase.assertNull
import org.junit.Test
import org.signal.core.util.readToSingleObject
import org.signal.core.util.requireLongOrNull
import org.signal.core.util.select
import org.signal.core.util.update
import org.signal.libsignal.protocol.ecc.Curve
import org.signal.libsignal.protocol.kem.KEMKeyPair
import org.signal.libsignal.protocol.kem.KEMKeyType
import org.signal.libsignal.protocol.state.KyberPreKeyRecord
import org.whispersystems.signalservice.api.push.ServiceId
import org.whispersystems.signalservice.api.push.ServiceId.ACI
import org.whispersystems.signalservice.api.push.ServiceId.PNI
import java.util.UUID
class KyberPreKeyTableTest {
private val aci: ACI = ACI.from(UUID.randomUUID())
private val pni: PNI = PNI.from(UUID.randomUUID())
@Test
fun markAllStaleIfNecessary_onlyUpdatesMatchingAccountAndZeroValues() {
insertTestRecord(aci, id = 1)
insertTestRecord(aci, id = 2)
insertTestRecord(aci, id = 3, staleTime = 42)
insertTestRecord(pni, id = 4)
val now = System.currentTimeMillis()
SignalDatabase.kyberPreKeys.markAllStaleIfNecessary(aci, now)
assertEquals(now, getStaleTime(aci, 1))
assertEquals(now, getStaleTime(aci, 2))
assertEquals(42L, getStaleTime(aci, 3))
assertEquals(0L, getStaleTime(pni, 4))
}
@Test
fun deleteAllStaleBefore_deleteOldBeforeThreshold() {
insertTestRecord(aci, id = 1, staleTime = 10)
insertTestRecord(aci, id = 2, staleTime = 10)
insertTestRecord(aci, id = 3, staleTime = 10)
insertTestRecord(aci, id = 4, staleTime = 15)
insertTestRecord(aci, id = 5, staleTime = 0)
SignalDatabase.kyberPreKeys.deleteAllStaleBefore(aci, threshold = 11, minCount = 0)
assertNull(getStaleTime(aci, 1))
assertNull(getStaleTime(aci, 2))
assertNull(getStaleTime(aci, 3))
assertNotNull(getStaleTime(aci, 4))
assertNotNull(getStaleTime(aci, 5))
}
@Test
fun deleteAllStaleBefore_neverDeleteStaleOfZero() {
insertTestRecord(aci, id = 1, staleTime = 0)
insertTestRecord(aci, id = 2, staleTime = 0)
insertTestRecord(aci, id = 3, staleTime = 0)
insertTestRecord(aci, id = 4, staleTime = 0)
insertTestRecord(aci, id = 5, staleTime = 0)
SignalDatabase.kyberPreKeys.deleteAllStaleBefore(aci, threshold = 10, minCount = 1)
assertNotNull(getStaleTime(aci, 1))
assertNotNull(getStaleTime(aci, 2))
assertNotNull(getStaleTime(aci, 3))
assertNotNull(getStaleTime(aci, 4))
assertNotNull(getStaleTime(aci, 5))
}
@Test
fun deleteAllStaleBefore_respectMinCount() {
insertTestRecord(aci, id = 1, staleTime = 10)
insertTestRecord(aci, id = 2, staleTime = 10)
insertTestRecord(aci, id = 3, staleTime = 10)
insertTestRecord(aci, id = 4, staleTime = 10)
insertTestRecord(aci, id = 5, staleTime = 10)
SignalDatabase.kyberPreKeys.deleteAllStaleBefore(aci, threshold = 11, minCount = 3)
assertNull(getStaleTime(aci, 1))
assertNull(getStaleTime(aci, 2))
assertNotNull(getStaleTime(aci, 3))
assertNotNull(getStaleTime(aci, 4))
assertNotNull(getStaleTime(aci, 5))
}
@Test
fun deleteAllStaleBefore_respectAccount() {
insertTestRecord(aci, id = 1, staleTime = 10)
insertTestRecord(aci, id = 2, staleTime = 10)
insertTestRecord(aci, id = 3, staleTime = 10)
insertTestRecord(pni, id = 4, staleTime = 10)
insertTestRecord(pni, id = 5, staleTime = 10)
SignalDatabase.kyberPreKeys.deleteAllStaleBefore(aci, threshold = 11, minCount = 2)
assertNull(getStaleTime(aci, 1))
assertNotNull(getStaleTime(aci, 2))
assertNotNull(getStaleTime(aci, 3))
assertNotNull(getStaleTime(pni, 4))
assertNotNull(getStaleTime(pni, 5))
}
@Test
fun deleteAllStaleBefore_ignoreLastResortForMinCount() {
insertTestRecord(aci, id = 1, staleTime = 10)
insertTestRecord(aci, id = 2, staleTime = 10)
insertTestRecord(aci, id = 3, staleTime = 10)
insertTestRecord(aci, id = 4, staleTime = 10)
insertTestRecord(aci, id = 5, staleTime = 10, lastResort = true)
SignalDatabase.kyberPreKeys.deleteAllStaleBefore(aci, threshold = 11, minCount = 3)
assertNull(getStaleTime(aci, 1))
assertNotNull(getStaleTime(aci, 2))
assertNotNull(getStaleTime(aci, 3))
assertNotNull(getStaleTime(aci, 4))
assertNotNull(getStaleTime(aci, 5))
}
@Test
fun deleteAllStaleBefore_neverDeleteLastResort() {
insertTestRecord(aci, id = 1, staleTime = 10, lastResort = true)
insertTestRecord(aci, id = 2, staleTime = 10, lastResort = true)
insertTestRecord(aci, id = 3, staleTime = 10, lastResort = true)
SignalDatabase.oneTimePreKeys.deleteAllStaleBefore(aci, threshold = 11, minCount = 0)
assertNotNull(getStaleTime(aci, 1))
assertNotNull(getStaleTime(aci, 2))
assertNotNull(getStaleTime(aci, 3))
}
private fun insertTestRecord(account: ServiceId, id: Int, staleTime: Long = 0, lastResort: Boolean = false) {
val kemKeyPair = KEMKeyPair.generate(KEMKeyType.KYBER_1024)
SignalDatabase.kyberPreKeys.insert(
serviceId = account,
keyId = id,
record = KyberPreKeyRecord(
id,
System.currentTimeMillis(),
kemKeyPair,
Curve.generateKeyPair().privateKey.calculateSignature(kemKeyPair.publicKey.serialize())
),
lastResort = lastResort
)
val count = SignalDatabase.rawDatabase
.update(KyberPreKeyTable.TABLE_NAME)
.values(KyberPreKeyTable.STALE_TIMESTAMP to staleTime)
.where("${KyberPreKeyTable.ACCOUNT_ID} = ? AND ${KyberPreKeyTable.KEY_ID} = $id", account)
.run()
assertEquals(1, count)
}
private fun getStaleTime(account: ServiceId, id: Int): Long? {
return SignalDatabase.rawDatabase
.select(KyberPreKeyTable.STALE_TIMESTAMP)
.from(KyberPreKeyTable.TABLE_NAME)
.where("${KyberPreKeyTable.ACCOUNT_ID} = ? AND ${KyberPreKeyTable.KEY_ID} = $id", account)
.run()
.readToSingleObject { it.requireLongOrNull(KyberPreKeyTable.STALE_TIMESTAMP) }
}
}

View File

@@ -0,0 +1,288 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.database
import org.junit.Test
import org.signal.core.util.forEach
import org.signal.core.util.requireLong
import org.signal.core.util.requireNonNullString
import org.signal.core.util.select
import org.signal.core.util.updateAll
import org.thoughtcrime.securesms.crash.CrashConfig
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.testing.assertIs
class LogDatabaseTest {
private val db: LogDatabase = LogDatabase.getInstance(ApplicationDependencies.getApplication())
@Test
fun crashTable_matchesNamePattern() {
val currentTime = System.currentTimeMillis()
db.crashes.saveCrash(
createdAt = currentTime,
name = "TestName",
message = "Test Message",
stackTrace = "test\nstack\ntrace"
)
val foundMatch = db.crashes.anyMatch(
listOf(
CrashConfig.CrashPattern(namePattern = "Test")
),
promptThreshold = currentTime
)
foundMatch assertIs true
}
@Test
fun crashTable_matchesMessagePattern() {
val currentTime = System.currentTimeMillis()
db.crashes.saveCrash(
createdAt = currentTime,
name = "TestName",
message = "Test Message",
stackTrace = "test\nstack\ntrace"
)
val foundMatch = db.crashes.anyMatch(
listOf(
CrashConfig.CrashPattern(messagePattern = "Message")
),
promptThreshold = currentTime
)
foundMatch assertIs true
}
@Test
fun crashTable_matchesStackTracePattern() {
val currentTime = System.currentTimeMillis()
db.crashes.saveCrash(
createdAt = currentTime,
name = "TestName",
message = "Test Message",
stackTrace = "test\nstack\ntrace"
)
val foundMatch = db.crashes.anyMatch(
listOf(
CrashConfig.CrashPattern(stackTracePattern = "stack")
),
promptThreshold = currentTime
)
foundMatch assertIs true
}
@Test
fun crashTable_matchesNameAndMessagePattern() {
val currentTime = System.currentTimeMillis()
db.crashes.saveCrash(
createdAt = currentTime,
name = "TestName",
message = "Test Message",
stackTrace = "test\nstack\ntrace"
)
val foundMatch = db.crashes.anyMatch(
listOf(
CrashConfig.CrashPattern(namePattern = "Test", messagePattern = "Message")
),
promptThreshold = currentTime
)
foundMatch assertIs true
}
@Test
fun crashTable_matchesNameAndStackTracePattern() {
val currentTime = System.currentTimeMillis()
db.crashes.saveCrash(
createdAt = currentTime,
name = "TestName",
message = "Test Message",
stackTrace = "test\nstack\ntrace"
)
val foundMatch = db.crashes.anyMatch(
listOf(
CrashConfig.CrashPattern(namePattern = "Test", stackTracePattern = "stack")
),
promptThreshold = currentTime
)
foundMatch assertIs true
}
@Test
fun crashTable_matchesNameAndMessageAndStackTracePattern() {
val currentTime = System.currentTimeMillis()
db.crashes.saveCrash(
createdAt = currentTime,
name = "TestName",
message = "Test Message",
stackTrace = "test\nstack\ntrace"
)
val foundMatch = db.crashes.anyMatch(
listOf(
CrashConfig.CrashPattern(namePattern = "Test", messagePattern = "Message", stackTracePattern = "stack")
),
promptThreshold = currentTime
)
foundMatch assertIs true
}
@Test
fun crashTable_doesNotMatchNamePattern() {
val currentTime = System.currentTimeMillis()
db.crashes.saveCrash(
createdAt = currentTime,
name = "TestName",
message = "Test Message",
stackTrace = "test\nstack\ntrace"
)
val foundMatch = db.crashes.anyMatch(
listOf(
CrashConfig.CrashPattern(namePattern = "Blah")
),
promptThreshold = currentTime
)
foundMatch assertIs false
}
@Test
fun crashTable_matchesNameButNotMessagePattern() {
val currentTime = System.currentTimeMillis()
db.crashes.saveCrash(
createdAt = currentTime,
name = "TestName",
message = "Test Message",
stackTrace = "test\nstack\ntrace"
)
val foundMatch = db.crashes.anyMatch(
listOf(
CrashConfig.CrashPattern(namePattern = "Test", messagePattern = "Blah")
),
promptThreshold = currentTime
)
foundMatch assertIs false
}
@Test
fun crashTable_matchesNameButNotStackTracePattern() {
val currentTime = System.currentTimeMillis()
db.crashes.saveCrash(
createdAt = currentTime,
name = "TestName",
message = "Test Message",
stackTrace = "test\nstack\ntrace"
)
val foundMatch = db.crashes.anyMatch(
listOf(
CrashConfig.CrashPattern(namePattern = "Test", stackTracePattern = "Blah")
),
promptThreshold = currentTime
)
foundMatch assertIs false
}
@Test
fun crashTable_matchesNamePatternButPromptedTooRecently() {
val currentTime = System.currentTimeMillis()
db.crashes.saveCrash(
createdAt = currentTime,
name = "TestName",
message = "Test Message",
stackTrace = "test\nstack\ntrace"
)
db.writableDatabase
.updateAll(LogDatabase.CrashTable.TABLE_NAME)
.values(LogDatabase.CrashTable.LAST_PROMPTED_AT to currentTime)
.run()
val foundMatch = db.crashes.anyMatch(
listOf(
CrashConfig.CrashPattern(namePattern = "Test")
),
promptThreshold = currentTime - 100
)
foundMatch assertIs false
}
@Test
fun crashTable_noMatches() {
val currentTime = System.currentTimeMillis()
val foundMatch = db.crashes.anyMatch(
listOf(
CrashConfig.CrashPattern(namePattern = "Test")
),
promptThreshold = currentTime - 100
)
foundMatch assertIs false
}
@Test
fun crashTable_updatesLastPromptTime() {
val currentTime = System.currentTimeMillis()
db.crashes.saveCrash(
createdAt = currentTime,
name = "TestName",
message = "Test Message",
stackTrace = "test\nstack\ntrace"
)
db.crashes.saveCrash(
createdAt = currentTime,
name = "XXX",
message = "XXX",
stackTrace = "XXX"
)
db.crashes.markAsPrompted(
listOf(
CrashConfig.CrashPattern(namePattern = "Test")
),
promptedAt = currentTime
)
db.writableDatabase
.select(LogDatabase.CrashTable.NAME, LogDatabase.CrashTable.LAST_PROMPTED_AT)
.from(LogDatabase.CrashTable.TABLE_NAME)
.run()
.forEach {
if (it.requireNonNullString(LogDatabase.CrashTable.NAME) == "TestName") {
it.requireLong(LogDatabase.CrashTable.LAST_PROMPTED_AT) assertIs currentTime
} else {
it.requireLong(LogDatabase.CrashTable.LAST_PROMPTED_AT) assertIs 0
}
}
}
}

View File

@@ -0,0 +1,187 @@
package org.thoughtcrime.securesms.database
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.thoughtcrime.securesms.database.model.databaseprotos.GiftBadge
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
import org.whispersystems.signalservice.api.push.ServiceId.ACI
import org.whispersystems.signalservice.api.push.ServiceId.PNI
import java.util.UUID
@Suppress("ClassName")
@RunWith(AndroidJUnit4::class)
class MessageTableTest_gifts {
private lateinit var mms: MessageTable
private val localAci = ACI.from(UUID.randomUUID())
private val localPni = PNI.from(UUID.randomUUID())
private lateinit var recipients: List<RecipientId>
@Before
fun setUp() {
mms = SignalDatabase.messages
mms.deleteAllThreads()
SignalStore.account().setAci(localAci)
SignalStore.account().setPni(localPni)
recipients = (0 until 5).map { SignalDatabase.recipients.getOrInsertFromServiceId(ACI.from(UUID.randomUUID())) }
}
@Test
fun givenNoSentGifts_whenISetOutgoingGiftsRevealed_thenIExpectEmptyList() {
val result = mms.setOutgoingGiftsRevealed(listOf(1))
assertTrue(result.isEmpty())
}
@Test
fun givenSentGift_whenISetOutgoingGiftsRevealed_thenIExpectNonEmptyListContainingThatGift() {
val messageId = MmsHelper.insert(
recipient = Recipient.resolved(recipients[0]),
sentTimeMillis = 1,
giftBadge = GiftBadge()
)
val result = mms.setOutgoingGiftsRevealed(listOf(messageId))
assertTrue(result.isNotEmpty())
assertEquals(messageId, result.first().messageId.id)
}
@Test
fun givenViewedSentGift_whenISetOutgoingGiftsRevealed_thenIExpectEmptyList() {
val messageId = MmsHelper.insert(
recipient = Recipient.resolved(recipients[0]),
sentTimeMillis = 1,
giftBadge = GiftBadge()
)
mms.setOutgoingGiftsRevealed(listOf(messageId))
val result = mms.setOutgoingGiftsRevealed(listOf(messageId))
assertTrue(result.isEmpty())
}
@Test
fun givenMultipleSentGift_whenISetOutgoingGiftsRevealedForOne_thenIExpectNonEmptyListContainingThatGift() {
val messageId = MmsHelper.insert(
recipient = Recipient.resolved(recipients[0]),
sentTimeMillis = 1,
giftBadge = GiftBadge()
)
MmsHelper.insert(
recipient = Recipient.resolved(recipients[0]),
sentTimeMillis = 2,
giftBadge = GiftBadge()
)
val result = mms.setOutgoingGiftsRevealed(listOf(messageId))
assertEquals(1, result.size)
assertEquals(messageId, result.first().messageId.id)
}
@Test
fun givenMultipleSentGift_whenISetOutgoingGiftsRevealedForBoth_thenIExpectNonEmptyListContainingThoseGifts() {
val messageId = MmsHelper.insert(
recipient = Recipient.resolved(recipients[0]),
sentTimeMillis = 1,
giftBadge = GiftBadge()
)
val messageId2 = MmsHelper.insert(
recipient = Recipient.resolved(recipients[0]),
sentTimeMillis = 2,
giftBadge = GiftBadge()
)
val result = mms.setOutgoingGiftsRevealed(listOf(messageId, messageId2))
assertEquals(listOf(messageId, messageId2), result.map { it.messageId.id })
}
@Test
fun givenMultipleSentGiftAndNonGift_whenISetOutgoingGiftsRevealedForBothGifts_thenIExpectNonEmptyListContainingJustThoseGifts() {
val messageId = MmsHelper.insert(
recipient = Recipient.resolved(recipients[0]),
sentTimeMillis = 1,
giftBadge = GiftBadge()
)
val messageId2 = MmsHelper.insert(
recipient = Recipient.resolved(recipients[0]),
sentTimeMillis = 2,
giftBadge = GiftBadge()
)
MmsHelper.insert(
recipient = Recipient.resolved(recipients[0]),
sentTimeMillis = 3,
giftBadge = null
)
val result = mms.setOutgoingGiftsRevealed(listOf(messageId, messageId2))
assertEquals(listOf(messageId, messageId2), result.map { it.messageId.id })
}
@Test
fun givenMultipleSentGiftAndNonGift_whenISetOutgoingGiftsRevealedForAllThree_thenIExpectNonEmptyListContainingJustThoseGifts() {
val messageId = MmsHelper.insert(
recipient = Recipient.resolved(recipients[0]),
sentTimeMillis = 1,
giftBadge = GiftBadge()
)
val messageId2 = MmsHelper.insert(
recipient = Recipient.resolved(recipients[0]),
sentTimeMillis = 2,
giftBadge = GiftBadge()
)
val messageId3 = MmsHelper.insert(
recipient = Recipient.resolved(recipients[0]),
sentTimeMillis = 3,
giftBadge = null
)
val result = mms.setOutgoingGiftsRevealed(listOf(messageId, messageId2, messageId3))
assertEquals(listOf(messageId, messageId2), result.map { it.messageId.id })
}
@Test
fun givenMultipleSentGiftAndNonGift_whenISetOutgoingGiftsRevealedForNonGift_thenIExpectEmptyList() {
MmsHelper.insert(
recipient = Recipient.resolved(recipients[0]),
sentTimeMillis = 1,
giftBadge = GiftBadge()
)
MmsHelper.insert(
recipient = Recipient.resolved(recipients[0]),
sentTimeMillis = 2,
giftBadge = GiftBadge()
)
val messageId3 = MmsHelper.insert(
recipient = Recipient.resolved(recipients[0]),
sentTimeMillis = 3,
giftBadge = null
)
val result = mms.setOutgoingGiftsRevealed(listOf(messageId3))
assertTrue(result.isEmpty())
}
}

View File

@@ -3,8 +3,8 @@ package org.thoughtcrime.securesms.database
import org.thoughtcrime.securesms.database.model.ParentStoryId
import org.thoughtcrime.securesms.database.model.StoryType
import org.thoughtcrime.securesms.database.model.databaseprotos.GiftBadge
import org.thoughtcrime.securesms.mms.IncomingMediaMessage
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage
import org.thoughtcrime.securesms.mms.IncomingMessage
import org.thoughtcrime.securesms.mms.OutgoingMessage
import org.thoughtcrime.securesms.recipients.Recipient
import java.util.Optional
@@ -17,7 +17,6 @@ object MmsHelper {
recipient: Recipient = Recipient.UNKNOWN,
body: String = "body",
sentTimeMillis: Long = System.currentTimeMillis(),
subscriptionId: Int = -1,
expiresIn: Long = 0,
viewOnce: Boolean = false,
distributionType: Int = ThreadTable.DistributionTypes.DEFAULT,
@@ -28,11 +27,10 @@ object MmsHelper {
giftBadge: GiftBadge? = null,
secure: Boolean = true
): Long {
val message = OutgoingMediaMessage(
val message = OutgoingMessage(
recipient = recipient,
body = body,
timestamp = sentTimeMillis,
subscriptionId = subscriptionId,
expiresIn = expiresIn,
viewOnce = viewOnce,
distributionType = distributionType,
@@ -50,16 +48,16 @@ object MmsHelper {
}
fun insert(
message: OutgoingMediaMessage,
message: OutgoingMessage,
threadId: Long
): Long {
return SignalDatabase.mms.insertMessageOutbox(message, threadId, false, GroupReceiptTable.STATUS_UNKNOWN, null)
return SignalDatabase.messages.insertMessageOutbox(message, threadId, false, GroupReceiptTable.STATUS_UNKNOWN, null)
}
fun insert(
message: IncomingMediaMessage,
message: IncomingMessage,
threadId: Long
): Optional<MessageTable.InsertResult> {
return SignalDatabase.mms.insertSecureDecryptedMessageInbox(message, threadId)
return SignalDatabase.messages.insertMessageInbox(message, threadId)
}
}

View File

@@ -1,188 +0,0 @@
package org.thoughtcrime.securesms.database
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.thoughtcrime.securesms.database.model.databaseprotos.GiftBadge
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
import org.whispersystems.signalservice.api.push.ACI
import org.whispersystems.signalservice.api.push.PNI
import org.whispersystems.signalservice.api.push.ServiceId
import java.util.UUID
@Suppress("ClassName")
@RunWith(AndroidJUnit4::class)
class MmsTableTest_gifts {
private lateinit var mms: MmsTable
private val localAci = ACI.from(UUID.randomUUID())
private val localPni = PNI.from(UUID.randomUUID())
private lateinit var recipients: List<RecipientId>
@Before
fun setUp() {
mms = SignalDatabase.mms
mms.deleteAllThreads()
SignalStore.account().setAci(localAci)
SignalStore.account().setPni(localPni)
recipients = (0 until 5).map { SignalDatabase.recipients.getOrInsertFromServiceId(ServiceId.from(UUID.randomUUID())) }
}
@Test
fun givenNoSentGifts_whenISetOutgoingGiftsRevealed_thenIExpectEmptyList() {
val result = mms.setOutgoingGiftsRevealed(listOf(1))
assertTrue(result.isEmpty())
}
@Test
fun givenSentGift_whenISetOutgoingGiftsRevealed_thenIExpectNonEmptyListContainingThatGift() {
val messageId = MmsHelper.insert(
recipient = Recipient.resolved(recipients[0]),
sentTimeMillis = 1,
giftBadge = GiftBadge.getDefaultInstance()
)
val result = mms.setOutgoingGiftsRevealed(listOf(messageId))
assertTrue(result.isNotEmpty())
assertEquals(messageId, result.first().messageId.id)
}
@Test
fun givenViewedSentGift_whenISetOutgoingGiftsRevealed_thenIExpectEmptyList() {
val messageId = MmsHelper.insert(
recipient = Recipient.resolved(recipients[0]),
sentTimeMillis = 1,
giftBadge = GiftBadge.getDefaultInstance()
)
mms.setOutgoingGiftsRevealed(listOf(messageId))
val result = mms.setOutgoingGiftsRevealed(listOf(messageId))
assertTrue(result.isEmpty())
}
@Test
fun givenMultipleSentGift_whenISetOutgoingGiftsRevealedForOne_thenIExpectNonEmptyListContainingThatGift() {
val messageId = MmsHelper.insert(
recipient = Recipient.resolved(recipients[0]),
sentTimeMillis = 1,
giftBadge = GiftBadge.getDefaultInstance()
)
MmsHelper.insert(
recipient = Recipient.resolved(recipients[0]),
sentTimeMillis = 1,
giftBadge = GiftBadge.getDefaultInstance()
)
val result = mms.setOutgoingGiftsRevealed(listOf(messageId))
assertEquals(1, result.size)
assertEquals(messageId, result.first().messageId.id)
}
@Test
fun givenMultipleSentGift_whenISetOutgoingGiftsRevealedForBoth_thenIExpectNonEmptyListContainingThoseGifts() {
val messageId = MmsHelper.insert(
recipient = Recipient.resolved(recipients[0]),
sentTimeMillis = 1,
giftBadge = GiftBadge.getDefaultInstance()
)
val messageId2 = MmsHelper.insert(
recipient = Recipient.resolved(recipients[0]),
sentTimeMillis = 1,
giftBadge = GiftBadge.getDefaultInstance()
)
val result = mms.setOutgoingGiftsRevealed(listOf(messageId, messageId2))
assertEquals(listOf(messageId, messageId2), result.map { it.messageId.id })
}
@Test
fun givenMultipleSentGiftAndNonGift_whenISetOutgoingGiftsRevealedForBothGifts_thenIExpectNonEmptyListContainingJustThoseGifts() {
val messageId = MmsHelper.insert(
recipient = Recipient.resolved(recipients[0]),
sentTimeMillis = 1,
giftBadge = GiftBadge.getDefaultInstance()
)
val messageId2 = MmsHelper.insert(
recipient = Recipient.resolved(recipients[0]),
sentTimeMillis = 1,
giftBadge = GiftBadge.getDefaultInstance()
)
MmsHelper.insert(
recipient = Recipient.resolved(recipients[0]),
sentTimeMillis = 1,
giftBadge = null
)
val result = mms.setOutgoingGiftsRevealed(listOf(messageId, messageId2))
assertEquals(listOf(messageId, messageId2), result.map { it.messageId.id })
}
@Test
fun givenMultipleSentGiftAndNonGift_whenISetOutgoingGiftsRevealedForAllThree_thenIExpectNonEmptyListContainingJustThoseGifts() {
val messageId = MmsHelper.insert(
recipient = Recipient.resolved(recipients[0]),
sentTimeMillis = 1,
giftBadge = GiftBadge.getDefaultInstance()
)
val messageId2 = MmsHelper.insert(
recipient = Recipient.resolved(recipients[0]),
sentTimeMillis = 1,
giftBadge = GiftBadge.getDefaultInstance()
)
val messageId3 = MmsHelper.insert(
recipient = Recipient.resolved(recipients[0]),
sentTimeMillis = 1,
giftBadge = null
)
val result = mms.setOutgoingGiftsRevealed(listOf(messageId, messageId2, messageId3))
assertEquals(listOf(messageId, messageId2), result.map { it.messageId.id })
}
@Test
fun givenMultipleSentGiftAndNonGift_whenISetOutgoingGiftsRevealedForNonGift_thenIExpectEmptyList() {
MmsHelper.insert(
recipient = Recipient.resolved(recipients[0]),
sentTimeMillis = 1,
giftBadge = GiftBadge.getDefaultInstance()
)
MmsHelper.insert(
recipient = Recipient.resolved(recipients[0]),
sentTimeMillis = 1,
giftBadge = GiftBadge.getDefaultInstance()
)
val messageId3 = MmsHelper.insert(
recipient = Recipient.resolved(recipients[0]),
sentTimeMillis = 1,
giftBadge = null
)
val result = mms.setOutgoingGiftsRevealed(listOf(messageId3))
assertTrue(result.isEmpty())
}
}

View File

@@ -13,12 +13,11 @@ import org.thoughtcrime.securesms.database.model.DistributionListId
import org.thoughtcrime.securesms.database.model.ParentStoryId
import org.thoughtcrime.securesms.database.model.StoryType
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.mms.IncomingMediaMessage
import org.thoughtcrime.securesms.mms.IncomingMessage
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
import org.whispersystems.signalservice.api.push.ACI
import org.whispersystems.signalservice.api.push.PNI
import org.whispersystems.signalservice.api.push.ServiceId
import org.whispersystems.signalservice.api.push.ServiceId.ACI
import org.whispersystems.signalservice.api.push.ServiceId.PNI
import java.util.UUID
import java.util.concurrent.TimeUnit
@@ -26,7 +25,7 @@ import java.util.concurrent.TimeUnit
@RunWith(AndroidJUnit4::class)
class MmsTableTest_stories {
private lateinit var mms: MmsTable
private lateinit var mms: MessageTable
private val localAci = ACI.from(UUID.randomUUID())
private val localPni = PNI.from(UUID.randomUUID())
@@ -37,7 +36,7 @@ class MmsTableTest_stories {
@Before
fun setUp() {
mms = SignalDatabase.mms
mms = SignalDatabase.messages
mms.deleteAllThreads()
@@ -45,7 +44,7 @@ class MmsTableTest_stories {
SignalStore.account().setPni(localPni)
myStory = Recipient.resolved(SignalDatabase.recipients.getOrInsertFromDistributionListId(DistributionListId.MY_STORY))
recipients = (0 until 5).map { SignalDatabase.recipients.getOrInsertFromServiceId(ServiceId.from(UUID.randomUUID())) }
recipients = (0 until 5).map { SignalDatabase.recipients.getOrInsertFromServiceId(ACI.from(UUID.randomUUID())) }
releaseChannelRecipient = Recipient.resolved(SignalDatabase.recipients.insertReleaseChannelRecipient())
SignalStore.releaseChannelValues().setReleaseChannelRecipientId(releaseChannelRecipient.id)
@@ -74,7 +73,8 @@ class MmsTableTest_stories {
)
MmsHelper.insert(
IncomingMediaMessage(
IncomingMessage(
type = MessageType.NORMAL,
from = sender,
sentTimeMillis = 2,
serverTimeMillis = 2,
@@ -96,7 +96,8 @@ class MmsTableTest_stories {
// GIVEN
val sender = recipients[0]
val messageId = MmsHelper.insert(
IncomingMediaMessage(
IncomingMessage(
type = MessageType.NORMAL,
from = sender,
sentTimeMillis = 2,
serverTimeMillis = 2,
@@ -106,14 +107,14 @@ class MmsTableTest_stories {
-1L
).get().messageId
val messageBeforeMark = SignalDatabase.mms.getMessageRecord(messageId)
val messageBeforeMark = SignalDatabase.messages.getMessageRecord(messageId)
assertFalse(messageBeforeMark.incomingStoryViewedAtTimestamp > 0)
// WHEN
SignalDatabase.mms.setIncomingMessageViewed(messageId)
SignalDatabase.messages.setIncomingMessageViewed(messageId)
// THEN
val messageAfterMark = SignalDatabase.mms.getMessageRecord(messageId)
val messageAfterMark = SignalDatabase.messages.getMessageRecord(messageId)
assertTrue(messageAfterMark.incomingStoryViewedAtTimestamp > 0)
}
@@ -123,12 +124,13 @@ class MmsTableTest_stories {
// GIVEN
val messageIds = recipients.take(5).map {
MmsHelper.insert(
IncomingMediaMessage(
IncomingMessage(
type = MessageType.NORMAL,
from = it,
sentTimeMillis = 2,
serverTimeMillis = 2,
receivedTimeMillis = 2,
storyType = StoryType.STORY_WITH_REPLIES,
storyType = StoryType.STORY_WITH_REPLIES
),
-1L
).get().messageId
@@ -136,12 +138,12 @@ class MmsTableTest_stories {
val randomizedOrderedIds = messageIds.shuffled()
randomizedOrderedIds.forEach {
SignalDatabase.mms.setIncomingMessageViewed(it)
SignalDatabase.messages.setIncomingMessageViewed(it)
Thread.sleep(5)
}
// WHEN
val result = SignalDatabase.mms.getOrderedStoryRecipientsAndIds(false)
val result = SignalDatabase.messages.getOrderedStoryRecipientsAndIds(false)
val resultOrderedIds = result.map { it.messageId }
// THEN
@@ -155,12 +157,13 @@ class MmsTableTest_stories {
val unviewedIds: List<Long> = (0 until 5).map {
Thread.sleep(5)
MmsHelper.insert(
IncomingMediaMessage(
IncomingMessage(
type = MessageType.NORMAL,
from = recipients[it],
sentTimeMillis = System.currentTimeMillis(),
serverTimeMillis = 2,
receivedTimeMillis = 2,
storyType = StoryType.STORY_WITH_REPLIES,
storyType = StoryType.STORY_WITH_REPLIES
),
-1L
).get().messageId
@@ -169,12 +172,13 @@ class MmsTableTest_stories {
val viewedIds: List<Long> = (0 until 5).map {
Thread.sleep(5)
MmsHelper.insert(
IncomingMediaMessage(
IncomingMessage(
type = MessageType.NORMAL,
from = recipients[it],
sentTimeMillis = System.currentTimeMillis(),
serverTimeMillis = 2,
receivedTimeMillis = 2,
storyType = StoryType.STORY_WITH_REPLIES,
storyType = StoryType.STORY_WITH_REPLIES
),
-1L
).get().messageId
@@ -183,7 +187,7 @@ class MmsTableTest_stories {
val interspersedIds: List<Long> = (0 until 10).map {
Thread.sleep(5)
if (it % 2 == 0) {
SignalDatabase.mms.setIncomingMessageViewed(viewedIds[it / 2])
SignalDatabase.messages.setIncomingMessageViewed(viewedIds[it / 2])
viewedIds[it / 2]
} else {
MmsHelper.insert(
@@ -195,7 +199,7 @@ class MmsTableTest_stories {
}
}
val result = SignalDatabase.mms.getOrderedStoryRecipientsAndIds(false)
val result = SignalDatabase.messages.getOrderedStoryRecipientsAndIds(false)
val resultOrderedIds = result.map { it.messageId }
assertEquals(unviewedIds.reversed() + interspersedIds.reversed(), resultOrderedIds)
@@ -214,12 +218,13 @@ class MmsTableTest_stories {
fun givenNoOutgoingStories_whenICheckIsOutgoingStoryAlreadyInDatabase_thenIExpectFalse() {
// GIVEN
MmsHelper.insert(
IncomingMediaMessage(
IncomingMessage(
type = MessageType.NORMAL,
from = recipients[0],
sentTimeMillis = 200,
serverTimeMillis = 2,
receivedTimeMillis = 2,
storyType = StoryType.STORY_WITH_REPLIES,
storyType = StoryType.STORY_WITH_REPLIES
),
-1L
)
@@ -253,8 +258,7 @@ class MmsTableTest_stories {
val groupStoryId = MmsHelper.insert(
recipient = myStory,
sentTimeMillis = 200,
storyType = StoryType.STORY_WITH_REPLIES,
threadId = -1L
storyType = StoryType.STORY_WITH_REPLIES
)
// WHEN
@@ -319,19 +323,19 @@ class MmsTableTest_stories {
val groupStoryId = MmsHelper.insert(
recipient = myStory,
sentTimeMillis = 200,
storyType = StoryType.STORY_WITH_REPLIES,
threadId = -1L
storyType = StoryType.STORY_WITH_REPLIES
)
MmsHelper.insert(
IncomingMediaMessage(
IncomingMessage(
type = MessageType.NORMAL,
from = myStory.id,
sentTimeMillis = 201,
serverTimeMillis = 201,
receivedTimeMillis = 202,
parentStoryId = ParentStoryId.GroupReply(groupStoryId)
),
-1
SignalDatabase.threads.getOrCreateThreadIdFor(myStory, ThreadTable.DistributionTypes.DEFAULT)
)
// WHEN
@@ -353,7 +357,7 @@ class MmsTableTest_stories {
)
// WHEN
val oldestTimestamp = SignalDatabase.mms.getOldestStorySendTimestamp(false)
val oldestTimestamp = SignalDatabase.messages.getOldestStorySendTimestamp(false)
// THEN
assertNull(oldestTimestamp)
@@ -372,7 +376,7 @@ class MmsTableTest_stories {
)
// WHEN
val oldestTimestamp = SignalDatabase.mms.getOldestStorySendTimestamp(true)
val oldestTimestamp = SignalDatabase.messages.getOldestStorySendTimestamp(true)
// THEN
assertEquals(expected, oldestTimestamp)

View File

@@ -0,0 +1,137 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.database
import junit.framework.TestCase.assertEquals
import junit.framework.TestCase.assertNotNull
import junit.framework.TestCase.assertNull
import org.junit.Test
import org.signal.core.util.readToSingleObject
import org.signal.core.util.requireLongOrNull
import org.signal.core.util.select
import org.signal.core.util.update
import org.signal.libsignal.protocol.ecc.Curve
import org.signal.libsignal.protocol.state.PreKeyRecord
import org.whispersystems.signalservice.api.push.ServiceId
import org.whispersystems.signalservice.api.push.ServiceId.ACI
import org.whispersystems.signalservice.api.push.ServiceId.PNI
import java.util.UUID
class OneTimePreKeyTableTest {
private val aci: ACI = ACI.from(UUID.randomUUID())
private val pni: PNI = PNI.from(UUID.randomUUID())
@Test
fun markAllStaleIfNecessary_onlyUpdatesMatchingAccountAndZeroValues() {
insertTestRecord(aci, id = 1)
insertTestRecord(aci, id = 2)
insertTestRecord(aci, id = 3, staleTime = 42)
insertTestRecord(pni, id = 4)
val now = System.currentTimeMillis()
SignalDatabase.oneTimePreKeys.markAllStaleIfNecessary(aci, now)
assertEquals(now, getStaleTime(aci, 1))
assertEquals(now, getStaleTime(aci, 2))
assertEquals(42L, getStaleTime(aci, 3))
assertEquals(0L, getStaleTime(pni, 4))
}
@Test
fun deleteAllStaleBefore_deleteOldBeforeThreshold() {
insertTestRecord(aci, id = 1, staleTime = 10)
insertTestRecord(aci, id = 2, staleTime = 10)
insertTestRecord(aci, id = 3, staleTime = 10)
insertTestRecord(aci, id = 4, staleTime = 15)
insertTestRecord(aci, id = 5, staleTime = 0)
SignalDatabase.oneTimePreKeys.deleteAllStaleBefore(aci, threshold = 11, minCount = 0)
assertNull(getStaleTime(aci, 1))
assertNull(getStaleTime(aci, 2))
assertNull(getStaleTime(aci, 3))
assertNotNull(getStaleTime(aci, 4))
assertNotNull(getStaleTime(aci, 5))
}
@Test
fun deleteAllStaleBefore_neverDeleteStaleOfZero() {
insertTestRecord(aci, id = 1, staleTime = 0)
insertTestRecord(aci, id = 2, staleTime = 0)
insertTestRecord(aci, id = 3, staleTime = 0)
insertTestRecord(aci, id = 4, staleTime = 0)
insertTestRecord(aci, id = 5, staleTime = 0)
SignalDatabase.oneTimePreKeys.deleteAllStaleBefore(aci, threshold = 10, minCount = 0)
assertNotNull(getStaleTime(aci, 1))
assertNotNull(getStaleTime(aci, 2))
assertNotNull(getStaleTime(aci, 3))
assertNotNull(getStaleTime(aci, 4))
assertNotNull(getStaleTime(aci, 5))
}
@Test
fun deleteAllStaleBefore_respectMinCount() {
insertTestRecord(aci, id = 1, staleTime = 10)
insertTestRecord(aci, id = 2, staleTime = 10)
insertTestRecord(aci, id = 3, staleTime = 10)
insertTestRecord(aci, id = 4, staleTime = 10)
insertTestRecord(aci, id = 5, staleTime = 10)
SignalDatabase.oneTimePreKeys.deleteAllStaleBefore(aci, threshold = 11, minCount = 3)
assertNull(getStaleTime(aci, 1))
assertNull(getStaleTime(aci, 2))
assertNotNull(getStaleTime(aci, 3))
assertNotNull(getStaleTime(aci, 4))
assertNotNull(getStaleTime(aci, 5))
}
@Test
fun deleteAllStaleBefore_respectAccount() {
insertTestRecord(aci, id = 1, staleTime = 10)
insertTestRecord(aci, id = 2, staleTime = 10)
insertTestRecord(aci, id = 3, staleTime = 10)
insertTestRecord(pni, id = 4, staleTime = 10)
insertTestRecord(pni, id = 5, staleTime = 10)
SignalDatabase.oneTimePreKeys.deleteAllStaleBefore(aci, threshold = 11, minCount = 2)
assertNull(getStaleTime(aci, 1))
assertNotNull(getStaleTime(aci, 2))
assertNotNull(getStaleTime(aci, 3))
assertNotNull(getStaleTime(pni, 4))
assertNotNull(getStaleTime(pni, 5))
}
private fun insertTestRecord(account: ServiceId, id: Int, staleTime: Long = 0) {
SignalDatabase.oneTimePreKeys.insert(
serviceId = account,
keyId = id,
record = PreKeyRecord(id, Curve.generateKeyPair())
)
val count = SignalDatabase.rawDatabase
.update(OneTimePreKeyTable.TABLE_NAME)
.values(OneTimePreKeyTable.STALE_TIMESTAMP to staleTime)
.where("${OneTimePreKeyTable.ACCOUNT_ID} = ? AND ${OneTimePreKeyTable.KEY_ID} = $id", account)
.run()
assertEquals(1, count)
}
private fun getStaleTime(account: ServiceId, id: Int): Long? {
return SignalDatabase.rawDatabase
.select(OneTimePreKeyTable.STALE_TIMESTAMP)
.from(OneTimePreKeyTable.TABLE_NAME)
.where("${OneTimePreKeyTable.ACCOUNT_ID} = ? AND ${OneTimePreKeyTable.KEY_ID} = $id", account)
.run()
.readToSingleObject { it.requireLongOrNull(OneTimePreKeyTable.STALE_TIMESTAMP) }
}
}

View File

@@ -11,6 +11,9 @@ import org.signal.core.util.CursorUtil
import org.thoughtcrime.securesms.profiles.ProfileName
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.testing.SignalActivityRule
import org.whispersystems.signalservice.api.push.ServiceId.ACI
import org.whispersystems.signalservice.api.push.ServiceId.PNI
import java.util.UUID
@RunWith(AndroidJUnit4::class)
class RecipientTableTest {
@@ -19,14 +22,14 @@ class RecipientTableTest {
val harness = SignalActivityRule()
@Test
fun givenAHiddenRecipient_whenIQueryAllContacts_thenIDoNotExpectHiddenToBeReturned() {
fun givenAHiddenRecipient_whenIQueryAllContacts_thenIExpectHiddenToBeReturned() {
val hiddenRecipient = harness.others[0]
SignalDatabase.recipients.setProfileName(hiddenRecipient, ProfileName.fromParts("Hidden", "Person"))
SignalDatabase.recipients.markHidden(hiddenRecipient)
val results = SignalDatabase.recipients.queryAllContacts("Hidden")!!
assertEquals(0, results.count)
assertEquals(1, results.count)
}
@Test
@@ -54,7 +57,7 @@ class RecipientTableTest {
SignalDatabase.recipients.setProfileName(hiddenRecipient, ProfileName.fromParts("Hidden", "Person"))
SignalDatabase.recipients.markHidden(hiddenRecipient)
val results = SignalDatabase.recipients.querySignalContacts("Hidden", false)!!
val results = SignalDatabase.recipients.querySignalContacts(RecipientTable.ContactSearchQuery("Hidden", false))!!
assertEquals(0, results.count)
}
@@ -125,7 +128,7 @@ class RecipientTableTest {
SignalDatabase.recipients.setProfileName(blockedRecipient, ProfileName.fromParts("Blocked", "Person"))
SignalDatabase.recipients.setBlocked(blockedRecipient, true)
val results = SignalDatabase.recipients.querySignalContacts("Blocked", false)!!
val results = SignalDatabase.recipients.querySignalContacts(RecipientTable.ContactSearchQuery("Blocked", false))!!
assertEquals(0, results.count)
}
@@ -159,4 +162,43 @@ class RecipientTableTest {
assertNotEquals(0, results.size)
assertFalse(blockedRecipient in results)
}
@Test
fun givenARecipientWithPniAndAci_whenIMarkItUnregistered_thenIExpectItToBeSplit() {
val mainId = SignalDatabase.recipients.getAndPossiblyMerge(ACI_A, PNI_A, E164_A)
SignalDatabase.recipients.markUnregistered(mainId)
val byAci: RecipientId = SignalDatabase.recipients.getByAci(ACI_A).get()
val byE164: RecipientId = SignalDatabase.recipients.getByE164(E164_A).get()
val byPni: RecipientId = SignalDatabase.recipients.getByPni(PNI_A).get()
assertEquals(mainId, byAci)
assertEquals(byE164, byPni)
assertNotEquals(byAci, byE164)
}
@Test
fun givenARecipientWithPniAndAci_whenISplitItForStorageSync_thenIExpectItToBeSplit() {
val mainId = SignalDatabase.recipients.getAndPossiblyMerge(ACI_A, PNI_A, E164_A)
val mainRecord = SignalDatabase.recipients.getRecord(mainId)
SignalDatabase.recipients.splitForStorageSyncIfNecessary(mainRecord.aci!!)
val byAci: RecipientId = SignalDatabase.recipients.getByAci(ACI_A).get()
val byE164: RecipientId = SignalDatabase.recipients.getByE164(E164_A).get()
val byPni: RecipientId = SignalDatabase.recipients.getByPni(PNI_A).get()
assertEquals(mainId, byAci)
assertEquals(byE164, byPni)
assertNotEquals(byAci, byE164)
}
companion object {
val ACI_A = ACI.from(UUID.fromString("aaaa0000-5a76-47fa-a98a-7e72c948a82e"))
val PNI_A = PNI.from(UUID.fromString("aaaa1111-c960-4f6c-8385-671ad2ffb999"))
const val E164_A = "+12222222222"
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.database
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.storage.StorageRecordUpdate
import org.thoughtcrime.securesms.storage.StorageSyncModels
import org.thoughtcrime.securesms.testing.SignalActivityRule
import org.thoughtcrime.securesms.testing.assertIs
import org.thoughtcrime.securesms.util.MessageTableTestUtils
import org.whispersystems.signalservice.api.storage.SignalContactRecord
import org.whispersystems.signalservice.internal.storage.protos.ContactRecord
@Suppress("ClassName")
@RunWith(AndroidJUnit4::class)
class RecipientTableTest_applyStorageSyncContactUpdate {
@get:Rule
val harness = SignalActivityRule()
@Test
fun insertMessageOnVerifiedToDefault() {
// GIVEN
val identities = ApplicationDependencies.getProtocolStore().aci().identities()
val other = Recipient.resolved(harness.others[0])
MmsHelper.insert(recipient = other)
identities.setVerified(other.id, harness.othersKeys[0].publicKey, IdentityTable.VerifiedStatus.VERIFIED)
val oldRecord: SignalContactRecord = StorageSyncModels.localToRemoteRecord(SignalDatabase.recipients.getRecordForSync(harness.others[0])!!).contact.get()
val newProto = oldRecord
.toProto()
.newBuilder()
.identityState(ContactRecord.IdentityState.DEFAULT)
.build()
val newRecord = SignalContactRecord(oldRecord.id, newProto)
val update = StorageRecordUpdate<SignalContactRecord>(oldRecord, newRecord)
// WHEN
val oldVerifiedStatus: IdentityTable.VerifiedStatus = identities.getIdentityRecord(other.id).get().verifiedStatus
SignalDatabase.recipients.applyStorageSyncContactUpdate(update)
val newVerifiedStatus: IdentityTable.VerifiedStatus = identities.getIdentityRecord(other.id).get().verifiedStatus
// THEN
oldVerifiedStatus assertIs IdentityTable.VerifiedStatus.VERIFIED
newVerifiedStatus assertIs IdentityTable.VerifiedStatus.DEFAULT
val messages = MessageTableTestUtils.getMessages(SignalDatabase.threads.getThreadIdFor(other.id)!!)
messages.first().isIdentityDefault assertIs true
}
}

View File

@@ -1,842 +0,0 @@
package org.thoughtcrime.securesms.database
import androidx.core.content.contentValuesOf
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.testing.SignalDatabaseRule
import org.thoughtcrime.securesms.util.Util
import org.whispersystems.signalservice.api.push.ACI
import org.whispersystems.signalservice.api.push.PNI
import org.whispersystems.signalservice.api.push.ServiceId
import java.lang.AssertionError
import java.lang.IllegalStateException
import java.util.UUID
@RunWith(AndroidJUnit4::class)
class RecipientTableTest_processPnpTupleToChangeSet {
@Rule
@JvmField
val databaseRule = SignalDatabaseRule(deleteAllThreadsOnEachRun = false)
private lateinit var db: RecipientTable
@Before
fun setup() {
db = SignalDatabase.recipients
}
@Test
fun noMatch_e164Only() {
val changeSet = db.processPnpTupleToChangeSet(E164_A, null, null, pniVerified = false)
assertEquals(
PnpChangeSet(
id = PnpIdResolver.PnpInsert(E164_A, null, null)
),
changeSet
)
}
@Test
fun noMatch_e164AndPni() {
val changeSet = db.processPnpTupleToChangeSet(E164_A, PNI_A, null, pniVerified = false)
assertEquals(
PnpChangeSet(
id = PnpIdResolver.PnpInsert(E164_A, PNI_A, null)
),
changeSet
)
}
@Test
fun noMatch_aciOnly() {
val changeSet = db.processPnpTupleToChangeSet(null, null, ACI_A, pniVerified = false)
assertEquals(
PnpChangeSet(
id = PnpIdResolver.PnpInsert(null, null, ACI_A)
),
changeSet
)
}
@Test(expected = IllegalStateException::class)
fun noMatch_noData() {
db.processPnpTupleToChangeSet(null, null, null, pniVerified = false)
}
@Test
fun noMatch_allFields() {
val changeSet = db.processPnpTupleToChangeSet(E164_A, PNI_A, ACI_A, pniVerified = false)
assertEquals(
PnpChangeSet(
id = PnpIdResolver.PnpInsert(E164_A, PNI_A, ACI_A)
),
changeSet
)
}
@Test
fun fullMatch() {
val result = applyAndAssert(
Input(E164_A, PNI_A, ACI_A),
Update(E164_A, PNI_A, ACI_A),
Output(E164_A, PNI_A, ACI_A)
)
assertEquals(
PnpChangeSet(
id = PnpIdResolver.PnpNoopId(result.id)
),
result.changeSet
)
}
@Test
fun onlyE164Matches() {
val result = applyAndAssert(
Input(E164_A, null, null),
Update(E164_A, PNI_A, ACI_A),
Output(E164_A, PNI_A, ACI_A)
)
assertEquals(
PnpChangeSet(
id = PnpIdResolver.PnpNoopId(result.id),
operations = listOf(
PnpOperation.SetPni(result.id, PNI_A),
PnpOperation.SetAci(result.id, ACI_A)
)
),
result.changeSet
)
}
@Test
fun onlyE164Matches_pniChanges_noAciProvided_existingPniSession() {
val result = applyAndAssert(
Input(E164_A, PNI_B, null, pniSession = true),
Update(E164_A, PNI_A, null),
Output(E164_A, PNI_A, null)
)
assertEquals(
PnpChangeSet(
id = PnpIdResolver.PnpNoopId(result.id),
operations = listOf(
PnpOperation.SetPni(result.id, PNI_A),
PnpOperation.SessionSwitchoverInsert(result.id)
)
),
result.changeSet
)
}
@Test
fun onlyE164Matches_pniChanges_noAciProvided_noPniSession() {
val result = applyAndAssert(
Input(E164_A, PNI_B, null),
Update(E164_A, PNI_A, null),
Output(E164_A, PNI_A, null)
)
assertEquals(
PnpChangeSet(
id = PnpIdResolver.PnpNoopId(result.id),
operations = listOf(
PnpOperation.SetPni(result.id, PNI_A)
)
),
result.changeSet
)
}
@Test
fun e164AndPniMatches_noExistingSession() {
val result = applyAndAssert(
Input(E164_A, PNI_A, null),
Update(E164_A, PNI_A, ACI_A),
Output(E164_A, PNI_A, ACI_A)
)
assertEquals(
PnpChangeSet(
id = PnpIdResolver.PnpNoopId(result.id),
operations = listOf(
PnpOperation.SetAci(result.id, ACI_A)
)
),
result.changeSet
)
}
@Test
fun e164AndPniMatches_existingPniSession() {
val result = applyAndAssert(
Input(E164_A, PNI_A, null, pniSession = true),
Update(E164_A, PNI_A, ACI_A),
Output(E164_A, PNI_A, ACI_A)
)
assertEquals(
PnpChangeSet(
id = PnpIdResolver.PnpNoopId(result.id),
operations = listOf(
PnpOperation.SetAci(result.id, ACI_A),
PnpOperation.SessionSwitchoverInsert(result.id)
)
),
result.changeSet
)
}
@Test
fun e164AndAciMatches() {
val result = applyAndAssert(
Input(E164_A, null, ACI_A),
Update(E164_A, PNI_A, ACI_A),
Output(E164_A, PNI_A, ACI_A)
)
assertEquals(
PnpChangeSet(
id = PnpIdResolver.PnpNoopId(result.id),
operations = listOf(
PnpOperation.SetPni(result.id, PNI_A)
)
),
result.changeSet
)
}
@Test
fun onlyPniMatches_noExistingSession() {
val result = applyAndAssert(
Input(null, PNI_A, null),
Update(E164_A, PNI_A, ACI_A),
Output(E164_A, PNI_A, ACI_A)
)
assertEquals(
PnpChangeSet(
id = PnpIdResolver.PnpNoopId(result.id),
operations = listOf(
PnpOperation.SetE164(result.id, E164_A),
PnpOperation.SetAci(result.id, ACI_A)
)
),
result.changeSet
)
}
@Test
fun onlyPniMatches_existingPniSession() {
val result = applyAndAssert(
Input(null, PNI_A, null, pniSession = true),
Update(E164_A, PNI_A, ACI_A),
Output(E164_A, PNI_A, ACI_A)
)
assertEquals(
PnpChangeSet(
id = PnpIdResolver.PnpNoopId(result.id),
operations = listOf(
PnpOperation.SetE164(result.id, E164_A),
PnpOperation.SetAci(result.id, ACI_A),
PnpOperation.SessionSwitchoverInsert(result.id)
)
),
result.changeSet
)
}
@Test
fun onlyPniMatches_existingPniSession_changeNumber() {
val result = applyAndAssert(
Input(E164_B, PNI_A, null, pniSession = true),
Update(E164_A, PNI_A, ACI_A),
Output(E164_A, PNI_A, ACI_A)
)
assertEquals(
PnpChangeSet(
id = PnpIdResolver.PnpNoopId(result.id),
operations = listOf(
PnpOperation.SetE164(result.id, E164_A),
PnpOperation.SetAci(result.id, ACI_A),
PnpOperation.ChangeNumberInsert(
recipientId = result.id,
oldE164 = E164_B,
newE164 = E164_A
),
PnpOperation.SessionSwitchoverInsert(result.id)
)
),
result.changeSet
)
}
@Test
fun pniAndAciMatches() {
val result = applyAndAssert(
Input(null, PNI_A, ACI_A),
Update(E164_A, PNI_A, ACI_A),
Output(E164_A, PNI_A, ACI_A)
)
assertEquals(
PnpChangeSet(
id = PnpIdResolver.PnpNoopId(result.id),
operations = listOf(
PnpOperation.SetE164(result.id, E164_A),
)
),
result.changeSet
)
}
@Test
fun pniAndAciMatches_changeNumber() {
val result = applyAndAssert(
Input(E164_B, PNI_A, ACI_A),
Update(E164_A, PNI_A, ACI_A),
Output(E164_A, PNI_A, ACI_A)
)
assertEquals(
PnpChangeSet(
id = PnpIdResolver.PnpNoopId(result.id),
operations = listOf(
PnpOperation.SetE164(result.id, E164_A),
PnpOperation.ChangeNumberInsert(
recipientId = result.id,
oldE164 = E164_B,
newE164 = E164_A
)
)
),
result.changeSet
)
}
@Test
fun onlyAciMatches() {
val result = applyAndAssert(
Input(null, null, ACI_A),
Update(E164_A, PNI_A, ACI_A),
Output(E164_A, PNI_A, ACI_A)
)
assertEquals(
PnpChangeSet(
id = PnpIdResolver.PnpNoopId(result.id),
operations = listOf(
PnpOperation.SetE164(result.id, E164_A),
PnpOperation.SetPni(result.id, PNI_A)
)
),
result.changeSet
)
}
@Test
fun onlyAciMatches_changeNumber() {
val result = applyAndAssert(
Input(E164_B, null, ACI_A),
Update(E164_A, PNI_A, ACI_A),
Output(E164_A, PNI_A, ACI_A)
)
assertEquals(
PnpChangeSet(
id = PnpIdResolver.PnpNoopId(result.id),
operations = listOf(
PnpOperation.SetE164(result.id, E164_A),
PnpOperation.SetPni(result.id, PNI_A),
PnpOperation.ChangeNumberInsert(
recipientId = result.id,
oldE164 = E164_B,
newE164 = E164_A
)
)
),
result.changeSet
)
}
@Test
fun merge_e164Only_pniOnly_aciOnly() {
val result = applyAndAssert(
listOf(
Input(E164_A, null, null),
Input(null, PNI_A, null),
Input(null, null, ACI_A)
),
Update(E164_A, PNI_A, ACI_A),
Output(E164_A, PNI_A, ACI_A)
)
assertEquals(
PnpChangeSet(
id = PnpIdResolver.PnpNoopId(result.thirdId),
operations = listOf(
PnpOperation.Merge(
primaryId = result.firstId,
secondaryId = result.secondId
),
PnpOperation.Merge(
primaryId = result.thirdId,
secondaryId = result.firstId
)
)
),
result.changeSet
)
}
@Test
fun merge_e164Only_pniOnly_noAciProvided() {
val result = applyAndAssert(
listOf(
Input(E164_A, null, null),
Input(null, PNI_A, null),
),
Update(E164_A, PNI_A, null),
Output(E164_A, PNI_A, null)
)
assertEquals(
PnpChangeSet(
id = PnpIdResolver.PnpNoopId(result.firstId),
operations = listOf(
PnpOperation.Merge(
primaryId = result.firstId,
secondaryId = result.secondId
)
)
),
result.changeSet
)
}
@Test
fun merge_e164Only_pniOnly_aciProvidedButNoAciRecord() {
val result = applyAndAssert(
listOf(
Input(E164_A, null, null),
Input(null, PNI_A, null),
),
Update(E164_A, PNI_A, ACI_A),
Output(E164_A, PNI_A, ACI_A)
)
assertEquals(
PnpChangeSet(
id = PnpIdResolver.PnpNoopId(result.firstId),
operations = listOf(
PnpOperation.Merge(
primaryId = result.firstId,
secondaryId = result.secondId
),
PnpOperation.SetAci(
recipientId = result.firstId,
aci = ACI_A
)
)
),
result.changeSet
)
}
@Test
fun merge_e164Only_pniAndE164_noAciProvided() {
val result = applyAndAssert(
listOf(
Input(E164_A, null, null),
Input(E164_B, PNI_A, null),
),
Update(E164_A, PNI_A, null),
Output(E164_A, PNI_A, null)
)
assertEquals(
PnpChangeSet(
id = PnpIdResolver.PnpNoopId(result.firstId),
operations = listOf(
PnpOperation.RemovePni(result.secondId),
PnpOperation.SetPni(
recipientId = result.firstId,
pni = PNI_A
),
)
),
result.changeSet
)
}
@Test
fun merge_e164AndPni_pniOnly_noAciProvided() {
val result = applyAndAssert(
listOf(
Input(E164_A, PNI_B, null),
Input(null, PNI_A, null),
),
Update(E164_A, PNI_A, null),
Output(E164_A, PNI_A, null)
)
assertEquals(
PnpChangeSet(
id = PnpIdResolver.PnpNoopId(result.firstId),
operations = listOf(
PnpOperation.RemovePni(result.firstId),
PnpOperation.Merge(
primaryId = result.firstId,
secondaryId = result.secondId
),
)
),
result.changeSet
)
}
@Test
fun merge_e164AndPni_e164AndPni_noAciProvided_noSessions() {
val result = applyAndAssert(
listOf(
Input(E164_A, PNI_B, null),
Input(E164_B, PNI_A, null),
),
Update(E164_A, PNI_A, null),
Output(E164_A, PNI_A, null)
)
assertEquals(
PnpChangeSet(
id = PnpIdResolver.PnpNoopId(result.firstId),
operations = listOf(
PnpOperation.RemovePni(result.secondId),
PnpOperation.SetPni(result.firstId, PNI_A)
)
),
result.changeSet
)
}
@Test
fun merge_e164AndPni_e164AndPni_noAciProvided_sessionsExist() {
val result = applyAndAssert(
listOf(
Input(E164_A, PNI_B, null, pniSession = true),
Input(E164_B, PNI_A, null, pniSession = true),
),
Update(E164_A, PNI_A, null),
Output(E164_A, PNI_A, null)
)
assertEquals(
PnpChangeSet(
id = PnpIdResolver.PnpNoopId(result.firstId),
operations = listOf(
PnpOperation.RemovePni(result.secondId),
PnpOperation.SetPni(result.firstId, PNI_A),
PnpOperation.SessionSwitchoverInsert(result.secondId),
PnpOperation.SessionSwitchoverInsert(result.firstId)
)
),
result.changeSet
)
}
@Test
fun merge_e164AndPni_aciOnly() {
val result = applyAndAssert(
listOf(
Input(E164_A, PNI_A, null),
Input(null, null, ACI_A),
),
Update(E164_A, PNI_A, ACI_A),
Output(E164_A, PNI_A, ACI_A)
)
assertEquals(
PnpChangeSet(
id = PnpIdResolver.PnpNoopId(result.secondId),
operations = listOf(
PnpOperation.Merge(
primaryId = result.secondId,
secondaryId = result.firstId
),
)
),
result.changeSet
)
}
@Test
fun merge_e164AndPni_aciOnly_e164RecordHasSeparateE164() {
val result = applyAndAssert(
listOf(
Input(E164_B, PNI_A, null),
Input(null, null, ACI_A),
),
Update(E164_A, PNI_A, ACI_A),
Output(E164_A, PNI_A, ACI_A)
)
assertEquals(
PnpChangeSet(
id = PnpIdResolver.PnpNoopId(result.secondId),
operations = listOf(
PnpOperation.RemovePni(result.firstId),
PnpOperation.SetPni(
recipientId = result.secondId,
pni = PNI_A,
),
PnpOperation.SetE164(
recipientId = result.secondId,
e164 = E164_A,
)
)
),
result.changeSet
)
}
@Test
fun merge_e164AndPni_aciOnly_e164RecordHasSeparateE164_changeNumber() {
val result = applyAndAssert(
listOf(
Input(E164_B, PNI_A, null),
Input(E164_C, null, ACI_A),
),
Update(E164_A, PNI_A, ACI_A),
Output(E164_A, PNI_A, ACI_A)
)
assertEquals(
PnpChangeSet(
id = PnpIdResolver.PnpNoopId(result.secondId),
operations = listOf(
PnpOperation.RemovePni(result.firstId),
PnpOperation.SetPni(
recipientId = result.secondId,
pni = PNI_A,
),
PnpOperation.SetE164(
recipientId = result.secondId,
e164 = E164_A,
),
PnpOperation.ChangeNumberInsert(
recipientId = result.secondId,
oldE164 = E164_C,
newE164 = E164_A
)
)
),
result.changeSet
)
}
@Test
fun merge_e164AndPni_e164AndPniAndAci_changeNumber() {
val result = applyAndAssert(
listOf(
Input(E164_A, PNI_A, null),
Input(E164_B, PNI_B, ACI_A),
),
Update(E164_A, PNI_A, ACI_A),
Output(E164_A, PNI_A, ACI_A)
)
assertEquals(
PnpChangeSet(
id = PnpIdResolver.PnpNoopId(result.secondId),
operations = listOf(
PnpOperation.RemovePni(result.secondId),
PnpOperation.RemoveE164(result.secondId),
PnpOperation.Merge(
primaryId = result.secondId,
secondaryId = result.firstId
),
PnpOperation.ChangeNumberInsert(
recipientId = result.secondId,
oldE164 = E164_B,
newE164 = E164_A
)
)
),
result.changeSet
)
}
@Test
fun merge_e164AndPni_e164Aci_changeNumber() {
val result = applyAndAssert(
listOf(
Input(E164_A, PNI_A, null),
Input(E164_B, null, ACI_A),
),
Update(E164_A, PNI_A, ACI_A),
Output(E164_A, PNI_A, ACI_A)
)
assertEquals(
PnpChangeSet(
id = PnpIdResolver.PnpNoopId(result.secondId),
operations = listOf(
PnpOperation.RemoveE164(result.secondId),
PnpOperation.Merge(
primaryId = result.secondId,
secondaryId = result.firstId
),
PnpOperation.ChangeNumberInsert(
recipientId = result.secondId,
oldE164 = E164_B,
newE164 = E164_A
)
)
),
result.changeSet
)
}
private fun insert(e164: String?, pni: PNI?, aci: ACI?): RecipientId {
val id: Long = SignalDatabase.rawDatabase.insert(
RecipientTable.TABLE_NAME,
null,
contentValuesOf(
RecipientTable.PHONE to e164,
RecipientTable.SERVICE_ID to (aci ?: pni)?.toString(),
RecipientTable.PNI_COLUMN to pni?.toString(),
RecipientTable.REGISTERED to RecipientTable.RegisteredState.REGISTERED.id
)
)
return RecipientId.from(id)
}
private fun insertMockSessionFor(account: ServiceId, address: ServiceId) {
SignalDatabase.rawDatabase.insert(
SessionTable.TABLE_NAME, null,
contentValuesOf(
SessionTable.ACCOUNT_ID to account.toString(),
SessionTable.ADDRESS to address.toString(),
SessionTable.DEVICE to 1,
SessionTable.RECORD to Util.getSecretBytes(32)
)
)
}
data class Input(val e164: String?, val pni: PNI?, val aci: ACI?, val pniSession: Boolean = false, val aciSession: Boolean = false)
data class Update(val e164: String?, val pni: PNI?, val aci: ACI?, val pniVerified: Boolean = false)
data class Output(val e164: String?, val pni: PNI?, val aci: ACI?)
data class PnpMatchResult(val ids: List<RecipientId>, val changeSet: PnpChangeSet) {
val id
get() = if (ids.size == 1) {
ids[0]
} else {
throw IllegalStateException("There are multiple IDs, but you assumed 1!")
}
val firstId
get() = ids[0]
val secondId
get() = ids[1]
val thirdId
get() = ids[2]
}
private fun applyAndAssert(input: Input, update: Update, output: Output): PnpMatchResult {
return applyAndAssert(listOf(input), update, output)
}
/**
* Helper method that will call insert your recipients, call [RecipientTable.processPnpTupleToChangeSet] with your params,
* and then verify your output matches what you expect.
*
* It results the inserted ID's and changeset for additional verification.
*
* But basically this is here to make the tests more readable. It gives you a clear list of:
* - input
* - update
* - output
*
* that you can spot check easily.
*
* Important: The output will only include records that contain fields from the input. That means
* for:
*
* Input: E164_B, PNI_A, null
* Update: E164_A, PNI_A, null
*
* You will get:
* Output: E164_A, PNI_A, null
*
* Even though there was an update that will also result in the row (E164_B, null, null)
*/
private fun applyAndAssert(input: List<Input>, update: Update, output: Output): PnpMatchResult {
val ids = input.map { insert(it.e164, it.pni, it.aci) }
input
.filter { it.pniSession }
.forEach { insertMockSessionFor(databaseRule.localAci, it.pni!!) }
input
.filter { it.aciSession }
.forEach { insertMockSessionFor(databaseRule.localAci, it.aci!!) }
val byE164 = update.e164?.let { db.getByE164(it).orElse(null) }
val byPniSid = update.pni?.let { db.getByServiceId(it).orElse(null) }
val byAciSid = update.aci?.let { db.getByServiceId(it).orElse(null) }
val data = PnpDataSet(
e164 = update.e164,
pni = update.pni,
aci = update.aci,
byE164 = byE164,
byPniSid = byPniSid,
byPniOnly = update.pni?.let { db.getByPni(it).orElse(null) },
byAciSid = byAciSid,
e164Record = byE164?.let { db.getRecord(it) },
pniSidRecord = byPniSid?.let { db.getRecord(it) },
aciSidRecord = byAciSid?.let { db.getRecord(it) }
)
val changeSet = db.processPnpTupleToChangeSet(update.e164, update.pni, update.aci, pniVerified = update.pniVerified)
val finalData = data.perform(changeSet.operations)
val finalRecords = setOfNotNull(finalData.e164Record, finalData.pniSidRecord, finalData.aciSidRecord)
assertEquals("There's still multiple records in the resulting record set! $finalRecords", 1, finalRecords.size)
finalRecords.firstOrNull { record -> record.e164 == output.e164 && record.pni == output.pni && record.serviceId == (output.aci ?: output.pni) }
?: throw AssertionError("Expected output was not found in the result set! Expected: $output")
return PnpMatchResult(
ids = ids,
changeSet = changeSet
)
}
companion object {
val ACI_A = ACI.from(UUID.fromString("3436efbe-5a76-47fa-a98a-7e72c948a82e"))
val ACI_B = ACI.from(UUID.fromString("8de7f691-0b60-4a68-9cd9-ed2f8453f9ed"))
val PNI_A = PNI.from(UUID.fromString("154b8d92-c960-4f6c-8385-671ad2ffb999"))
val PNI_B = PNI.from(UUID.fromString("ba92b1fb-cd55-40bf-adda-c35a85375533"))
const val E164_A = "+12221234567"
const val E164_B = "+13331234567"
const val E164_C = "+14441234567"
}
}

View File

@@ -180,4 +180,45 @@ class SQLiteDatabaseTest {
assertTrue(hasRun1.get())
assertTrue(hasRun2.get())
}
@Test
fun runPostSuccessfulTransaction_runsAfterMainTransactionInNestedTransaction() {
val hasRun1 = AtomicBoolean(false)
val hasRun2 = AtomicBoolean(false)
db.beginTransaction()
db.runPostSuccessfulTransaction {
assertFalse(hasRun1.get())
assertFalse(hasRun2.get())
hasRun1.set(true)
}
assertFalse(hasRun1.get())
assertFalse(hasRun2.get())
db.beginTransaction()
db.runPostSuccessfulTransaction {
assertTrue(hasRun1.get())
assertFalse(hasRun2.get())
hasRun2.set(true)
}
db.setTransactionSuccessful()
assertFalse(hasRun1.get())
assertFalse(hasRun2.get())
db.endTransaction()
db.setTransactionSuccessful()
assertFalse(hasRun1.get())
assertFalse(hasRun2.get())
db.endTransaction()
assertTrue(hasRun1.get())
assertTrue(hasRun2.get())
}
}

View File

@@ -18,13 +18,10 @@ import org.thoughtcrime.securesms.database.model.databaseprotos.groupChange
import org.thoughtcrime.securesms.database.model.databaseprotos.groupContext
import org.thoughtcrime.securesms.groups.GroupId
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.mms.IncomingMessage
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.sms.IncomingGroupUpdateMessage
import org.thoughtcrime.securesms.sms.IncomingTextMessage
import org.whispersystems.signalservice.api.push.ACI
import org.whispersystems.signalservice.api.push.PNI
import org.whispersystems.signalservice.api.push.ServiceId
import java.util.Optional
import org.whispersystems.signalservice.api.push.ServiceId.ACI
import org.whispersystems.signalservice.api.push.ServiceId.PNI
import java.util.UUID
@Suppress("ClassName", "TestFunctionName")
@@ -32,7 +29,7 @@ import java.util.UUID
class SmsDatabaseTest_collapseJoinRequestEventsIfPossible {
private lateinit var recipients: RecipientTable
private lateinit var sms: SmsTable
private lateinit var sms: MessageTable
private val localAci = ACI.from(UUID.randomUUID())
private val localPni = PNI.from(UUID.randomUUID())
@@ -45,7 +42,7 @@ class SmsDatabaseTest_collapseJoinRequestEventsIfPossible {
@Before
fun setUp() {
recipients = SignalDatabase.recipients
sms = SignalDatabase.sms
sms = SignalDatabase.messages
SignalStore.account().setAci(localAci)
SignalStore.account().setPni(localPni)
@@ -273,18 +270,34 @@ class SmsDatabaseTest_collapseJoinRequestEventsIfPossible {
assertThat("latest message should be deleted", sms.getMessageRecordOrNull(latestMessage.messageId), nullValue())
}
private fun smsMessage(sender: RecipientId, body: String? = ""): IncomingTextMessage {
private fun smsMessage(sender: RecipientId, body: String? = ""): IncomingMessage {
wallClock++
return IncomingTextMessage(sender, 1, wallClock, wallClock, wallClock, body, Optional.of(groupId), 0, true, null)
return IncomingMessage(
type = MessageType.NORMAL,
from = sender,
sentTimeMillis = wallClock,
serverTimeMillis = wallClock,
receivedTimeMillis = wallClock,
body = body,
groupId = groupId,
isUnidentified = true
)
}
private fun groupUpdateMessage(sender: RecipientId, groupContext: DecryptedGroupV2Context): IncomingGroupUpdateMessage {
return IncomingGroupUpdateMessage(smsMessage(sender, null), groupContext)
private fun groupUpdateMessage(sender: RecipientId, groupContext: DecryptedGroupV2Context): IncomingMessage {
wallClock++
return IncomingMessage.groupUpdate(
from = sender,
timestamp = wallClock,
groupId = groupId,
groupContext = groupContext,
serverGuid = null
)
}
companion object {
private val aliceServiceId: ServiceId = ACI.from(UUID.fromString("3436efbe-5a76-47fa-a98a-7e72c948a82e"))
private val bobServiceId: ServiceId = ACI.from(UUID.fromString("8de7f691-0b60-4a68-9cd9-ed2f8453f9ed"))
private val aliceServiceId: ACI = ACI.from(UUID.fromString("3436efbe-5a76-47fa-a98a-7e72c948a82e"))
private val bobServiceId: ACI = ACI.from(UUID.fromString("8de7f691-0b60-4a68-9cd9-ed2f8453f9ed"))
private val masterKey = GroupMasterKey(Hex.fromStringCondensed("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"))
private val groupId = GroupId.v2(masterKey)

View File

@@ -12,19 +12,24 @@ import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.junit.Assert.fail
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.thoughtcrime.securesms.database.model.DistributionListId
import org.thoughtcrime.securesms.database.model.StoryType
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.testing.SignalActivityRule
import org.whispersystems.signalservice.api.push.DistributionId
import org.whispersystems.signalservice.api.push.ServiceId
import org.whispersystems.signalservice.api.push.ServiceId.ACI
import java.util.UUID
@RunWith(AndroidJUnit4::class)
class StorySendTableTest {
@get:Rule
val harness = SignalActivityRule(othersCount = 0, createGroup = false)
private val distributionId1 = DistributionId.from(UUID.randomUUID())
private val distributionId2 = DistributionId.from(UUID.randomUUID())
private val distributionId3 = DistributionId.from(UUID.randomUUID())
@@ -65,17 +70,17 @@ class StorySendTableTest {
messageId1 = MmsHelper.insert(
recipient = distributionListRecipient1,
storyType = StoryType.STORY_WITHOUT_REPLIES,
storyType = StoryType.STORY_WITHOUT_REPLIES
)
messageId2 = MmsHelper.insert(
recipient = distributionListRecipient2,
storyType = StoryType.STORY_WITH_REPLIES,
storyType = StoryType.STORY_WITH_REPLIES
)
messageId3 = MmsHelper.insert(
recipient = distributionListRecipient3,
storyType = StoryType.STORY_WITHOUT_REPLIES,
storyType = StoryType.STORY_WITHOUT_REPLIES
)
recipients6to15 = recipients1to10.takeLast(5) + recipients11to20.take(5)
@@ -184,7 +189,7 @@ class StorySendTableTest {
@Test
fun getRemoteDeleteRecipients_overlapWithPreviousDeletes() {
storySends.insert(messageId1, recipients1to10, 200, false, distributionId1)
SignalDatabase.mms.markAsRemoteDelete(messageId1)
SignalDatabase.messages.markAsRemoteDelete(messageId1)
storySends.insert(messageId2, recipients6to15, 200, true, distributionId2)
@@ -281,7 +286,7 @@ class StorySendTableTest {
fun givenTwoStoriesAndOneIsRemoteDeleted_whenIGetFullSentStorySyncManifestForStory2_thenIExpectNonNullResult() {
storySends.insert(messageId1, recipients1to10, 200, false, distributionId1)
storySends.insert(messageId2, recipients1to10, 200, true, distributionId2)
SignalDatabase.mms.markAsRemoteDelete(messageId1)
SignalDatabase.messages.markAsRemoteDelete(messageId1)
val manifest = storySends.getFullSentStorySyncManifest(messageId2, 200)!!
@@ -294,7 +299,7 @@ class StorySendTableTest {
storySends.insert(messageId1, recipients1to10, 200, false, distributionId1)
storySends.insert(messageId1, recipients11to20, 200, false, distributionId1)
storySends.insert(messageId2, recipients1to10, 200, true, distributionId2)
SignalDatabase.mms.markAsRemoteDelete(messageId1)
SignalDatabase.messages.markAsRemoteDelete(messageId1)
val recipientIds = storySends.getRecipientIdsForManifestUpdate(200, messageId1)
@@ -306,7 +311,7 @@ class StorySendTableTest {
storySends.insert(messageId1, recipients1to10, 200, false, distributionId1)
storySends.insert(messageId2, recipients1to10, 200, true, distributionId2)
storySends.insert(messageId2, recipients11to20, 200, true, distributionId2)
SignalDatabase.mms.markAsRemoteDelete(messageId1)
SignalDatabase.messages.markAsRemoteDelete(messageId1)
val recipientIds = storySends.getRecipientIdsForManifestUpdate(200, messageId1)
val results = storySends.getSentStorySyncManifestForUpdate(200, recipientIds)
@@ -319,7 +324,7 @@ class StorySendTableTest {
fun givenTwoStoriesAndTheOneThatAllowedRepliesIsRemoteDeleted_whenIGetPartialSentStorySyncManifest_thenIExpectAllowRepliesToBeTrue() {
storySends.insert(messageId1, recipients1to10, 200, false, distributionId1)
storySends.insert(messageId2, recipients1to10, 200, true, distributionId2)
SignalDatabase.mms.markAsRemoteDelete(messageId2)
SignalDatabase.messages.markAsRemoteDelete(messageId2)
val recipientIds = storySends.getRecipientIdsForManifestUpdate(200, messageId1)
val results = storySends.getSentStorySyncManifestForUpdate(200, recipientIds)
@@ -377,7 +382,7 @@ class StorySendTableTest {
storySends.applySentStoryManifest(remote, 200)
SignalDatabase.mms.getMessageRecord(messageId5)
SignalDatabase.messages.getMessageRecord(messageId5)
fail("Expected messageId5 to no longer exist.")
}
@@ -402,7 +407,7 @@ class StorySendTableTest {
storySends.applySentStoryManifest(remote, 200)
assertFalse(SignalDatabase.mms.getMessageRecord(messageId4).isRemoteDelete)
assertFalse(SignalDatabase.messages.getMessageRecord(messageId4).isRemoteDelete)
}
@Test
@@ -460,7 +465,7 @@ class StorySendTableTest {
private fun makeRecipients(count: Int): List<RecipientId> {
return (1..count).map {
SignalDatabase.recipients.getOrInsertFromServiceId(ServiceId.from(UUID.randomUUID()))
SignalDatabase.recipients.getOrInsertFromServiceId(ACI.from(UUID.randomUUID()))
}
}
}

View File

@@ -0,0 +1,144 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.database
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.thoughtcrime.securesms.conversationlist.model.ConversationFilter
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.testing.SignalDatabaseRule
import org.whispersystems.signalservice.api.push.ServiceId.ACI
import java.util.UUID
@Suppress("ClassName")
class ThreadTableTest_active {
@Rule
@JvmField
val databaseRule = SignalDatabaseRule()
private lateinit var recipient: Recipient
@Before
fun setUp() {
recipient = Recipient.resolved(SignalDatabase.recipients.getOrInsertFromServiceId(ACI.from(UUID.randomUUID())))
}
@Test
fun givenActiveUnarchivedThread_whenIGetUnarchivedConversationList_thenIExpectThread() {
val threadId = SignalDatabase.threads.getOrCreateThreadIdFor(recipient)
MmsHelper.insert(recipient = recipient, threadId = threadId)
SignalDatabase.threads.update(threadId, false)
SignalDatabase.threads.getUnarchivedConversationList(
ConversationFilter.OFF,
false,
0,
10
).use { threads ->
assertEquals(1, threads.count)
val record = ThreadTable.StaticReader(threads, InstrumentationRegistry.getInstrumentation().context).getNext()
assertNotNull(record)
assertEquals(record!!.recipient.id, recipient.id)
}
}
@Test
fun givenInactiveUnarchivedThread_whenIGetUnarchivedConversationList_thenIExpectNoThread() {
val threadId = SignalDatabase.threads.getOrCreateThreadIdFor(recipient)
MmsHelper.insert(recipient = recipient, threadId = threadId)
SignalDatabase.threads.update(threadId, false)
SignalDatabase.threads.deleteConversation(threadId)
SignalDatabase.threads.getUnarchivedConversationList(
ConversationFilter.OFF,
false,
0,
10
).use { threads ->
assertEquals(0, threads.count)
}
val threadId2 = SignalDatabase.threads.getOrCreateThreadIdFor(recipient)
assertEquals(threadId2, threadId)
}
@Test
fun givenActiveArchivedThread_whenIGetUnarchivedConversationList_thenIExpectNoThread() {
val threadId = SignalDatabase.threads.getOrCreateThreadIdFor(recipient)
MmsHelper.insert(recipient = recipient, threadId = threadId)
SignalDatabase.threads.update(threadId, false)
SignalDatabase.threads.setArchived(setOf(threadId), true)
SignalDatabase.threads.getUnarchivedConversationList(
ConversationFilter.OFF,
false,
0,
10
).use { threads ->
assertEquals(0, threads.count)
}
}
@Test
fun givenActiveArchivedThread_whenIGetArchivedConversationList_thenIExpectThread() {
val threadId = SignalDatabase.threads.getOrCreateThreadIdFor(recipient)
MmsHelper.insert(recipient = recipient, threadId = threadId)
SignalDatabase.threads.update(threadId, false)
SignalDatabase.threads.setArchived(setOf(threadId), true)
SignalDatabase.threads.getArchivedConversationList(
ConversationFilter.OFF,
0,
10
).use { threads ->
assertEquals(1, threads.count)
}
}
@Test
fun givenInactiveArchivedThread_whenIGetArchivedConversationList_thenIExpectNoThread() {
val threadId = SignalDatabase.threads.getOrCreateThreadIdFor(recipient)
MmsHelper.insert(recipient = recipient, threadId = threadId)
SignalDatabase.threads.update(threadId, false)
SignalDatabase.threads.deleteConversation(threadId)
SignalDatabase.threads.setArchived(setOf(threadId), true)
SignalDatabase.threads.getArchivedConversationList(
ConversationFilter.OFF,
0,
10
).use { threads ->
assertEquals(0, threads.count)
}
val threadId2 = SignalDatabase.threads.getOrCreateThreadIdFor(recipient)
assertEquals(threadId2, threadId)
}
@Test
fun givenActiveArchivedThread_whenIDeactivateThread_thenIExpectNoMessages() {
val threadId = SignalDatabase.threads.getOrCreateThreadIdFor(recipient)
MmsHelper.insert(recipient = recipient, threadId = threadId)
SignalDatabase.threads.update(threadId, false)
SignalDatabase.messages.getConversation(threadId).use {
assertEquals(1, it.count)
}
SignalDatabase.threads.deleteConversation(threadId)
SignalDatabase.messages.getConversation(threadId).use {
assertEquals(0, it.count)
}
}
}

View File

@@ -9,7 +9,7 @@ import org.signal.core.util.CursorUtil
import org.thoughtcrime.securesms.conversationlist.model.ConversationFilter
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.testing.SignalDatabaseRule
import org.whispersystems.signalservice.api.push.ServiceId
import org.whispersystems.signalservice.api.push.ServiceId.ACI
import java.util.UUID
@Suppress("ClassName")
@@ -23,7 +23,7 @@ class ThreadTableTest_pinned {
@Before
fun setUp() {
recipient = Recipient.resolved(SignalDatabase.recipients.getOrInsertFromServiceId(ServiceId.from(UUID.randomUUID())))
recipient = Recipient.resolved(SignalDatabase.recipients.getOrInsertFromServiceId(ACI.from(UUID.randomUUID())))
}
@Test
@@ -34,7 +34,7 @@ class ThreadTableTest_pinned {
SignalDatabase.threads.pinConversations(listOf(threadId))
// WHEN
SignalDatabase.mms.deleteMessage(messageId)
SignalDatabase.messages.deleteMessage(messageId)
// THEN
val pinned = SignalDatabase.threads.getPinnedThreadIds()
@@ -49,7 +49,7 @@ class ThreadTableTest_pinned {
SignalDatabase.threads.pinConversations(listOf(threadId))
// WHEN
SignalDatabase.mms.deleteMessage(messageId)
SignalDatabase.messages.deleteMessage(messageId)
// THEN
val unarchivedCount = SignalDatabase.threads.getUnarchivedConversationListCount(ConversationFilter.OFF)
@@ -64,7 +64,7 @@ class ThreadTableTest_pinned {
SignalDatabase.threads.pinConversations(listOf(threadId))
// WHEN
SignalDatabase.mms.deleteMessage(messageId)
SignalDatabase.messages.deleteMessage(messageId)
// THEN
SignalDatabase.threads.getUnarchivedConversationList(ConversationFilter.OFF, true, 0, 1).use {

View File

@@ -10,7 +10,7 @@ import org.signal.core.util.CursorUtil
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.testing.SignalDatabaseRule
import org.whispersystems.signalservice.api.push.ServiceId
import org.whispersystems.signalservice.api.push.ServiceId.ACI
import java.util.UUID
@Suppress("ClassName")
@@ -25,7 +25,7 @@ class ThreadTableTest_recents {
@Before
fun setUp() {
recipient = Recipient.resolved(SignalDatabase.recipients.getOrInsertFromServiceId(ServiceId.from(UUID.randomUUID())))
recipient = Recipient.resolved(SignalDatabase.recipients.getOrInsertFromServiceId(ACI.from(UUID.randomUUID())))
}
@Test

View File

@@ -2,78 +2,86 @@ package org.thoughtcrime.securesms.dependencies
import android.app.Application
import okhttp3.ConnectionSpec
import okhttp3.Response
import okhttp3.WebSocket
import okhttp3.WebSocketListener
import okhttp3.mockwebserver.Dispatcher
import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer
import okhttp3.mockwebserver.RecordedRequest
import okio.ByteString
import org.mockito.kotlin.any
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.signal.core.util.Base64
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.BuildConfig
import org.thoughtcrime.securesms.KbsEnclave
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess
import org.thoughtcrime.securesms.push.SignalServiceTrustStore
import org.thoughtcrime.securesms.recipients.LiveRecipientCache
import org.thoughtcrime.securesms.testing.Get
import org.thoughtcrime.securesms.testing.Verb
import org.thoughtcrime.securesms.testing.runSync
import org.thoughtcrime.securesms.util.Base64
import org.whispersystems.signalservice.api.KeyBackupService
import org.whispersystems.signalservice.api.SignalServiceAccountManager
import org.thoughtcrime.securesms.testing.success
import org.whispersystems.signalservice.api.push.TrustStore
import org.whispersystems.signalservice.internal.configuration.SignalCdnUrl
import org.whispersystems.signalservice.internal.configuration.SignalCdsiUrl
import org.whispersystems.signalservice.internal.configuration.SignalContactDiscoveryUrl
import org.whispersystems.signalservice.internal.configuration.SignalKeyBackupServiceUrl
import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration
import org.whispersystems.signalservice.internal.configuration.SignalServiceUrl
import org.whispersystems.signalservice.internal.configuration.SignalStorageUrl
import java.security.KeyStore
import org.whispersystems.signalservice.internal.configuration.SignalSvr2Url
import java.util.Optional
/**
* Dependency provider used for instrumentation tests (aka androidTests).
*
* Handles setting up a mock web server for API calls, and provides mockable versions of [SignalServiceNetworkAccess] and
* [KeyBackupService].
* Handles setting up a mock web server for API calls, and provides mockable versions of [SignalServiceNetworkAccess].
*/
class InstrumentationApplicationDependencyProvider(application: Application, default: ApplicationDependencyProvider) : ApplicationDependencies.Provider by default {
class InstrumentationApplicationDependencyProvider(val application: Application, private val default: ApplicationDependencyProvider) : ApplicationDependencies.Provider by default {
private val serviceTrustStore: TrustStore
private val uncensoredConfiguration: SignalServiceConfiguration
private val serviceNetworkAccessMock: SignalServiceNetworkAccess
private val keyBackupService: KeyBackupService
private val recipientCache: LiveRecipientCache
init {
runSync {
webServer = MockWebServer()
baseUrl = webServer.url("").toString()
addMockWebRequestHandlers(
Get("/v1/websocket/?login=") {
MockResponse().success().withWebSocketUpgrade(mockIdentifiedWebSocket)
},
Get("/v1/websocket", { !it.path.contains("login") }) {
MockResponse().success().withWebSocketUpgrade(object : WebSocketListener() {})
}
)
}
webServer.setDispatcher(object : Dispatcher() {
override fun dispatch(request: RecordedRequest): MockResponse {
val handler = handlers.firstOrNull {
request.method == it.verb && request.path.startsWith("/${it.path}")
}
val handler = handlers.firstOrNull { it.requestPredicate(request) }
return handler?.responseFactory?.invoke(request) ?: MockResponse().setResponseCode(500)
}
})
serviceTrustStore = SignalServiceTrustStore(application)
uncensoredConfiguration = SignalServiceConfiguration(
arrayOf(SignalServiceUrl(baseUrl, "localhost", serviceTrustStore, ConnectionSpec.CLEARTEXT)),
mapOf(
signalServiceUrls = arrayOf(SignalServiceUrl(baseUrl, "localhost", serviceTrustStore, ConnectionSpec.CLEARTEXT)),
signalCdnUrlMap = mapOf(
0 to arrayOf(SignalCdnUrl(baseUrl, "localhost", serviceTrustStore, ConnectionSpec.CLEARTEXT)),
2 to arrayOf(SignalCdnUrl(baseUrl, "localhost", serviceTrustStore, ConnectionSpec.CLEARTEXT))
),
arrayOf(SignalContactDiscoveryUrl(baseUrl, "localhost", serviceTrustStore, ConnectionSpec.CLEARTEXT)),
arrayOf(SignalKeyBackupServiceUrl(baseUrl, "localhost", serviceTrustStore, ConnectionSpec.CLEARTEXT)),
arrayOf(SignalStorageUrl(baseUrl, "localhost", serviceTrustStore, ConnectionSpec.CLEARTEXT)),
arrayOf(SignalCdsiUrl(baseUrl, "localhost", serviceTrustStore, ConnectionSpec.CLEARTEXT)),
emptyList(),
Optional.of(SignalServiceNetworkAccess.DNS),
Optional.empty(),
Base64.decode(BuildConfig.ZKGROUP_SERVER_PUBLIC_PARAMS)
signalStorageUrls = arrayOf(SignalStorageUrl(baseUrl, "localhost", serviceTrustStore, ConnectionSpec.CLEARTEXT)),
signalCdsiUrls = arrayOf(SignalCdsiUrl(baseUrl, "localhost", serviceTrustStore, ConnectionSpec.CLEARTEXT)),
signalSvr2Urls = arrayOf(SignalSvr2Url(baseUrl, serviceTrustStore, "localhost", ConnectionSpec.CLEARTEXT)),
networkInterceptors = emptyList(),
dns = Optional.of(SignalServiceNetworkAccess.DNS),
signalProxy = Optional.empty(),
zkGroupServerPublicParams = Base64.decode(BuildConfig.ZKGROUP_SERVER_PUBLIC_PARAMS),
genericServerPublicParams = Base64.decode(BuildConfig.GENERIC_SERVER_PUBLIC_PARAMS),
backupServerPublicParams = Base64.decode(BuildConfig.BACKUP_SERVER_PUBLIC_PARAMS)
)
serviceNetworkAccessMock = mock {
@@ -82,8 +90,6 @@ class InstrumentationApplicationDependencyProvider(application: Application, def
on { uncensoredConfiguration } doReturn uncensoredConfiguration
}
keyBackupService = mock()
recipientCache = LiveRecipientCache(application) { r -> r.run() }
}
@@ -91,26 +97,55 @@ class InstrumentationApplicationDependencyProvider(application: Application, def
return serviceNetworkAccessMock
}
override fun provideKeyBackupService(signalServiceAccountManager: SignalServiceAccountManager, keyStore: KeyStore, enclave: KbsEnclave): KeyBackupService {
return keyBackupService
}
override fun provideRecipientCache(): LiveRecipientCache {
return recipientCache
}
class MockWebSocket : WebSocketListener() {
private val TAG = "MockWebSocket"
var webSocket: WebSocket? = null
private set
override fun onOpen(webSocket: WebSocket, response: Response) {
Log.i(TAG, "onOpen(${webSocket.hashCode()})")
this.webSocket = webSocket
}
override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {
Log.i(TAG, "onClosing(${webSocket.hashCode()}): $code, $reason")
this.webSocket = null
}
override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
Log.i(TAG, "onClosed(${webSocket.hashCode()}): $code, $reason")
this.webSocket = null
}
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
Log.w(TAG, "onFailure(${webSocket.hashCode()})", t)
this.webSocket = null
}
}
companion object {
lateinit var webServer: MockWebServer
private set
lateinit var baseUrl: String
private set
val mockIdentifiedWebSocket = MockWebSocket()
private val handlers: MutableList<Verb> = mutableListOf()
fun addMockWebRequestHandlers(vararg verbs: Verb) {
handlers.addAll(verbs)
}
fun injectWebSocketMessage(value: ByteString) {
mockIdentifiedWebSocket.webSocket!!.send(value)
}
fun clearHandlers() {
handlers.clear()
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.jobs
import android.net.Uri
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.signal.core.util.StreamUtil
import org.thoughtcrime.securesms.attachments.UriAttachment
import org.thoughtcrime.securesms.database.AttachmentTable
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.UriAttachmentBuilder
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.jobmanager.Job
import org.thoughtcrime.securesms.mms.SentMediaQuality
import org.thoughtcrime.securesms.providers.BlobProvider
import org.thoughtcrime.securesms.testing.SignalActivityRule
import org.thoughtcrime.securesms.testing.assertIs
import org.thoughtcrime.securesms.util.MediaUtil
import java.util.Optional
import java.util.concurrent.CountDownLatch
@RunWith(AndroidJUnit4::class)
class AttachmentCompressionJobTest {
@get:Rule
val harness = SignalActivityRule()
@Test
fun testCompressionJobsWithDifferentTransformPropertiesCompleteSuccessfully() {
val imageBytes: ByteArray = InstrumentationRegistry.getInstrumentation().context.resources.assets.open("images/sample_image.png").use {
StreamUtil.readFully(it)
}
val blob = BlobProvider.getInstance().forData(imageBytes).createForSingleSessionOnDisk(ApplicationDependencies.getApplication())
val firstPreUpload = createAttachment(1, blob, AttachmentTable.TransformProperties.empty())
val firstDatabaseAttachment = SignalDatabase.attachments.insertAttachmentForPreUpload(firstPreUpload)
val firstCompressionJob: AttachmentCompressionJob = AttachmentCompressionJob.fromAttachment(firstDatabaseAttachment, false, -1)
var secondCompressionJob: AttachmentCompressionJob? = null
var firstJobResult: Job.Result? = null
var secondJobResult: Job.Result? = null
val secondJobLatch = CountDownLatch(1)
val jobThread = Thread {
firstCompressionJob.setContext(ApplicationDependencies.getApplication())
firstJobResult = firstCompressionJob.run()
secondJobLatch.await()
secondCompressionJob!!.setContext(ApplicationDependencies.getApplication())
secondJobResult = secondCompressionJob!!.run()
}
jobThread.start()
val secondPreUpload = createAttachment(1, blob, AttachmentTable.TransformProperties.forSentMediaQuality(Optional.empty(), SentMediaQuality.HIGH))
val secondDatabaseAttachment = SignalDatabase.attachments.insertAttachmentForPreUpload(secondPreUpload)
secondCompressionJob = AttachmentCompressionJob.fromAttachment(secondDatabaseAttachment, false, -1)
secondJobLatch.countDown()
jobThread.join()
firstJobResult!!.isSuccess assertIs true
secondJobResult!!.isSuccess assertIs true
}
private fun createAttachment(id: Long, uri: Uri, transformProperties: AttachmentTable.TransformProperties): UriAttachment {
return UriAttachmentBuilder.build(
id,
uri = uri,
contentType = MediaUtil.IMAGE_JPEG,
transformProperties = transformProperties
)
}
}

View File

@@ -1,212 +0,0 @@
package org.thoughtcrime.securesms.jobs
import androidx.test.ext.junit.runners.AndroidJUnit4
import okhttp3.mockwebserver.MockResponse
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.signal.libsignal.protocol.ecc.Curve
import org.thoughtcrime.securesms.crypto.storage.PreKeyMetadataStore
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.dependencies.InstrumentationApplicationDependencyProvider
import org.thoughtcrime.securesms.jobmanager.Job
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.testing.Get
import org.thoughtcrime.securesms.testing.Put
import org.thoughtcrime.securesms.testing.SignalActivityRule
import org.thoughtcrime.securesms.testing.assertIs
import org.thoughtcrime.securesms.testing.assertIsNot
import org.thoughtcrime.securesms.testing.parsedRequestBody
import org.thoughtcrime.securesms.testing.success
import org.whispersystems.signalservice.api.push.SignedPreKeyEntity
import org.whispersystems.signalservice.internal.push.PreKeyState
import org.whispersystems.signalservice.internal.push.PreKeyStatus
@RunWith(AndroidJUnit4::class)
class PreKeysSyncJobTest {
@get:Rule
val harness = SignalActivityRule()
private val aciPreKeyMeta: PreKeyMetadataStore
get() = SignalStore.account().aciPreKeys
private val pniPreKeyMeta: PreKeyMetadataStore
get() = SignalStore.account().pniPreKeys
private lateinit var job: PreKeysSyncJob
@Before
fun setUp() {
job = PreKeysSyncJob()
}
@After
fun tearDown() {
InstrumentationApplicationDependencyProvider.clearHandlers()
}
/**
* Create signed prekeys for both identities when both do not have registered prekeys according
* to our local state.
*/
@Test
fun runWithoutRegisteredKeysForBothIdentities() {
// GIVEN
aciPreKeyMeta.isSignedPreKeyRegistered = false
pniPreKeyMeta.isSignedPreKeyRegistered = false
lateinit var aciSignedPreKey: SignedPreKeyEntity
lateinit var pniSignedPreKey: SignedPreKeyEntity
InstrumentationApplicationDependencyProvider.addMockWebRequestHandlers(
Put("/v2/keys/signed?identity=aci") { r ->
aciSignedPreKey = r.parsedRequestBody()
MockResponse().success()
},
Put("/v2/keys/signed?identity=pni") { r ->
pniSignedPreKey = r.parsedRequestBody()
MockResponse().success()
},
)
// WHEN
val result: Job.Result = job.run()
// THEN
result.isSuccess assertIs true
aciPreKeyMeta.isSignedPreKeyRegistered assertIs true
pniPreKeyMeta.isSignedPreKeyRegistered assertIs true
val aciVerifySignatureResult = Curve.verifySignature(
ApplicationDependencies.getProtocolStore().aci().identityKeyPair.publicKey.publicKey,
aciSignedPreKey.publicKey.serialize(),
aciSignedPreKey.signature
)
aciVerifySignatureResult assertIs true
val pniVerifySignatureResult = Curve.verifySignature(
ApplicationDependencies.getProtocolStore().pni().identityKeyPair.publicKey.publicKey,
pniSignedPreKey.publicKey.serialize(),
pniSignedPreKey.signature
)
pniVerifySignatureResult assertIs true
}
/**
* With 100 prekeys registered for each identity, do nothing.
*/
@Test
fun runWithRegisteredKeysForBothIdentities() {
// GIVEN
val currentAciKeyId = aciPreKeyMeta.activeSignedPreKeyId
val currentPniKeyId = pniPreKeyMeta.activeSignedPreKeyId
InstrumentationApplicationDependencyProvider.addMockWebRequestHandlers(
Get("/v2/keys?identity=aci") { MockResponse().success(PreKeyStatus(100)) },
Get("/v2/keys?identity=pni") { MockResponse().success(PreKeyStatus(100)) },
)
// WHEN
val result: Job.Result = job.run()
// THEN
result.isSuccess assertIs true
aciPreKeyMeta.activeSignedPreKeyId assertIs currentAciKeyId
pniPreKeyMeta.activeSignedPreKeyId assertIs currentPniKeyId
}
/**
* With 100 prekeys registered for ACI, but no PNI prekeys registered according to local state,
* do nothing for ACI but create PNI prekeys and update local state.
*/
@Test
fun runWithRegisteredKeysForAciIdentityOnly() {
// GIVEN
pniPreKeyMeta.isSignedPreKeyRegistered = false
val currentAciKeyId = aciPreKeyMeta.activeSignedPreKeyId
val currentPniKeyId = pniPreKeyMeta.activeSignedPreKeyId
InstrumentationApplicationDependencyProvider.addMockWebRequestHandlers(
Get("/v2/keys?identity=aci") { MockResponse().success(PreKeyStatus(100)) },
Put("/v2/keys/signed?identity=pni") { MockResponse().success() },
)
// WHEN
val result: Job.Result = job.run()
// THEN
result.isSuccess assertIs true
pniPreKeyMeta.isSignedPreKeyRegistered assertIs true
aciPreKeyMeta.activeSignedPreKeyId assertIs currentAciKeyId
pniPreKeyMeta.activeSignedPreKeyId assertIsNot currentPniKeyId
}
/**
* With <10 prekeys registered for each identity, upload new.
*/
@Test
fun runWithLowNumberOfRegisteredKeysForBothIdentities() {
// GIVEN
val currentAciKeyId = aciPreKeyMeta.activeSignedPreKeyId
val currentPniKeyId = pniPreKeyMeta.activeSignedPreKeyId
val currentNextAciPreKeyId = aciPreKeyMeta.nextOneTimePreKeyId
val currentNextPniPreKeyId = pniPreKeyMeta.nextOneTimePreKeyId
lateinit var aciPreKeyStateRequest: PreKeyState
lateinit var pniPreKeyStateRequest: PreKeyState
InstrumentationApplicationDependencyProvider.addMockWebRequestHandlers(
Get("/v2/keys?identity=aci") { MockResponse().success(PreKeyStatus(5)) },
Get("/v2/keys?identity=pni") { MockResponse().success(PreKeyStatus(5)) },
Put("/v2/keys/?identity=aci") { r ->
aciPreKeyStateRequest = r.parsedRequestBody()
MockResponse().success()
},
Put("/v2/keys/?identity=pni") { r ->
pniPreKeyStateRequest = r.parsedRequestBody()
MockResponse().success()
},
)
// WHEN
val result: Job.Result = job.run()
// THEN
result.isSuccess assertIs true
aciPreKeyMeta.activeSignedPreKeyId assertIsNot currentAciKeyId
pniPreKeyMeta.activeSignedPreKeyId assertIsNot currentPniKeyId
aciPreKeyMeta.nextOneTimePreKeyId assertIsNot currentNextAciPreKeyId
pniPreKeyMeta.nextOneTimePreKeyId assertIsNot currentNextPniPreKeyId
ApplicationDependencies.getProtocolStore().aci().identityKeyPair.publicKey.let { aciIdentityKey ->
aciPreKeyStateRequest.identityKey assertIs aciIdentityKey
val verifySignatureResult = Curve.verifySignature(
aciIdentityKey.publicKey,
aciPreKeyStateRequest.signedPreKey.publicKey.serialize(),
aciPreKeyStateRequest.signedPreKey.signature
)
verifySignatureResult assertIs true
}
ApplicationDependencies.getProtocolStore().pni().identityKeyPair.publicKey.let { pniIdentityKey ->
pniPreKeyStateRequest.identityKey assertIs pniIdentityKey
val verifySignatureResult = Curve.verifySignature(
pniIdentityKey.publicKey,
pniPreKeyStateRequest.signedPreKey.publicKey.serialize(),
pniPreKeyStateRequest.signedPreKey.signature
)
verifySignatureResult assertIs true
}
}
}

View File

@@ -5,9 +5,10 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.signal.core.util.Hex;
import org.whispersystems.signalservice.api.kbs.HashedPin;
import org.signal.libsignal.svr2.PinHash;
import org.whispersystems.signalservice.api.kbs.KbsData;
import org.whispersystems.signalservice.api.kbs.MasterKey;
import org.whispersystems.signalservice.api.kbs.PinHashUtil;
import java.io.IOException;
@@ -21,77 +22,76 @@ public final class PinHashing_hashPin_Test {
@Test
public void argon2_hashed_pin_password() throws IOException {
String pin = "password";
byte[] backupId = Hex.fromStringCondensed("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");
byte[] salt = Hex.fromStringCondensed("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");
MasterKey masterKey = new MasterKey(Hex.fromStringCondensed("202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f"));
HashedPin hashedPin = PinHashing.hashPin(pin, () -> backupId);
KbsData kbsData = hashedPin.createNewKbsData(masterKey);
PinHash hashedPin = PinHashUtil.hashPin(pin, salt);
KbsData kbsData = PinHashUtil.createNewKbsData(hashedPin, masterKey);
assertArrayEquals(hashedPin.getKbsAccessKey(), kbsData.getKbsAccessKey());
assertArrayEquals(hashedPin.accessKey(), kbsData.getKbsAccessKey());
assertArrayEquals(Hex.fromStringCondensed("ab7e8499d21f80a6600b3b9ee349ac6d72c07e3359fe885a934ba7aa844429f8"), kbsData.getKbsAccessKey());
assertArrayEquals(Hex.fromStringCondensed("3f33ce58eb25b40436592a30eae2a8fabab1899095f4e2fba6e2d0dc43b4a2d9cac5a3931748522393951e0e54dec769"), kbsData.getCipherText());
assertEquals(masterKey, kbsData.getMasterKey());
String localPinHash = PinHashing.localPinHash(pin);
assertTrue(PinHashing.verifyLocalPinHash(localPinHash, pin));
String localPinHash = PinHashUtil.localPinHash(pin);
assertTrue(PinHashUtil.verifyLocalPinHash(localPinHash, pin));
}
@Test
public void argon2_hashed_pin_another_password() throws IOException {
String pin = "anotherpassword";
byte[] backupId = Hex.fromStringCondensed("202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f");
byte[] salt = Hex.fromStringCondensed("202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f");
MasterKey masterKey = new MasterKey(Hex.fromStringCondensed("88a787415a2ecd79da0d1016a82a27c5c695c9a19b88b0aa1d35683280aa9a67"));
HashedPin hashedPin = PinHashing.hashPin(pin, () -> backupId);
KbsData kbsData = hashedPin.createNewKbsData(masterKey);
PinHash hashedPin = PinHashUtil.hashPin(pin, salt);
KbsData kbsData = PinHashUtil.createNewKbsData(hashedPin, masterKey);
assertArrayEquals(hashedPin.getKbsAccessKey(), kbsData.getKbsAccessKey());
assertArrayEquals(hashedPin.accessKey(), kbsData.getKbsAccessKey());
assertArrayEquals(Hex.fromStringCondensed("301d9dd1e96f20ce51083f67d3298fd37b97525de8324d5e12ed2d407d3d927b"), kbsData.getKbsAccessKey());
assertArrayEquals(Hex.fromStringCondensed("9d9b05402ea39c17ff1c9298c8a0e86784a352aa02a74943bf8bcf07ec0f4b574a5b786ad0182c8d308d9eb06538b8c9"), kbsData.getCipherText());
assertEquals(masterKey, kbsData.getMasterKey());
String localPinHash = PinHashing.localPinHash(pin);
assertTrue(PinHashing.verifyLocalPinHash(localPinHash, pin));
String localPinHash = PinHashUtil.localPinHash(pin);
assertTrue(PinHashUtil.verifyLocalPinHash(localPinHash, pin));
}
@Test
public void argon2_hashed_pin_password_with_spaces_diacritics_and_non_arabic_numerals() throws IOException {
String pin = " Pass६örd ";
byte[] backupId = Hex.fromStringCondensed("cba811749042b303a6a7efa5ccd160aea5e3ea243c8d2692bd13d515732f51a8");
byte[] salt = Hex.fromStringCondensed("cba811749042b303a6a7efa5ccd160aea5e3ea243c8d2692bd13d515732f51a8");
MasterKey masterKey = new MasterKey(Hex.fromStringCondensed("9571f3fde1e58588ba49bcf82be1b301ca3859a6f59076f79a8f47181ef952bf"));
HashedPin hashedPin = PinHashing.hashPin(pin, () -> backupId);
KbsData kbsData = hashedPin.createNewKbsData(masterKey);
PinHash hashedPin = PinHashUtil.hashPin(pin, salt);
KbsData kbsData = PinHashUtil.createNewKbsData(hashedPin, masterKey);
assertArrayEquals(hashedPin.getKbsAccessKey(), kbsData.getKbsAccessKey());
assertArrayEquals(hashedPin.accessKey(), kbsData.getKbsAccessKey());
assertArrayEquals(Hex.fromStringCondensed("ab645acdccc1652a48a34b2ac6926340ff35c03034013f68760f20013f028dd8"), kbsData.getKbsAccessKey());
assertArrayEquals(Hex.fromStringCondensed("11c0ba1834db15e47c172f6c987c64bd4cfc69c6047dd67a022afeec0165a10943f204d5b8f37b3cb7bab21c6dfc39c8"), kbsData.getCipherText());
assertEquals(masterKey, kbsData.getMasterKey());
assertEquals("577939bccb2b6638c39222d5a97998a867c5e154e30b82cc120f2dd07a3de987", kbsData.getMasterKey().deriveRegistrationLock());
String localPinHash = PinHashing.localPinHash(pin);
assertTrue(PinHashing.verifyLocalPinHash(localPinHash, pin));
String localPinHash = PinHashUtil.localPinHash(pin);
assertTrue(PinHashUtil.verifyLocalPinHash(localPinHash, pin));
}
@Test
public void argon2_hashed_pin_password_with_just_non_arabic_numerals() throws IOException {
String pin = " ६१८ ";
byte[] backupId = Hex.fromStringCondensed("717dc111a98423a57196512606822fca646c653facd037c10728f14ba0be2ab3");
byte[] salt = Hex.fromStringCondensed("717dc111a98423a57196512606822fca646c653facd037c10728f14ba0be2ab3");
MasterKey masterKey = new MasterKey(Hex.fromStringCondensed("0432d735b32f66d0e3a70d4f9cc821a8529521a4937d26b987715d8eff4e4c54"));
HashedPin hashedPin = PinHashing.hashPin(pin, () -> backupId);
KbsData kbsData = hashedPin.createNewKbsData(masterKey);
PinHash hashedPin = PinHashUtil.hashPin(pin, salt);
KbsData kbsData = PinHashUtil.createNewKbsData(hashedPin, masterKey);
assertArrayEquals(hashedPin.getKbsAccessKey(), kbsData.getKbsAccessKey());
assertArrayEquals(hashedPin.accessKey(), kbsData.getKbsAccessKey());
assertArrayEquals(Hex.fromStringCondensed("d2fedabd0d4c17a371491c9722578843a26be3b4923e28d452ab2fc5491e794b"), kbsData.getKbsAccessKey());
assertArrayEquals(Hex.fromStringCondensed("877ef871ef1fc668401c717ef21aa12e8523579fb1ff4474b76f28c2293537c80cc7569996c9e0229bea7f378e3a824e"), kbsData.getCipherText());
assertEquals(masterKey, kbsData.getMasterKey());
assertEquals("23a75cb1df1a87df45cc2ed167c2bdc85ab1220b847c88761b0005cac907fce5", kbsData.getMasterKey().deriveRegistrationLock());
String localPinHash = PinHashing.localPinHash(pin);
assertTrue(PinHashing.verifyLocalPinHash(localPinHash, pin));
String localPinHash = PinHashUtil.localPinHash(pin);
assertTrue(PinHashUtil.verifyLocalPinHash(localPinHash, pin));
}
}

View File

@@ -0,0 +1,253 @@
package org.thoughtcrime.securesms.messages
import android.database.Cursor
import android.util.Base64
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.signal.core.util.ThreadUtil
import org.signal.core.util.readToList
import org.signal.core.util.select
import org.signal.core.util.withinTransaction
import org.thoughtcrime.securesms.database.AttachmentTable
import org.thoughtcrime.securesms.database.MessageTable
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.ThreadTable
import org.thoughtcrime.securesms.database.model.toBodyRangeList
import org.thoughtcrime.securesms.mms.OutgoingMessage
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.testing.MessageContentFuzzer
import org.thoughtcrime.securesms.testing.SignalActivityRule
import org.thoughtcrime.securesms.testing.assertIs
import org.thoughtcrime.securesms.util.MessageTableTestUtils
import org.whispersystems.signalservice.internal.push.Content
import org.whispersystems.signalservice.internal.push.EditMessage
import org.whispersystems.signalservice.internal.push.SyncMessage
import kotlin.time.Duration.Companion.seconds
@RunWith(AndroidJUnit4::class)
class EditMessageSyncProcessorTest {
companion object {
private val IGNORE_MESSAGE_COLUMNS = listOf(
MessageTable.DATE_RECEIVED,
MessageTable.NOTIFIED_TIMESTAMP,
MessageTable.REACTIONS_LAST_SEEN,
MessageTable.NOTIFIED
)
private val IGNORE_ATTACHMENT_COLUMNS = listOf(
AttachmentTable.TRANSFER_FILE
)
}
@get:Rule
val harness = SignalActivityRule()
private lateinit var processorV2: MessageContentProcessor
private lateinit var testResult: TestResults
private var envelopeTimestamp: Long = 0
@Before
fun setup() {
processorV2 = MessageContentProcessor(harness.context)
envelopeTimestamp = System.currentTimeMillis()
testResult = TestResults()
}
@Test
fun textMessage() {
var originalTimestamp = envelopeTimestamp + 200
for (i in 1..10) {
originalTimestamp += 400
val toRecipient = Recipient.resolved(harness.others[0])
val content = MessageContentFuzzer.fuzzTextMessage()
val metadata = MessageContentFuzzer.envelopeMetadata(harness.self.id, toRecipient.id)
val syncContent = Content.Builder().syncMessage(
SyncMessage.Builder().sent(
SyncMessage.Sent.Builder()
.destinationServiceId(metadata.destinationServiceId.toString())
.timestamp(originalTimestamp)
.expirationStartTimestamp(originalTimestamp)
.message(content.dataMessage)
.build()
).build()
).build()
SignalDatabase.recipients.setExpireMessages(toRecipient.id, content.dataMessage?.expireTimer ?: 0)
val syncTextMessage = TestMessage(
envelope = MessageContentFuzzer.envelope(originalTimestamp),
content = syncContent,
metadata = metadata,
serverDeliveredTimestamp = MessageContentFuzzer.fuzzServerDeliveredTimestamp(originalTimestamp)
)
val editTimestamp = originalTimestamp + 200
val editedContent = MessageContentFuzzer.fuzzTextMessage()
val editSyncContent = Content.Builder().syncMessage(
SyncMessage.Builder().sent(
SyncMessage.Sent.Builder()
.destinationServiceId(metadata.destinationServiceId.toString())
.timestamp(editTimestamp)
.expirationStartTimestamp(editTimestamp)
.editMessage(
EditMessage.Builder()
.dataMessage(editedContent.dataMessage)
.targetSentTimestamp(originalTimestamp)
.build()
)
.build()
).build()
).build()
val syncEditMessage = TestMessage(
envelope = MessageContentFuzzer.envelope(editTimestamp),
content = editSyncContent,
metadata = metadata,
serverDeliveredTimestamp = MessageContentFuzzer.fuzzServerDeliveredTimestamp(editTimestamp)
)
testResult.runSync(listOf(syncTextMessage, syncEditMessage))
SignalDatabase.recipients.setExpireMessages(toRecipient.id, (content.dataMessage?.expireTimer ?: 0) / 1000)
val originalTextMessage = OutgoingMessage(
threadRecipient = toRecipient,
sentTimeMillis = originalTimestamp,
body = content.dataMessage?.body ?: "",
expiresIn = content.dataMessage?.expireTimer?.seconds?.inWholeMilliseconds ?: 0,
isUrgent = true,
isSecure = true,
bodyRanges = content.dataMessage?.bodyRanges.toBodyRangeList()
)
val threadId = SignalDatabase.threads.getOrCreateThreadIdFor(toRecipient)
val originalMessageId = SignalDatabase.messages.insertMessageOutbox(originalTextMessage, threadId, false, null)
SignalDatabase.messages.markAsSent(originalMessageId, true)
if ((content.dataMessage?.expireTimer ?: 0) > 0) {
SignalDatabase.messages.markExpireStarted(originalMessageId, originalTimestamp)
}
val editMessage = OutgoingMessage(
threadRecipient = toRecipient,
sentTimeMillis = editTimestamp,
body = editedContent.dataMessage?.body ?: "",
expiresIn = content.dataMessage?.expireTimer?.seconds?.inWholeMilliseconds ?: 0,
isUrgent = true,
isSecure = true,
bodyRanges = editedContent.dataMessage?.bodyRanges.toBodyRangeList(),
messageToEdit = originalMessageId
)
val editMessageId = SignalDatabase.messages.insertMessageOutbox(editMessage, threadId, false, null)
SignalDatabase.messages.markAsSent(editMessageId, true)
if ((content.dataMessage?.expireTimer ?: 0) > 0) {
SignalDatabase.messages.markExpireStarted(editMessageId, originalTimestamp)
}
testResult.collectLocal()
testResult.assert()
}
}
private inner class TestResults {
private lateinit var localMessages: List<List<Pair<String, String?>>>
private lateinit var localAttachments: List<List<Pair<String, String?>>>
private lateinit var syncMessages: List<List<Pair<String, String?>>>
private lateinit var syncAttachments: List<List<Pair<String, String?>>>
fun collectLocal() {
harness.inMemoryLogger.clear()
localMessages = dumpMessages()
localAttachments = dumpAttachments()
cleanup()
}
fun runSync(messages: List<TestMessage>) {
messages.forEach { (envelope, content, metadata, serverDeliveredTimestamp) ->
if (content.syncMessage != null) {
processorV2.process(
envelope,
content,
metadata,
serverDeliveredTimestamp,
false
)
ThreadUtil.sleep(1)
}
}
harness.inMemoryLogger.clear()
syncMessages = dumpMessages()
syncAttachments = dumpAttachments()
cleanup()
}
fun cleanup() {
SignalDatabase.rawDatabase.withinTransaction { db ->
SignalDatabase.threads.deleteAllConversations()
db.execSQL("DELETE FROM sqlite_sequence WHERE name = '${MessageTable.TABLE_NAME}'")
db.execSQL("DELETE FROM sqlite_sequence WHERE name = '${ThreadTable.TABLE_NAME}'")
db.execSQL("DELETE FROM sqlite_sequence WHERE name = '${AttachmentTable.TABLE_NAME}'")
}
}
fun assert() {
syncMessages.zip(localMessages)
.forEach { (v2, v1) ->
v2.assertIs(v1)
}
syncAttachments.zip(localAttachments)
.forEach { (v2, v1) ->
v2.assertIs(v1)
}
}
private fun dumpMessages(): List<List<Pair<String, String?>>> {
return dumpTable(MessageTable.TABLE_NAME)
.map { row ->
val newRow = row.toMutableList()
newRow.removeIf { IGNORE_MESSAGE_COLUMNS.contains(it.first) }
newRow
}
}
private fun dumpAttachments(): List<List<Pair<String, String?>>> {
return dumpTable(AttachmentTable.TABLE_NAME)
.map { row ->
val newRow = row.toMutableList()
newRow.removeIf { IGNORE_ATTACHMENT_COLUMNS.contains(it.first) }
newRow
}
}
private fun dumpTable(table: String): List<List<Pair<String, String?>>> {
return SignalDatabase.rawDatabase
.select()
.from(table)
.run()
.readToList { cursor ->
val map: List<Pair<String, String?>> = cursor.columnNames.map { column ->
val index = cursor.getColumnIndex(column)
var data: String? = when (cursor.getType(index)) {
Cursor.FIELD_TYPE_BLOB -> Base64.encodeToString(cursor.getBlob(index), 0)
else -> cursor.getString(index)
}
if (table == MessageTable.TABLE_NAME && column == MessageTable.TYPE) {
data = MessageTableTestUtils.typeColumnToString(cursor.getLong(index))
}
column to data
}
map
}
}
}
}

View File

@@ -1,62 +0,0 @@
package org.thoughtcrime.securesms.messages
import android.app.Application
import androidx.test.core.app.ApplicationProvider
import org.junit.Rule
import org.thoughtcrime.securesms.messages.MessageContentProcessor.ExceptionMetadata
import org.thoughtcrime.securesms.messages.MessageContentProcessor.MessageState
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.testing.SignalActivityRule
import org.thoughtcrime.securesms.testing.TestProtos
import org.whispersystems.signalservice.api.messages.SignalServiceContent
import org.whispersystems.signalservice.internal.push.SignalServiceProtos
import org.whispersystems.signalservice.internal.serialize.protos.SignalServiceContentProto
abstract class MessageContentProcessorTest {
@get:Rule
val harness = SignalActivityRule()
protected fun MessageContentProcessor.doProcess(
messageState: MessageState = MessageState.DECRYPTED_OK,
content: SignalServiceContent,
exceptionMetadata: ExceptionMetadata = ExceptionMetadata("sender", 1),
timestamp: Long = 100L,
smsMessageId: Long = -1L
) {
process(messageState, content, exceptionMetadata, timestamp, smsMessageId)
}
protected fun createNormalContentTestSubject(): MessageContentProcessor {
val context = ApplicationProvider.getApplicationContext<Application>()
return MessageContentProcessor.forNormalContent(context)
}
/**
* Creates a valid ServiceContentProto with a data message which can be built via
* `injectDataMessage`. This function is intended to be built on-top of for more
* specific scenario in subclasses.
*
* Example can be seen in __handleStoryMessageTest
*/
protected fun createServiceContentWithDataMessage(
messageSender: Recipient = Recipient.resolved(harness.others.first()),
injectDataMessage: SignalServiceProtos.DataMessage.Builder.() -> Unit
): SignalServiceContentProto {
return TestProtos.build {
serviceContent(
localAddress = address(uuid = harness.self.requireServiceId().uuid()).build(),
metadata = metadata(
address = address(uuid = messageSender.requireServiceId().uuid()).build()
).build()
).apply {
content = content().apply {
dataMessage = dataMessage().apply {
injectDataMessage()
}.build()
}.build()
}.build()
}
}
}

View File

@@ -1,181 +0,0 @@
package org.thoughtcrime.securesms.messages
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.signal.core.util.requireLong
import org.signal.libsignal.zkgroup.groups.GroupMasterKey
import org.signal.storageservice.protos.groups.Member
import org.signal.storageservice.protos.groups.local.DecryptedGroup
import org.signal.storageservice.protos.groups.local.DecryptedMember
import org.thoughtcrime.securesms.database.MessageTable
import org.thoughtcrime.securesms.database.MmsHelper
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.model.DistributionListId
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord
import org.thoughtcrime.securesms.database.model.ParentStoryId
import org.thoughtcrime.securesms.database.model.StoryType
import org.thoughtcrime.securesms.mms.IncomingMediaMessage
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.testing.TestProtos
import org.whispersystems.signalservice.api.messages.SignalServiceContent
import org.whispersystems.signalservice.api.push.DistributionId
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage
import org.whispersystems.signalservice.internal.serialize.protos.SignalServiceContentProto
import kotlin.random.Random
@Suppress("ClassName")
class MessageContentProcessor__handleStoryMessageTest : MessageContentProcessorTest() {
@Before
fun setUp() {
SignalDatabase.mms.deleteAllThreads()
}
@After
fun tearDown() {
SignalDatabase.mms.deleteAllThreads()
}
@Test
fun givenContentWithADirectStoryReplyWhenIProcessThenIInsertAReplyInTheCorrectThread() {
val sender = Recipient.resolved(harness.others.first())
val senderThreadId = SignalDatabase.threads.getOrCreateThreadIdFor(sender)
val myStory = Recipient.resolved(SignalDatabase.distributionLists.getRecipientId(DistributionListId.MY_STORY)!!)
val myStoryThread = SignalDatabase.threads.getOrCreateThreadIdFor(myStory)
val expectedSentTime = 200L
val storyMessageId = MmsHelper.insert(
sentTimeMillis = expectedSentTime,
recipient = myStory,
storyType = StoryType.STORY_WITH_REPLIES,
threadId = myStoryThread
)
SignalDatabase.storySends.insert(
messageId = storyMessageId,
recipientIds = listOf(sender.id),
sentTimestamp = expectedSentTime,
allowsReplies = true,
distributionId = DistributionId.MY_STORY
)
val expectedBody = "Hello!"
val storyContent: SignalServiceContentProto = createServiceContentWithStoryContext(
messageSender = sender,
storyAuthor = harness.self,
storySentTimestamp = expectedSentTime
) {
body = expectedBody
}
runTestWithContent(contentProto = storyContent)
val replyId = SignalDatabase.mmsSms.getConversation(senderThreadId, 0, 1).use {
it.moveToFirst()
it.requireLong(MessageTable.ID)
}
val replyRecord = SignalDatabase.mms.getMessageRecord(replyId) as MediaMmsMessageRecord
assertEquals(ParentStoryId.DirectReply(storyMessageId).serialize(), replyRecord.parentStoryId!!.serialize())
assertEquals(expectedBody, replyRecord.body)
SignalDatabase.mms.deleteAllThreads()
}
@Test
fun givenContentWithAGroupStoryReplyWhenIProcessThenIInsertAReplyToTheCorrectStory() {
val sender = Recipient.resolved(harness.others[0])
val groupMasterKey = GroupMasterKey(Random.nextBytes(GroupMasterKey.SIZE))
val decryptedGroupState = DecryptedGroup.newBuilder()
.addAllMembers(
listOf(
DecryptedMember.newBuilder()
.setUuid(harness.self.requireServiceId().toByteString())
.setJoinedAtRevision(0)
.setRole(Member.Role.DEFAULT)
.build(),
DecryptedMember.newBuilder()
.setUuid(sender.requireServiceId().toByteString())
.setJoinedAtRevision(0)
.setRole(Member.Role.DEFAULT)
.build()
)
)
.setRevision(0)
.build()
val group = SignalDatabase.groups.create(
groupMasterKey,
decryptedGroupState
)
val groupRecipient = Recipient.externalGroupExact(group)
val threadForGroup = SignalDatabase.threads.getOrCreateThreadIdFor(groupRecipient)
val insertResult = MmsHelper.insert(
message = IncomingMediaMessage(
from = sender.id,
sentTimeMillis = 100L,
serverTimeMillis = 101L,
receivedTimeMillis = 102L,
storyType = StoryType.STORY_WITH_REPLIES
),
threadId = threadForGroup
)
val expectedBody = "Hello, World!"
val storyContent: SignalServiceContentProto = createServiceContentWithStoryContext(
messageSender = sender,
storyAuthor = sender,
storySentTimestamp = 100L
) {
groupV2 = TestProtos.build { groupContextV2(masterKeyBytes = groupMasterKey.serialize()).build() }
body = expectedBody
}
runTestWithContent(storyContent)
val replyId = SignalDatabase.mms.getStoryReplies(insertResult.get().messageId).use { cursor ->
assertEquals(1, cursor.count)
cursor.moveToFirst()
cursor.requireLong(MessageTable.ID)
}
val replyRecord = SignalDatabase.mms.getMessageRecord(replyId) as MediaMmsMessageRecord
assertEquals(ParentStoryId.GroupReply(insertResult.get().messageId).serialize(), replyRecord.parentStoryId?.serialize())
assertEquals(threadForGroup, replyRecord.threadId)
assertEquals(expectedBody, replyRecord.body)
SignalDatabase.mms.deleteGroupStoryReplies(insertResult.get().messageId)
SignalDatabase.mms.deleteAllThreads()
}
/**
* Creates a ServiceContent proto with a StoryContext, and then
* uses `injectDataMessage` to fill in the data message object.
*/
private fun createServiceContentWithStoryContext(
messageSender: Recipient,
storyAuthor: Recipient,
storySentTimestamp: Long,
injectDataMessage: DataMessage.Builder.() -> Unit
): SignalServiceContentProto {
return createServiceContentWithDataMessage(messageSender) {
storyContext = TestProtos.build {
storyContext(
sentTimestamp = storySentTimestamp,
authorUuid = storyAuthor.requireServiceId().toString()
).build()
}
injectDataMessage()
}
}
private fun runTestWithContent(contentProto: SignalServiceContentProto) {
val content = SignalServiceContent.createFromProto(contentProto)
val testSubject = createNormalContentTestSubject()
testSubject.doProcess(content = content!!)
}
}

View File

@@ -1,33 +0,0 @@
package org.thoughtcrime.securesms.messages
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
import org.thoughtcrime.securesms.database.SignalDatabase
import org.whispersystems.signalservice.api.messages.SignalServiceContent
import org.whispersystems.signalservice.internal.serialize.protos.SignalServiceContentProto
@Suppress("ClassName")
class MessageContentProcessor__handleTextMessageTest : MessageContentProcessorTest() {
@Test
fun givenContentWithATextMessageWhenIProcessThenIInsertTheTextMessage() {
val testSubject: MessageContentProcessor = createNormalContentTestSubject()
val expectedBody = "Hello, World!"
val contentProto: SignalServiceContentProto = createServiceContentWithDataMessage {
body = expectedBody
}
val content = SignalServiceContent.createFromProto(contentProto)
// WHEN
testSubject.doProcess(content = content!!)
// THEN
val record = SignalDatabase.sms.getMessageRecord(1)
val threadSize = SignalDatabase.mmsSms.getConversationCount(record.threadId)
assertEquals(1, threadSize)
assertTrue(record.isSecure)
assertEquals(expectedBody, record.body)
}
}

View File

@@ -0,0 +1,84 @@
package org.thoughtcrime.securesms.messages
import androidx.test.ext.junit.runners.AndroidJUnit4
import okio.ByteString.Companion.toByteString
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.thoughtcrime.securesms.database.GroupReceiptTable
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.messages.SignalServiceProtoUtil.buildWith
import org.thoughtcrime.securesms.testing.GroupTestingUtils
import org.thoughtcrime.securesms.testing.GroupTestingUtils.asMember
import org.thoughtcrime.securesms.testing.MessageContentFuzzer
import org.thoughtcrime.securesms.testing.SignalActivityRule
import org.thoughtcrime.securesms.testing.assertIs
import org.thoughtcrime.securesms.util.MessageTableTestUtils
import org.whispersystems.signalservice.internal.push.DataMessage
import org.whispersystems.signalservice.internal.push.GroupContextV2
@Suppress("ClassName")
@RunWith(AndroidJUnit4::class)
class MessageContentProcessor__recipientStatusTest {
@get:Rule
val harness = SignalActivityRule()
private lateinit var processor: MessageContentProcessor
private var envelopeTimestamp: Long = 0
@Before
fun setup() {
processor = MessageContentProcessor(harness.context)
envelopeTimestamp = System.currentTimeMillis()
}
/**
* Process sync group sent text transcript with partial send and then process second sync with recipient update
* flag set to true with the rest of the send completed.
*/
@Test
fun syncGroupSentTextMessageWithRecipientUpdateFollowup() {
val (groupId, masterKey, groupRecipientId) = GroupTestingUtils.insertGroup(revision = 0, harness.self.asMember(), harness.others[0].asMember(), harness.others[1].asMember())
val groupContextV2 = GroupContextV2.Builder().revision(0).masterKey(masterKey.serialize().toByteString()).build()
val initialTextMessage = DataMessage.Builder().buildWith {
body = MessageContentFuzzer.string()
groupV2 = groupContextV2
timestamp = envelopeTimestamp
}
processor.process(
envelope = MessageContentFuzzer.envelope(envelopeTimestamp),
content = MessageContentFuzzer.syncSentTextMessage(initialTextMessage, deliveredTo = listOf(harness.others[0])),
metadata = MessageContentFuzzer.envelopeMetadata(harness.self.id, harness.self.id, groupId = groupId),
serverDeliveredTimestamp = MessageContentFuzzer.fuzzServerDeliveredTimestamp(envelopeTimestamp)
)
val threadId = SignalDatabase.threads.getThreadIdFor(groupRecipientId)!!
val firstSyncMessages = MessageTableTestUtils.getMessages(threadId)
val firstMessageId = firstSyncMessages[0].id
val firstReceiptInfo = SignalDatabase.groupReceipts.getGroupReceiptInfo(firstMessageId)
processor.process(
envelope = MessageContentFuzzer.envelope(envelopeTimestamp),
content = MessageContentFuzzer.syncSentTextMessage(initialTextMessage, deliveredTo = listOf(harness.others[0], harness.others[1]), recipientUpdate = true),
metadata = MessageContentFuzzer.envelopeMetadata(harness.self.id, harness.self.id, groupId = groupId),
serverDeliveredTimestamp = MessageContentFuzzer.fuzzServerDeliveredTimestamp(envelopeTimestamp)
)
val secondSyncMessages = MessageTableTestUtils.getMessages(threadId)
val secondReceiptInfo = SignalDatabase.groupReceipts.getGroupReceiptInfo(firstMessageId)
firstSyncMessages.size assertIs 1
firstSyncMessages[0].body assertIs initialTextMessage.body
firstReceiptInfo.first { it.recipientId == harness.others[0] }.status assertIs GroupReceiptTable.STATUS_UNDELIVERED
firstReceiptInfo.first { it.recipientId == harness.others[1] }.status assertIs GroupReceiptTable.STATUS_UNKNOWN
secondSyncMessages.size assertIs 1
secondSyncMessages[0].body assertIs initialTextMessage.body
secondReceiptInfo.first { it.recipientId == harness.others[0] }.status assertIs GroupReceiptTable.STATUS_UNDELIVERED
secondReceiptInfo.first { it.recipientId == harness.others[1] }.status assertIs GroupReceiptTable.STATUS_UNDELIVERED
}
}

View File

@@ -0,0 +1,196 @@
package org.thoughtcrime.securesms.messages
import androidx.test.ext.junit.runners.AndroidJUnit4
import io.mockk.every
import io.mockk.mockkObject
import io.mockk.mockkStatic
import io.mockk.unmockkStatic
import okio.ByteString
import org.junit.After
import org.junit.Before
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.signal.core.util.logging.Log
import org.signal.libsignal.protocol.ecc.Curve
import org.signal.libsignal.protocol.ecc.ECKeyPair
import org.signal.libsignal.zkgroup.profiles.ProfileKey
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil
import org.thoughtcrime.securesms.dependencies.InstrumentationApplicationDependencyProvider
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.testing.AliceClient
import org.thoughtcrime.securesms.testing.BobClient
import org.thoughtcrime.securesms.testing.Entry
import org.thoughtcrime.securesms.testing.FakeClientHelpers
import org.thoughtcrime.securesms.testing.SignalActivityRule
import org.thoughtcrime.securesms.testing.awaitFor
import org.whispersystems.signalservice.internal.push.Envelope
import org.whispersystems.signalservice.internal.websocket.WebSocketMessage
import org.whispersystems.signalservice.internal.websocket.WebSocketRequestMessage
import java.util.regex.Pattern
import kotlin.random.Random
import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.seconds
import android.util.Log as AndroidLog
/**
* Sends N messages from Bob to Alice to track performance of Alice's processing of messages.
*/
@Ignore("Ignore test in normal testing as it's a performance test with no assertions")
@RunWith(AndroidJUnit4::class)
class MessageProcessingPerformanceTest {
companion object {
private val TAG = Log.tag(MessageProcessingPerformanceTest::class.java)
private val TIMING_TAG = "TIMING_$TAG".substring(0..23)
private val DECRYPTION_TIME_PATTERN = Pattern.compile("^Decrypted (?<count>\\d+) envelopes in (?<duration>\\d+) ms.*$")
}
@get:Rule
val harness = SignalActivityRule()
private val trustRoot: ECKeyPair = Curve.generateKeyPair()
@Before
fun setup() {
mockkStatic(UnidentifiedAccessUtil::class)
every { UnidentifiedAccessUtil.getCertificateValidator() } returns FakeClientHelpers.noOpCertificateValidator
mockkObject(MessageContentProcessor)
every { MessageContentProcessor.create(harness.application) } returns TimingMessageContentProcessor(harness.application)
}
@After
fun after() {
unmockkStatic(UnidentifiedAccessUtil::class)
unmockkStatic(MessageContentProcessor::class)
}
@Test
fun testPerformance() {
val aliceClient = AliceClient(
serviceId = harness.self.requireServiceId(),
e164 = harness.self.requireE164(),
trustRoot = trustRoot
)
val bob = Recipient.resolved(harness.others[0])
val bobClient = BobClient(
serviceId = bob.requireServiceId(),
e164 = bob.requireE164(),
identityKeyPair = harness.othersKeys[0],
trustRoot = trustRoot,
profileKey = ProfileKey(bob.profileKey)
)
// Send the initial messages to get past the prekey phase
establishSession(aliceClient, bobClient, bob)
// Have Bob generate N messages that will be received by Alice
val messageCount = 100
val envelopes = generateInboundEnvelopes(bobClient, messageCount)
val firstTimestamp = envelopes.first().timestamp
val lastTimestamp = envelopes.last().timestamp ?: 0
// Inject the envelopes into the websocket
Thread {
for (envelope in envelopes) {
Log.i(TIMING_TAG, "Retrieved envelope! ${envelope.timestamp}")
InstrumentationApplicationDependencyProvider.injectWebSocketMessage(envelope.toWebSocketPayload())
}
InstrumentationApplicationDependencyProvider.injectWebSocketMessage(webSocketTombstone())
}.start()
// Wait until they've all been fully decrypted + processed
harness
.inMemoryLogger
.getLockForUntil(TimingMessageContentProcessor.endTagPredicate(lastTimestamp))
.awaitFor(1.minutes)
harness.inMemoryLogger.flush()
// Process logs for timing data
val entries = harness.inMemoryLogger.entries()
// Calculate decryption average
val totalDecryptDuration: Long = entries
.mapNotNull { entry -> entry.message?.let { DECRYPTION_TIME_PATTERN.matcher(it) } }
.filter { it.matches() }
.drop(1) // Ignore the first message, which represents the prekey exchange
.sumOf { it.group("duration")!!.toLong() }
AndroidLog.w(TAG, "Decryption: Average runtime: ${totalDecryptDuration.toFloat() / messageCount.toFloat()}ms")
// Calculate MessageContentProcessor
val takeLast: List<Entry> = entries.filter { it.tag == TimingMessageContentProcessor.TAG }.drop(2)
val iterator = takeLast.iterator()
var processCount = 0L
var processDuration = 0L
while (iterator.hasNext()) {
val start = iterator.next()
val end = iterator.next()
processCount++
processDuration += end.timestamp - start.timestamp
}
AndroidLog.w(TAG, "MessageContentProcessor.process: Average runtime: ${processDuration.toFloat() / processCount.toFloat()}ms")
// Calculate messages per second from "retrieving" first message post session initialization to processing last message
val start = entries.first { it.message == "Retrieved envelope! $firstTimestamp" }
val end = entries.first { it.message == TimingMessageContentProcessor.endTag(lastTimestamp) }
val duration = (end.timestamp - start.timestamp).toFloat() / 1000f
val messagePerSecond = messageCount.toFloat() / duration
AndroidLog.w(TAG, "Processing $messageCount messages took ${duration}s or ${messagePerSecond}m/s")
}
private fun establishSession(aliceClient: AliceClient, bobClient: BobClient, bob: Recipient) {
// Send message from Bob to Alice (self)
val firstPreKeyMessageTimestamp = System.currentTimeMillis()
val encryptedEnvelope = bobClient.encrypt(firstPreKeyMessageTimestamp)
val aliceProcessFirstMessageLatch = harness
.inMemoryLogger
.getLockForUntil(TimingMessageContentProcessor.endTagPredicate(firstPreKeyMessageTimestamp))
Thread { aliceClient.process(encryptedEnvelope, System.currentTimeMillis()) }.start()
aliceProcessFirstMessageLatch.awaitFor(15.seconds)
// Send message from Alice to Bob
val aliceNow = System.currentTimeMillis()
bobClient.decrypt(aliceClient.encrypt(aliceNow, bob), aliceNow)
}
private fun generateInboundEnvelopes(bobClient: BobClient, count: Int): List<Envelope> {
val envelopes = ArrayList<Envelope>(count)
var now = System.currentTimeMillis()
for (i in 0..count) {
envelopes += bobClient.encrypt(now)
now += 3
}
return envelopes
}
private fun webSocketTombstone(): ByteString {
return WebSocketMessage(request = WebSocketRequestMessage(verb = "PUT", path = "/api/v1/queue/empty")).encodeByteString()
}
private fun Envelope.toWebSocketPayload(): ByteString {
return WebSocketMessage(
type = WebSocketMessage.Type.REQUEST,
request = WebSocketRequestMessage(
verb = "PUT",
path = "/api/v1/message",
id = Random(System.currentTimeMillis()).nextLong(),
headers = listOf("X-Signal-Timestamp: ${this.timestamp}"),
body = this.encodeByteString()
)
).encodeByteString()
}
}

View File

@@ -0,0 +1,225 @@
/*
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.messages
import androidx.test.ext.junit.runners.AndroidJUnit4
import io.mockk.every
import io.mockk.mockkStatic
import io.mockk.slot
import io.mockk.unmockkStatic
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.jobs.ThreadUpdateJob
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.testing.GroupTestingUtils
import org.thoughtcrime.securesms.testing.MessageContentFuzzer
import org.thoughtcrime.securesms.testing.SignalActivityRule
import org.thoughtcrime.securesms.testing.assertIs
import java.util.UUID
@Suppress("ClassName")
@RunWith(AndroidJUnit4::class)
class SyncMessageProcessorTest_readSyncs {
@get:Rule
val harness = SignalActivityRule(createGroup = true)
private lateinit var alice: RecipientId
private lateinit var bob: RecipientId
private lateinit var group: GroupTestingUtils.TestGroupInfo
private lateinit var processor: MessageContentProcessor
@Before
fun setUp() {
alice = harness.others[0]
bob = harness.others[1]
group = harness.group!!
processor = MessageContentProcessor(harness.context)
val threadIdSlot = slot<Long>()
mockkStatic(ThreadUpdateJob::class)
every { ThreadUpdateJob.enqueue(capture(threadIdSlot)) } answers {
SignalDatabase.threads.update(threadIdSlot.captured, false)
}
}
@After
fun tearDown() {
unmockkStatic(ThreadUpdateJob::class)
}
@Test
fun handleSynchronizeReadMessage() {
val messageHelper = MessageHelper()
val message1Timestamp = messageHelper.incomingText().timestamp
val message2Timestamp = messageHelper.incomingText().timestamp
val threadId = SignalDatabase.threads.getThreadIdFor(alice)!!
var threadRecord = SignalDatabase.threads.getThreadRecord(threadId)!!
threadRecord.unreadCount assertIs 2
messageHelper.syncReadMessage(alice to message1Timestamp, alice to message2Timestamp)
threadRecord = SignalDatabase.threads.getThreadRecord(threadId)!!
threadRecord.unreadCount assertIs 0
}
@Test
fun handleSynchronizeReadMessageMissingTimestamp() {
val messageHelper = MessageHelper()
messageHelper.incomingText().timestamp
val message2Timestamp = messageHelper.incomingText().timestamp
val threadId = SignalDatabase.threads.getThreadIdFor(alice)!!
var threadRecord = SignalDatabase.threads.getThreadRecord(threadId)!!
threadRecord.unreadCount assertIs 2
messageHelper.syncReadMessage(alice to message2Timestamp)
threadRecord = SignalDatabase.threads.getThreadRecord(threadId)!!
threadRecord.unreadCount assertIs 0
}
@Test
fun handleSynchronizeReadWithEdits() {
val messageHelper = MessageHelper()
val message1Timestamp = messageHelper.incomingText().timestamp
messageHelper.syncReadMessage(alice to message1Timestamp)
val editMessage1Timestamp1 = messageHelper.incomingEditText(message1Timestamp).timestamp
val editMessage1Timestamp2 = messageHelper.incomingEditText(editMessage1Timestamp1).timestamp
val message2Timestamp = messageHelper.incomingMedia().timestamp
val threadId = SignalDatabase.threads.getThreadIdFor(alice)!!
var threadRecord = SignalDatabase.threads.getThreadRecord(threadId)!!
threadRecord.unreadCount assertIs 2
messageHelper.syncReadMessage(alice to message2Timestamp, alice to editMessage1Timestamp1, alice to editMessage1Timestamp2)
threadRecord = SignalDatabase.threads.getThreadRecord(threadId)!!
threadRecord.unreadCount assertIs 0
}
@Test
fun handleSynchronizeReadWithEditsInGroup() {
val messageHelper = MessageHelper()
val message1Timestamp = messageHelper.incomingText(sender = alice, destination = group.recipientId).timestamp
messageHelper.syncReadMessage(alice to message1Timestamp)
val editMessage1Timestamp1 = messageHelper.incomingEditText(targetTimestamp = message1Timestamp, sender = alice, destination = group.recipientId).timestamp
val editMessage1Timestamp2 = messageHelper.incomingEditText(targetTimestamp = editMessage1Timestamp1, sender = alice, destination = group.recipientId).timestamp
val message2Timestamp = messageHelper.incomingMedia(sender = bob, destination = group.recipientId).timestamp
val threadId = SignalDatabase.threads.getThreadIdFor(group.recipientId)!!
var threadRecord = SignalDatabase.threads.getThreadRecord(threadId)!!
threadRecord.unreadCount assertIs 2
messageHelper.syncReadMessage(bob to message2Timestamp, alice to editMessage1Timestamp1, alice to editMessage1Timestamp2)
threadRecord = SignalDatabase.threads.getThreadRecord(threadId)!!
threadRecord.unreadCount assertIs 0
}
private inner class MessageHelper(var startTime: Long = System.currentTimeMillis()) {
fun incomingText(sender: RecipientId = alice, destination: RecipientId = harness.self.id): MessageData {
startTime += 1000
val messageData = MessageData(timestamp = startTime)
processor.process(
envelope = MessageContentFuzzer.envelope(messageData.timestamp, serverGuid = messageData.serverGuid),
content = MessageContentFuzzer.fuzzTextMessage(
sentTimestamp = messageData.timestamp,
groupContextV2 = if (destination == group.recipientId) group.groupV2Context else null
),
metadata = MessageContentFuzzer.envelopeMetadata(
source = sender,
destination = harness.self.id,
groupId = if (destination == group.recipientId) group.groupId else null
),
serverDeliveredTimestamp = messageData.timestamp + 10
)
return messageData
}
fun incomingMedia(sender: RecipientId = alice, destination: RecipientId = harness.self.id): MessageData {
startTime += 1000
val messageData = MessageData(timestamp = startTime)
processor.process(
envelope = MessageContentFuzzer.envelope(messageData.timestamp, serverGuid = messageData.serverGuid),
content = MessageContentFuzzer.fuzzStickerMediaMessage(
sentTimestamp = messageData.timestamp,
groupContextV2 = if (destination == group.recipientId) group.groupV2Context else null
),
metadata = MessageContentFuzzer.envelopeMetadata(
source = sender,
destination = harness.self.id,
groupId = if (destination == group.recipientId) group.groupId else null
),
serverDeliveredTimestamp = messageData.timestamp + 10
)
return messageData
}
fun incomingEditText(targetTimestamp: Long = System.currentTimeMillis(), sender: RecipientId = alice, destination: RecipientId = harness.self.id): MessageData {
startTime += 1000
val messageData = MessageData(timestamp = startTime)
processor.process(
envelope = MessageContentFuzzer.envelope(messageData.timestamp, serverGuid = messageData.serverGuid),
content = MessageContentFuzzer.editTextMessage(
targetTimestamp = targetTimestamp,
editedDataMessage = MessageContentFuzzer.fuzzTextMessage(
sentTimestamp = messageData.timestamp,
groupContextV2 = if (destination == group.recipientId) group.groupV2Context else null
).dataMessage!!
),
metadata = MessageContentFuzzer.envelopeMetadata(
source = sender,
destination = harness.self.id,
groupId = if (destination == group.recipientId) group.groupId else null
),
serverDeliveredTimestamp = messageData.timestamp + 10
)
return messageData
}
fun syncReadMessage(vararg reads: Pair<RecipientId, Long>): MessageData {
startTime += 1000
val messageData = MessageData(timestamp = startTime)
processor.process(
envelope = MessageContentFuzzer.envelope(messageData.timestamp, serverGuid = messageData.serverGuid),
content = MessageContentFuzzer.syncReadsMessage(reads.toList()),
metadata = MessageContentFuzzer.envelopeMetadata(harness.self.id, harness.self.id, sourceDeviceId = 2),
serverDeliveredTimestamp = messageData.timestamp + 10
)
return messageData
}
}
private data class MessageData(val serverGuid: UUID = UUID.randomUUID(), val timestamp: Long)
}

View File

@@ -0,0 +1,12 @@
package org.thoughtcrime.securesms.messages
import org.whispersystems.signalservice.api.crypto.EnvelopeMetadata
import org.whispersystems.signalservice.internal.push.Content
import org.whispersystems.signalservice.internal.push.Envelope
data class TestMessage(
val envelope: Envelope,
val content: Content,
val metadata: EnvelopeMetadata,
val serverDeliveredTimestamp: Long
)

View File

@@ -0,0 +1,28 @@
package org.thoughtcrime.securesms.messages
import android.content.Context
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.testing.LogPredicate
import org.thoughtcrime.securesms.util.SignalLocalMetrics
import org.whispersystems.signalservice.api.crypto.EnvelopeMetadata
import org.whispersystems.signalservice.internal.push.Content
import org.whispersystems.signalservice.internal.push.Envelope
class TimingMessageContentProcessor(context: Context) : MessageContentProcessor(context) {
companion object {
val TAG = Log.tag(TimingMessageContentProcessor::class.java)
fun endTagPredicate(timestamp: Long): LogPredicate = { entry ->
entry.tag == TAG && entry.message == endTag(timestamp)
}
private fun startTag(timestamp: Long) = "$timestamp start"
fun endTag(timestamp: Long) = "$timestamp end"
}
override fun process(envelope: Envelope, content: Content, metadata: EnvelopeMetadata, serverDeliveredTimestamp: Long, processingEarlyContent: Boolean, localMetric: SignalLocalMetrics.MessageReceive?) {
Log.d(TAG, startTag(envelope.timestamp!!))
super.process(envelope, content, metadata, serverDeliveredTimestamp, processingEarlyContent, localMetric)
Log.d(TAG, endTag(envelope.timestamp!!))
}
}

View File

@@ -33,6 +33,7 @@ import org.thoughtcrime.securesms.testing.SignalActivityRule
import org.thoughtcrime.securesms.testing.assertIsNotNull
import org.thoughtcrime.securesms.testing.assertIsNull
import org.thoughtcrime.securesms.testing.success
import org.whispersystems.signalservice.api.util.Usernames
import org.whispersystems.signalservice.internal.push.ReserveUsernameResponse
import java.util.concurrent.TimeUnit
@@ -56,27 +57,10 @@ class UsernameEditFragmentTest {
InstrumentationApplicationDependencyProvider.clearHandlers()
}
@Test
fun testUsernameCreationInRegistration() {
val scenario = createScenario(true)
scenario.moveToState(Lifecycle.State.RESUMED)
onView(withId(R.id.toolbar)).check { view, noViewFoundException ->
noViewFoundException.assertIsNull()
val toolbar = view as Toolbar
toolbar.navigationIcon.assertIsNull()
}
onView(withText(R.string.UsernameEditFragment__add_a_username)).check(matches(isDisplayed()))
onView(withContentDescription(R.string.load_more_header__loading)).check(matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE)))
}
@Ignore("Flakey espresso test.")
@Test
fun testUsernameCreationOutsideOfRegistration() {
val scenario = createScenario()
val scenario = createScenario(UsernameEditMode.NORMAL)
scenario.moveToState(Lifecycle.State.RESUMED)
@@ -96,18 +80,18 @@ class UsernameEditFragmentTest {
fun testNicknameUpdateHappyPath() {
val nickname = "Spiderman"
val discriminator = "4578"
val username = "$nickname${UsernameState.DELIMITER}$discriminator"
val username = "$nickname${Usernames.DELIMITER}$discriminator"
InstrumentationApplicationDependencyProvider.addMockWebRequestHandlers(
Put("/v1/accounts/username/reserved") {
MockResponse().success(ReserveUsernameResponse(username, "reservationToken"))
MockResponse().success(ReserveUsernameResponse(username))
},
Put("/v1/accounts/username/confirm") {
MockResponse().success()
}
)
val scenario = createScenario(isInRegistration = true)
val scenario = createScenario(UsernameEditMode.NORMAL)
scenario.moveToState(Lifecycle.State.RESUMED)
onView(withId(R.id.username_text)).perform(typeText(nickname))
@@ -131,8 +115,8 @@ class UsernameEditFragmentTest {
onView(withId(R.id.username_done_button)).check(matches(isNotEnabled()))
}
private fun createScenario(isInRegistration: Boolean = false): FragmentScenario<UsernameEditFragment> {
val fragmentArgs = UsernameEditFragmentArgs.Builder().setIsInRegistration(isInRegistration).build().toBundle()
private fun createScenario(mode: UsernameEditMode = UsernameEditMode.NORMAL): FragmentScenario<UsernameEditFragment> {
val fragmentArgs = UsernameEditFragmentArgs.Builder().setMode(mode).build().toBundle()
return launchFragmentInContainer(
fragmentArgs = fragmentArgs,
themeResId = R.style.Signal_DayNight_NoActionBar

View File

@@ -27,7 +27,7 @@ class SafetyNumberBottomSheetRepositoryTest {
@Test
fun givenIOnlyHave1to1Destinations_whenIGetBuckets_thenIOnlyHaveContactsBucketContainingAllRecipients() {
val recipients = harness.others
val destinations = harness.others.map { ContactSearchKey.RecipientSearchKey.KnownRecipient(it) }
val destinations = harness.others.map { ContactSearchKey.RecipientSearchKey(it, false) }
val result = subjectUnderTest.getBuckets(recipients, destinations).test()
@@ -42,7 +42,7 @@ class SafetyNumberBottomSheetRepositoryTest {
fun givenIOnlyHaveASingle1to1Destination_whenIGetBuckets_thenIOnlyHaveContactsBucketContainingAllRecipients() {
// GIVEN
val recipients = harness.others
val destination = harness.others.take(1).map { ContactSearchKey.RecipientSearchKey.KnownRecipient(it) }
val destination = harness.others.take(1).map { ContactSearchKey.RecipientSearchKey(it, false) }
// WHEN
val result = subjectUnderTest.getBuckets(recipients, destination).test(1)
@@ -59,7 +59,7 @@ class SafetyNumberBottomSheetRepositoryTest {
// GIVEN
val distributionListMembers = harness.others.take(5)
val distributionList = SignalDatabase.distributionLists.createList("ListA", distributionListMembers)!!
val destinationKey = ContactSearchKey.RecipientSearchKey.Story(SignalDatabase.distributionLists.getRecipientId(distributionList)!!)
val destinationKey = ContactSearchKey.RecipientSearchKey(SignalDatabase.distributionLists.getRecipientId(distributionList)!!, true)
// WHEN
val result = subjectUnderTest.getBuckets(harness.others, listOf(destinationKey)).test(1)
@@ -82,7 +82,7 @@ class SafetyNumberBottomSheetRepositoryTest {
val distributionListMembers = harness.others.take(5)
val toRemove = distributionListMembers.last()
val distributionList = SignalDatabase.distributionLists.createList("ListA", distributionListMembers)!!
val destinationKey = ContactSearchKey.RecipientSearchKey.Story(SignalDatabase.distributionLists.getRecipientId(distributionList)!!)
val destinationKey = ContactSearchKey.RecipientSearchKey(SignalDatabase.distributionLists.getRecipientId(distributionList)!!, true)
val testSubscriber = subjectUnderTest.getBuckets(distributionListMembers, listOf(destinationKey)).test(2)
testScheduler.triggerActions()
@@ -108,7 +108,7 @@ class SafetyNumberBottomSheetRepositoryTest {
// GIVEN
val distributionListMembers = harness.others.take(5)
val distributionList = SignalDatabase.distributionLists.createList("ListA", distributionListMembers)!!
val destinationKey = ContactSearchKey.RecipientSearchKey.Story(SignalDatabase.distributionLists.getRecipientId(distributionList)!!)
val destinationKey = ContactSearchKey.RecipientSearchKey(SignalDatabase.distributionLists.getRecipientId(distributionList)!!, true)
val testSubscriber = subjectUnderTest.getBuckets(distributionListMembers, listOf(destinationKey)).test(2)
testScheduler.triggerActions()

View File

@@ -0,0 +1,164 @@
package org.thoughtcrime.securesms.storage
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.signal.core.util.Base64
import org.signal.core.util.update
import org.thoughtcrime.securesms.database.RecipientTable
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.recipients.RecipientId
import org.whispersystems.signalservice.api.push.ServiceId.ACI
import org.whispersystems.signalservice.api.push.ServiceId.PNI
import org.whispersystems.signalservice.api.storage.SignalContactRecord
import org.whispersystems.signalservice.api.storage.StorageId
import org.whispersystems.signalservice.internal.storage.protos.ContactRecord
import java.util.UUID
@RunWith(AndroidJUnit4::class)
class ContactRecordProcessorTest {
@Before
fun setup() {
SignalStore.account().setE164(E164_SELF)
SignalStore.account().setAci(ACI_SELF)
SignalStore.account().setPni(PNI_SELF)
}
@Test
fun process_splitContact_normalSplit_twoRecords() {
// GIVEN
val originalId = SignalDatabase.recipients.getAndPossiblyMerge(ACI_A, PNI_A, E164_A)
setStorageId(originalId, STORAGE_ID_A)
val remote1 = buildRecord(
STORAGE_ID_B,
ContactRecord(
aci = ACI_A.toString(),
unregisteredAtTimestamp = 100
)
)
val remote2 = buildRecord(
STORAGE_ID_C,
ContactRecord(
pni = PNI_A.toString(),
e164 = E164_A
)
)
// WHEN
val subject = ContactRecordProcessor()
subject.process(listOf(remote1, remote2), StorageSyncHelper.KEY_GENERATOR)
// THEN
val byAci: RecipientId = SignalDatabase.recipients.getByAci(ACI_A).get()
val byE164: RecipientId = SignalDatabase.recipients.getByE164(E164_A).get()
val byPni: RecipientId = SignalDatabase.recipients.getByPni(PNI_A).get()
assertEquals(originalId, byAci)
assertEquals(byE164, byPni)
assertNotEquals(byAci, byE164)
}
@Test
fun process_splitContact_normalSplit_oneRecord() {
// GIVEN
val originalId = SignalDatabase.recipients.getAndPossiblyMerge(ACI_A, PNI_A, E164_A)
setStorageId(originalId, STORAGE_ID_A)
val remote = buildRecord(
STORAGE_ID_B,
ContactRecord(
aci = ACI_A.toString(),
unregisteredAtTimestamp = 100
)
)
// WHEN
val subject = ContactRecordProcessor()
subject.process(listOf(remote), StorageSyncHelper.KEY_GENERATOR)
// THEN
val byAci: RecipientId = SignalDatabase.recipients.getByAci(ACI_A).get()
val byE164: RecipientId = SignalDatabase.recipients.getByE164(E164_A).get()
val byPni: RecipientId = SignalDatabase.recipients.getByPni(PNI_A).get()
assertEquals(originalId, byAci)
assertEquals(byE164, byPni)
assertNotEquals(byAci, byE164)
}
@Test
fun process_splitContact_doNotSplitIfAciRecordIsRegistered() {
// GIVEN
val originalId = SignalDatabase.recipients.getAndPossiblyMerge(ACI_A, PNI_A, E164_A)
setStorageId(originalId, STORAGE_ID_A)
val remote1 = buildRecord(
STORAGE_ID_B,
ContactRecord(
aci = ACI_A.toString(),
unregisteredAtTimestamp = 0
)
)
val remote2 = buildRecord(
STORAGE_ID_C,
ContactRecord(
aci = PNI_A.toString(),
pni = PNI_A.toString(),
e164 = E164_A
)
)
// WHEN
val subject = ContactRecordProcessor()
subject.process(listOf(remote1, remote2), StorageSyncHelper.KEY_GENERATOR)
// THEN
val byAci: RecipientId = SignalDatabase.recipients.getByAci(ACI_A).get()
val byE164: RecipientId = SignalDatabase.recipients.getByE164(E164_A).get()
val byPni: RecipientId = SignalDatabase.recipients.getByPni(PNI_A).get()
assertEquals(originalId, byAci)
assertEquals(byE164, byPni)
assertEquals(byAci, byE164)
}
private fun buildRecord(id: StorageId, record: ContactRecord): SignalContactRecord {
return SignalContactRecord(id, record)
}
private fun setStorageId(recipientId: RecipientId, storageId: StorageId) {
SignalDatabase.rawDatabase
.update(RecipientTable.TABLE_NAME)
.values(RecipientTable.STORAGE_SERVICE_ID to Base64.encodeWithPadding(storageId.raw))
.where("${RecipientTable.ID} = ?", recipientId)
.run()
}
companion object {
val ACI_A = ACI.from(UUID.fromString("aaaa0000-5a76-47fa-a98a-7e72c948a82e"))
val ACI_B = ACI.from(UUID.fromString("bbbb0000-0b60-4a68-9cd9-ed2f8453f9ed"))
val ACI_SELF = ACI.from(UUID.fromString("77770000-b477-4f35-a824-d92987a63641"))
val PNI_A = PNI.from(UUID.fromString("aaaa1111-c960-4f6c-8385-671ad2ffb999"))
val PNI_B = PNI.from(UUID.fromString("bbbb1111-cd55-40bf-adda-c35a85375533"))
val PNI_SELF = PNI.from(UUID.fromString("77771111-b014-41fb-bf73-05cb2ec52910"))
const val E164_A = "+12222222222"
const val E164_B = "+13333333333"
const val E164_SELF = "+10000000000"
val STORAGE_ID_A: StorageId = StorageId.forContact(byteArrayOf(1, 2, 3, 4))
val STORAGE_ID_B: StorageId = StorageId.forContact(byteArrayOf(5, 6, 7, 8))
val STORAGE_ID_C: StorageId = StorageId.forContact(byteArrayOf(9, 10, 11, 12))
}
}

View File

@@ -0,0 +1,59 @@
package org.thoughtcrime.securesms.testing
import org.signal.core.util.logging.Log
import org.signal.libsignal.protocol.ecc.ECKeyPair
import org.signal.libsignal.zkgroup.profiles.ProfileKey
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.messages.protocol.BufferedProtocolStore
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.testing.FakeClientHelpers.toEnvelope
import org.whispersystems.signalservice.api.push.ServiceId
import org.whispersystems.signalservice.api.push.SignalServiceAddress
import org.whispersystems.signalservice.internal.push.Envelope
/**
* Welcome to Alice's Client.
*
* Alice represent the Android instrumentation test user. Unlike [BobClient] much less is needed here
* as it can make use of the standard Signal Android App infrastructure.
*/
class AliceClient(val serviceId: ServiceId, val e164: String, val trustRoot: ECKeyPair) {
companion object {
val TAG = Log.tag(AliceClient::class.java)
}
private val aliceSenderCertificate = FakeClientHelpers.createCertificateFor(
trustRoot = trustRoot,
uuid = serviceId.rawUuid,
e164 = e164,
deviceId = 1,
identityKey = SignalStore.account().aciIdentityKey.publicKey.publicKey,
expires = 31337
)
fun process(envelope: Envelope, serverDeliveredTimestamp: Long) {
val start = System.currentTimeMillis()
val bufferedStore = BufferedProtocolStore.create()
ApplicationDependencies.getIncomingMessageObserver()
.processEnvelope(bufferedStore, envelope, serverDeliveredTimestamp)
?.mapNotNull { it.run() }
?.forEach { it.enqueue() }
bufferedStore.flushToDisk()
val end = System.currentTimeMillis()
Log.d(TAG, "${end - start}")
}
fun encrypt(now: Long, destination: Recipient): Envelope {
return ApplicationDependencies.getSignalServiceMessageSender().getEncryptedMessage(
SignalServiceAddress(destination.requireServiceId(), destination.requireE164()),
FakeClientHelpers.getTargetUnidentifiedAccess(ProfileKeyUtil.getSelfProfileKey(), ProfileKey(destination.profileKey), aliceSenderCertificate),
1,
FakeClientHelpers.encryptedTextMessage(now),
false
).toEnvelope(now, destination.requireServiceId())
}
}

View File

@@ -0,0 +1,179 @@
package org.thoughtcrime.securesms.testing
import org.signal.core.util.readToSingleInt
import org.signal.core.util.select
import org.signal.libsignal.protocol.IdentityKey
import org.signal.libsignal.protocol.IdentityKeyPair
import org.signal.libsignal.protocol.SessionBuilder
import org.signal.libsignal.protocol.SignalProtocolAddress
import org.signal.libsignal.protocol.ecc.ECKeyPair
import org.signal.libsignal.protocol.groups.state.SenderKeyRecord
import org.signal.libsignal.protocol.state.IdentityKeyStore
import org.signal.libsignal.protocol.state.KyberPreKeyRecord
import org.signal.libsignal.protocol.state.PreKeyBundle
import org.signal.libsignal.protocol.state.PreKeyRecord
import org.signal.libsignal.protocol.state.SessionRecord
import org.signal.libsignal.protocol.state.SignedPreKeyRecord
import org.signal.libsignal.protocol.util.KeyHelper
import org.signal.libsignal.zkgroup.profiles.ProfileKey
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil
import org.thoughtcrime.securesms.database.OneTimePreKeyTable
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.SignedPreKeyTable
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.testing.FakeClientHelpers.toEnvelope
import org.whispersystems.signalservice.api.SignalServiceAccountDataStore
import org.whispersystems.signalservice.api.SignalSessionLock
import org.whispersystems.signalservice.api.crypto.SignalServiceCipher
import org.whispersystems.signalservice.api.crypto.SignalSessionBuilder
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess
import org.whispersystems.signalservice.api.push.DistributionId
import org.whispersystems.signalservice.api.push.ServiceId
import org.whispersystems.signalservice.api.push.SignalServiceAddress
import org.whispersystems.signalservice.internal.push.Envelope
import java.util.Optional
import java.util.UUID
import java.util.concurrent.locks.ReentrantLock
/**
* Welcome to Bob's Client.
*
* Bob is a "fake" client that can start a session with the Android instrumentation test user (Alice).
*
* Bob can create a new session using a prekey bundle created from Alice's prekeys, send a message, decrypt
* a return message from Alice, and that'll start a standard Signal session with normal keys/ratcheting.
*/
class BobClient(val serviceId: ServiceId, val e164: String, val identityKeyPair: IdentityKeyPair, val trustRoot: ECKeyPair, val profileKey: ProfileKey) {
private val serviceAddress = SignalServiceAddress(serviceId, e164)
private val registrationId = KeyHelper.generateRegistrationId(false)
private val aciStore = BobSignalServiceAccountDataStore(registrationId, identityKeyPair)
private val senderCertificate = FakeClientHelpers.createCertificateFor(trustRoot, serviceId.rawUuid, e164, 1, identityKeyPair.publicKey.publicKey, 31337)
private val sessionLock = object : SignalSessionLock {
private val lock = ReentrantLock()
override fun acquire(): SignalSessionLock.Lock {
lock.lock()
return SignalSessionLock.Lock { lock.unlock() }
}
}
/** Inspired by SignalServiceMessageSender#getEncryptedMessage */
fun encrypt(now: Long): Envelope {
val envelopeContent = FakeClientHelpers.encryptedTextMessage(now)
val cipher = SignalServiceCipher(serviceAddress, 1, aciStore, sessionLock, null)
if (!aciStore.containsSession(getAliceProtocolAddress())) {
val sessionBuilder = SignalSessionBuilder(sessionLock, SessionBuilder(aciStore, getAliceProtocolAddress()))
sessionBuilder.process(getAlicePreKeyBundle())
}
return cipher.encrypt(getAliceProtocolAddress(), getAliceUnidentifiedAccess(), envelopeContent)
.toEnvelope(envelopeContent.content.get().dataMessage!!.timestamp!!, getAliceServiceId())
}
fun decrypt(envelope: Envelope, serverDeliveredTimestamp: Long) {
val cipher = SignalServiceCipher(serviceAddress, 1, aciStore, sessionLock, UnidentifiedAccessUtil.getCertificateValidator())
cipher.decrypt(envelope, serverDeliveredTimestamp)
}
private fun getAliceServiceId(): ServiceId {
return SignalStore.account().requireAci()
}
private fun getAlicePreKeyBundle(): PreKeyBundle {
val selfPreKeyId = SignalDatabase.rawDatabase
.select(OneTimePreKeyTable.KEY_ID)
.from(OneTimePreKeyTable.TABLE_NAME)
.where("${OneTimePreKeyTable.ACCOUNT_ID} = ?", getAliceServiceId().toString())
.run()
.readToSingleInt(-1)
val selfPreKeyRecord = SignalDatabase.oneTimePreKeys.get(getAliceServiceId(), selfPreKeyId)!!
val selfSignedPreKeyId = SignalDatabase.rawDatabase
.select(SignedPreKeyTable.KEY_ID)
.from(SignedPreKeyTable.TABLE_NAME)
.where("${SignedPreKeyTable.ACCOUNT_ID} = ?", getAliceServiceId().toString())
.run()
.readToSingleInt(-1)
val selfSignedPreKeyRecord = SignalDatabase.signedPreKeys.get(getAliceServiceId(), selfSignedPreKeyId)!!
return PreKeyBundle(
SignalStore.account().registrationId,
1,
selfPreKeyId,
selfPreKeyRecord.keyPair.publicKey,
selfSignedPreKeyId,
selfSignedPreKeyRecord.keyPair.publicKey,
selfSignedPreKeyRecord.signature,
getAlicePublicKey()
)
}
private fun getAliceProtocolAddress(): SignalProtocolAddress {
return SignalProtocolAddress(SignalStore.account().requireAci().toString(), 1)
}
private fun getAlicePublicKey(): IdentityKey {
return SignalStore.account().aciIdentityKey.publicKey
}
private fun getAliceProfileKey(): ProfileKey {
return ProfileKeyUtil.getSelfProfileKey()
}
private fun getAliceUnidentifiedAccess(): Optional<UnidentifiedAccess> {
return FakeClientHelpers.getTargetUnidentifiedAccess(profileKey, getAliceProfileKey(), senderCertificate)
}
private class BobSignalServiceAccountDataStore(private val registrationId: Int, private val identityKeyPair: IdentityKeyPair) : SignalServiceAccountDataStore {
private var aliceSessionRecord: SessionRecord? = null
override fun getIdentityKeyPair(): IdentityKeyPair = identityKeyPair
override fun getLocalRegistrationId(): Int = registrationId
override fun isTrustedIdentity(address: SignalProtocolAddress?, identityKey: IdentityKey?, direction: IdentityKeyStore.Direction?): Boolean = true
override fun loadSession(address: SignalProtocolAddress?): SessionRecord = aliceSessionRecord ?: SessionRecord()
override fun saveIdentity(address: SignalProtocolAddress?, identityKey: IdentityKey?): Boolean = false
override fun storeSession(address: SignalProtocolAddress?, record: SessionRecord?) { aliceSessionRecord = record }
override fun getSubDeviceSessions(name: String?): List<Int> = emptyList()
override fun containsSession(address: SignalProtocolAddress?): Boolean = aliceSessionRecord != null
override fun getIdentity(address: SignalProtocolAddress?): IdentityKey = SignalStore.account().aciIdentityKey.publicKey
override fun loadPreKey(preKeyId: Int): PreKeyRecord = throw UnsupportedOperationException()
override fun storePreKey(preKeyId: Int, record: PreKeyRecord?) = throw UnsupportedOperationException()
override fun containsPreKey(preKeyId: Int): Boolean = throw UnsupportedOperationException()
override fun removePreKey(preKeyId: Int) = throw UnsupportedOperationException()
override fun loadExistingSessions(addresses: MutableList<SignalProtocolAddress>?): MutableList<SessionRecord> = throw UnsupportedOperationException()
override fun deleteSession(address: SignalProtocolAddress?) = throw UnsupportedOperationException()
override fun deleteAllSessions(name: String?) = throw UnsupportedOperationException()
override fun loadSignedPreKey(signedPreKeyId: Int): SignedPreKeyRecord = throw UnsupportedOperationException()
override fun loadSignedPreKeys(): MutableList<SignedPreKeyRecord> = throw UnsupportedOperationException()
override fun storeSignedPreKey(signedPreKeyId: Int, record: SignedPreKeyRecord?) = throw UnsupportedOperationException()
override fun containsSignedPreKey(signedPreKeyId: Int): Boolean = throw UnsupportedOperationException()
override fun removeSignedPreKey(signedPreKeyId: Int) = throw UnsupportedOperationException()
override fun loadKyberPreKey(kyberPreKeyId: Int): KyberPreKeyRecord = throw UnsupportedOperationException()
override fun loadKyberPreKeys(): MutableList<KyberPreKeyRecord> = throw UnsupportedOperationException()
override fun storeKyberPreKey(kyberPreKeyId: Int, record: KyberPreKeyRecord?) = throw UnsupportedOperationException()
override fun containsKyberPreKey(kyberPreKeyId: Int): Boolean = throw UnsupportedOperationException()
override fun markKyberPreKeyUsed(kyberPreKeyId: Int) = throw UnsupportedOperationException()
override fun deleteAllStaleOneTimeEcPreKeys(threshold: Long, minCount: Int) = throw UnsupportedOperationException()
override fun markAllOneTimeEcPreKeysStaleIfNecessary(staleTime: Long) = throw UnsupportedOperationException()
override fun storeSenderKey(sender: SignalProtocolAddress?, distributionId: UUID?, record: SenderKeyRecord?) = throw UnsupportedOperationException()
override fun loadSenderKey(sender: SignalProtocolAddress?, distributionId: UUID?): SenderKeyRecord = throw UnsupportedOperationException()
override fun archiveSession(address: SignalProtocolAddress?) = throw UnsupportedOperationException()
override fun getAllAddressesWithActiveSessions(addressNames: MutableList<String>?): MutableMap<SignalProtocolAddress, SessionRecord> = throw UnsupportedOperationException()
override fun getSenderKeySharedWith(distributionId: DistributionId?): MutableSet<SignalProtocolAddress> = throw UnsupportedOperationException()
override fun markSenderKeySharedWith(distributionId: DistributionId?, addresses: MutableCollection<SignalProtocolAddress>?) = throw UnsupportedOperationException()
override fun clearSenderKeySharedWith(addresses: MutableCollection<SignalProtocolAddress>?) = throw UnsupportedOperationException()
override fun storeLastResortKyberPreKey(kyberPreKeyId: Int, kyberPreKeyRecord: KyberPreKeyRecord) = throw UnsupportedOperationException()
override fun removeKyberPreKey(kyberPreKeyId: Int) = throw UnsupportedOperationException()
override fun markAllOneTimeKyberPreKeysStaleIfNecessary(staleTime: Long) = throw UnsupportedOperationException()
override fun deleteAllStaleOneTimeKyberPreKeys(threshold: Long, minCount: Int) = throw UnsupportedOperationException()
override fun loadLastResortKyberPreKeys(): List<KyberPreKeyRecord> = throw UnsupportedOperationException()
override fun isMultiDevice(): Boolean = throw UnsupportedOperationException()
}
}

View File

@@ -0,0 +1,81 @@
package org.thoughtcrime.securesms.testing
import okio.ByteString.Companion.toByteString
import org.signal.core.util.Base64
import org.signal.libsignal.internal.Native
import org.signal.libsignal.internal.NativeHandleGuard
import org.signal.libsignal.metadata.certificate.CertificateValidator
import org.signal.libsignal.metadata.certificate.SenderCertificate
import org.signal.libsignal.metadata.certificate.ServerCertificate
import org.signal.libsignal.protocol.ecc.Curve
import org.signal.libsignal.protocol.ecc.ECKeyPair
import org.signal.libsignal.protocol.ecc.ECPublicKey
import org.signal.libsignal.zkgroup.profiles.ProfileKey
import org.thoughtcrime.securesms.messages.SignalServiceProtoUtil.buildWith
import org.whispersystems.signalservice.api.crypto.ContentHint
import org.whispersystems.signalservice.api.crypto.EnvelopeContent
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair
import org.whispersystems.signalservice.api.push.ServiceId
import org.whispersystems.signalservice.internal.push.Content
import org.whispersystems.signalservice.internal.push.DataMessage
import org.whispersystems.signalservice.internal.push.Envelope
import org.whispersystems.signalservice.internal.push.OutgoingPushMessage
import java.util.Optional
import java.util.UUID
object FakeClientHelpers {
val noOpCertificateValidator = object : CertificateValidator(null) {
override fun validate(certificate: SenderCertificate, validationTime: Long) = Unit
}
fun createCertificateFor(trustRoot: ECKeyPair, uuid: UUID, e164: String, deviceId: Int, identityKey: ECPublicKey, expires: Long): SenderCertificate {
val serverKey: ECKeyPair = Curve.generateKeyPair()
NativeHandleGuard(serverKey.publicKey).use { serverPublicGuard ->
NativeHandleGuard(trustRoot.privateKey).use { trustRootPrivateGuard ->
val serverCertificate = ServerCertificate(Native.ServerCertificate_New(1, serverPublicGuard.nativeHandle(), trustRootPrivateGuard.nativeHandle()))
NativeHandleGuard(identityKey).use { identityGuard ->
NativeHandleGuard(serverCertificate).use { serverCertificateGuard ->
NativeHandleGuard(serverKey.privateKey).use { serverPrivateGuard ->
return SenderCertificate(Native.SenderCertificate_New(uuid.toString(), e164, deviceId, identityGuard.nativeHandle(), expires, serverCertificateGuard.nativeHandle(), serverPrivateGuard.nativeHandle()))
}
}
}
}
}
}
fun getTargetUnidentifiedAccess(myProfileKey: ProfileKey, theirProfileKey: ProfileKey, senderCertificate: SenderCertificate): Optional<UnidentifiedAccess> {
val selfUnidentifiedAccessKey = UnidentifiedAccess.deriveAccessKeyFrom(myProfileKey)
val themUnidentifiedAccessKey = UnidentifiedAccess.deriveAccessKeyFrom(theirProfileKey)
return UnidentifiedAccessPair(UnidentifiedAccess(selfUnidentifiedAccessKey, senderCertificate.serialized, false), UnidentifiedAccess(themUnidentifiedAccessKey, senderCertificate.serialized, false)).targetUnidentifiedAccess
}
fun encryptedTextMessage(now: Long, message: String = "Test body message"): EnvelopeContent {
val content = Content.Builder().apply {
dataMessage(
DataMessage.Builder().buildWith {
body = message
timestamp = now
}
)
}
return EnvelopeContent.encrypted(content.build(), ContentHint.RESENDABLE, Optional.empty())
}
fun OutgoingPushMessage.toEnvelope(timestamp: Long, destination: ServiceId): Envelope {
return Envelope.Builder()
.type(Envelope.Type.fromValue(this.type))
.sourceDevice(1)
.timestamp(timestamp)
.serverTimestamp(timestamp + 1)
.destinationServiceId(destination.toString())
.serverGuid(UUID.randomUUID().toString())
.content(Base64.decode(this.content).toByteString())
.urgent(true)
.story(false)
.build()
}
}

View File

@@ -0,0 +1,55 @@
package org.thoughtcrime.securesms.testing
import okio.ByteString.Companion.toByteString
import org.signal.libsignal.zkgroup.groups.GroupMasterKey
import org.signal.storageservice.protos.groups.Member
import org.signal.storageservice.protos.groups.local.DecryptedGroup
import org.signal.storageservice.protos.groups.local.DecryptedMember
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.groups.GroupId
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
import org.whispersystems.signalservice.api.push.ServiceId.ACI
import org.whispersystems.signalservice.internal.push.GroupContextV2
import kotlin.random.Random
/**
* Helper methods for creating groups for message processing tests et al.
*/
object GroupTestingUtils {
fun member(aci: ACI, revision: Int = 0, role: Member.Role = Member.Role.ADMINISTRATOR): DecryptedMember {
return DecryptedMember.Builder()
.aciBytes(aci.toByteString())
.joinedAtRevision(revision)
.role(role)
.build()
}
fun insertGroup(revision: Int = 0, vararg members: DecryptedMember): TestGroupInfo {
val groupMasterKey = GroupMasterKey(Random.nextBytes(GroupMasterKey.SIZE))
val decryptedGroupState = DecryptedGroup.Builder()
.members(members.toList())
.revision(revision)
.title(MessageContentFuzzer.string())
.build()
val groupId = SignalDatabase.groups.create(groupMasterKey, decryptedGroupState)!!
val groupRecipientId = SignalDatabase.recipients.getOrInsertFromGroupId(groupId)
SignalDatabase.recipients.setProfileSharing(groupRecipientId, true)
return TestGroupInfo(groupId, groupMasterKey, groupRecipientId)
}
fun RecipientId.asMember(): DecryptedMember {
return Recipient.resolved(this).asMember()
}
fun Recipient.asMember(): DecryptedMember {
return member(aci = requireAci())
}
data class TestGroupInfo(val groupId: GroupId.V2, val masterKey: GroupMasterKey, val recipientId: RecipientId) {
val groupV2Context: GroupContextV2
get() = GroupContextV2(masterKey = masterKey.serialize().toByteString(), revision = 0)
}
}

View File

@@ -0,0 +1,97 @@
package org.thoughtcrime.securesms.testing
import org.signal.core.util.ThreadUtil
import org.signal.core.util.concurrent.SignalExecutors
import org.signal.core.util.logging.Log
import java.util.concurrent.CountDownLatch
typealias LogPredicate = (Entry) -> Boolean
/**
* Logging implementation that holds logs in memory as they are added to be retrieve at a later time by a test.
* Can also be used for multithreaded synchronization and waiting until certain logs are emitted before continuing
* a test.
*/
class InMemoryLogger : Log.Logger() {
private val executor = SignalExecutors.newCachedSingleThreadExecutor("inmemory-logger", ThreadUtil.PRIORITY_BACKGROUND_THREAD)
private val predicates = mutableListOf<LogPredicate>()
private val logEntries = mutableListOf<Entry>()
override fun v(tag: String, message: String?, t: Throwable?, keepLonger: Boolean) = add(Verbose(tag, message, t, System.currentTimeMillis()))
override fun d(tag: String, message: String?, t: Throwable?, keepLonger: Boolean) = add(Debug(tag, message, t, System.currentTimeMillis()))
override fun i(tag: String, message: String?, t: Throwable?, keepLonger: Boolean) = add(Info(tag, message, t, System.currentTimeMillis()))
override fun w(tag: String, message: String?, t: Throwable?, keepLonger: Boolean) = add(Warn(tag, message, t, System.currentTimeMillis()))
override fun e(tag: String, message: String?, t: Throwable?, keepLonger: Boolean) = add(Error(tag, message, t, System.currentTimeMillis()))
override fun flush() {
val latch = CountDownLatch(1)
executor.execute { latch.countDown() }
latch.await()
}
fun clear() {
val latch = CountDownLatch(1)
executor.execute {
predicates.clear()
logEntries.clear()
latch.countDown()
}
latch.await()
}
private fun add(entry: Entry) {
executor.execute {
logEntries += entry
val iterator = predicates.iterator()
while (iterator.hasNext()) {
val predicate = iterator.next()
if (predicate(entry)) {
iterator.remove()
}
}
}
}
/** Blocks until a snapshot of all log entries can be taken in a thread-safe way. */
fun entries(): List<Entry> {
val latch = CountDownLatch(1)
var entries: List<Entry> = emptyList()
executor.execute {
entries = logEntries.toList()
latch.countDown()
}
latch.await()
return entries
}
/** Returns a countdown latch that'll fire at a future point when an [Entry] is received that matches the predicate. */
fun getLockForUntil(predicate: LogPredicate): CountDownLatch {
val latch = CountDownLatch(1)
executor.execute {
predicates += { entry ->
if (predicate(entry)) {
latch.countDown()
true
} else {
false
}
}
}
return latch
}
}
sealed interface Entry {
val tag: String
val message: String?
val throwable: Throwable?
val timestamp: Long
}
data class Verbose(override val tag: String, override val message: String?, override val throwable: Throwable?, override val timestamp: Long) : Entry
data class Debug(override val tag: String, override val message: String?, override val throwable: Throwable?, override val timestamp: Long) : Entry
data class Info(override val tag: String, override val message: String?, override val throwable: Throwable?, override val timestamp: Long) : Entry
data class Warn(override val tag: String, override val message: String?, override val throwable: Throwable?, override val timestamp: Long) : Entry
data class Error(override val tag: String, override val message: String?, override val throwable: Throwable?, override val timestamp: Long) : Entry

View File

@@ -0,0 +1,293 @@
package org.thoughtcrime.securesms.testing
import okio.ByteString
import okio.ByteString.Companion.toByteString
import org.thoughtcrime.securesms.groups.GroupId
import org.thoughtcrime.securesms.messages.SignalServiceProtoUtil.buildWith
import org.thoughtcrime.securesms.messages.TestMessage
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
import org.whispersystems.signalservice.api.crypto.EnvelopeMetadata
import org.whispersystems.signalservice.internal.push.AttachmentPointer
import org.whispersystems.signalservice.internal.push.BodyRange
import org.whispersystems.signalservice.internal.push.Content
import org.whispersystems.signalservice.internal.push.DataMessage
import org.whispersystems.signalservice.internal.push.EditMessage
import org.whispersystems.signalservice.internal.push.Envelope
import org.whispersystems.signalservice.internal.push.GroupContextV2
import org.whispersystems.signalservice.internal.push.SyncMessage
import java.util.UUID
import kotlin.random.Random
import kotlin.random.nextInt
import kotlin.time.Duration.Companion.days
/**
* Random but deterministic fuzzer for create various message content protos.
*/
object MessageContentFuzzer {
private val mediaTypes = listOf("image/png", "image/jpeg", "image/heic", "image/heif", "image/avif", "image/webp", "image/gif", "audio/aac", "audio/*", "video/mp4", "video/*", "text/x-vcard", "text/x-signal-plain", "application/x-signal-view-once", "*/*", "application/octet-stream")
private val emojis = listOf("😂", "❤️", "🔥", "😍", "👀", "🤔", "🙏", "👍", "🤷", "🥺")
private val random = Random(1)
/**
* Create an [Envelope].
*/
fun envelope(timestamp: Long, serverGuid: UUID = UUID.randomUUID()): Envelope {
return Envelope.Builder()
.timestamp(timestamp)
.serverTimestamp(timestamp + 5)
.serverGuid(serverGuid.toString())
.build()
}
/**
* Create metadata to match an [Envelope].
*/
fun envelopeMetadata(source: RecipientId, destination: RecipientId, sourceDeviceId: Int = 1, groupId: GroupId.V2? = null): EnvelopeMetadata {
return EnvelopeMetadata(
sourceServiceId = Recipient.resolved(source).requireServiceId(),
sourceE164 = null,
sourceDeviceId = sourceDeviceId,
sealedSender = true,
groupId = groupId?.decodedId,
destinationServiceId = Recipient.resolved(destination).requireServiceId()
)
}
/**
* Create a random text message that will contain a body but may also contain
* - An expire timer value
* - Bold style body ranges
*/
fun fuzzTextMessage(sentTimestamp: Long? = null, groupContextV2: GroupContextV2? = null): Content {
return Content.Builder()
.dataMessage(
DataMessage.Builder().buildWith {
timestamp = sentTimestamp
body = string()
if (random.nextBoolean()) {
expireTimer = random.nextInt(0..28.days.inWholeSeconds.toInt())
}
if (random.nextBoolean()) {
bodyRanges(
listOf(
BodyRange.Builder().buildWith {
start = 0
length = 1
style = BodyRange.Style.BOLD
}
)
)
}
if (groupContextV2 != null) {
groupV2 = groupContextV2
}
}
)
.build()
}
/**
* Create an edit message.
*/
fun editTextMessage(targetTimestamp: Long, editedDataMessage: DataMessage): Content {
return Content.Builder()
.editMessage(
EditMessage.Builder().buildWith {
targetSentTimestamp = targetTimestamp
dataMessage = editedDataMessage
}
)
.build()
}
/**
* Create a sync sent text message for the given [DataMessage].
*/
fun syncSentTextMessage(
textMessage: DataMessage,
deliveredTo: List<RecipientId>,
recipientUpdate: Boolean = false
): Content {
return Content
.Builder()
.syncMessage(
SyncMessage.Builder().buildWith {
sent = SyncMessage.Sent.Builder().buildWith {
timestamp = textMessage.timestamp
message = textMessage
isRecipientUpdate = recipientUpdate
unidentifiedStatus(
deliveredTo.map {
SyncMessage.Sent.UnidentifiedDeliveryStatus.Builder().buildWith {
destinationServiceId = Recipient.resolved(it).requireServiceId().toString()
unidentified = true
}
}
)
}
}
).build()
}
/**
* Create a sync reads message for the given [RecipientId] and message timestamp pairings.
*/
fun syncReadsMessage(timestamps: List<Pair<RecipientId, Long>>): Content {
return Content
.Builder()
.syncMessage(
SyncMessage.Builder().buildWith {
read = timestamps.map { (senderId, timestamp) ->
SyncMessage.Read.Builder().buildWith {
this.senderAci = Recipient.resolved(senderId).requireAci().toString()
this.timestamp = timestamp
}
}
}
).build()
}
/**
* Create a random media message that may be:
* - A text body
* - A text body with a quote that references an existing message
* - A text body with a quote that references a non existing message
* - A message with 0-2 attachment pointers and may contain a text body
*/
fun fuzzMediaMessageWithBody(quoteAble: List<TestMessage> = emptyList()): Content {
return Content.Builder()
.dataMessage(
DataMessage.Builder().buildWith {
if (random.nextBoolean()) {
body = string()
}
if (random.nextBoolean() && quoteAble.isNotEmpty()) {
body = string()
val quoted = quoteAble.random(random)
quote = DataMessage.Quote.Builder().buildWith {
id = quoted.envelope.timestamp
authorAci = quoted.metadata.sourceServiceId.toString()
text = quoted.content.dataMessage?.body
attachments(quoted.content.dataMessage?.attachments ?: emptyList())
bodyRanges(quoted.content.dataMessage?.bodyRanges ?: emptyList())
type = DataMessage.Quote.Type.NORMAL
}
}
if (random.nextFloat() < 0.1 && quoteAble.isNotEmpty()) {
val quoted = quoteAble.random(random)
quote = DataMessage.Quote.Builder().buildWith {
id = random.nextLong(quoted.envelope.timestamp!! - 1000000, quoted.envelope.timestamp!!)
authorAci = quoted.metadata.sourceServiceId.toString()
text = quoted.content.dataMessage?.body
}
}
if (random.nextFloat() < 0.25) {
val total = random.nextInt(1, 2)
attachments((0..total).map { attachmentPointer() })
}
}
)
.build()
}
/**
* Creates a random media message that contains no traditional media content. It may be:
* - A reaction to a prior message
*/
fun fuzzMediaMessageNoContent(previousMessages: List<TestMessage> = emptyList()): Content {
return Content.Builder()
.dataMessage(
DataMessage.Builder().buildWith {
if (random.nextFloat() < 0.25) {
val reactTo = previousMessages.random(random)
reaction = DataMessage.Reaction.Builder().buildWith {
emoji = emojis.random(random)
remove = false
targetAuthorAci = reactTo.metadata.sourceServiceId.toString()
targetSentTimestamp = reactTo.envelope.timestamp
}
}
}
).build()
}
/**
* Create a random media message that contains a sticker.
*/
fun fuzzStickerMediaMessage(sentTimestamp: Long? = null, groupContextV2: GroupContextV2? = null): Content {
return Content.Builder()
.dataMessage(
DataMessage.Builder().buildWith {
timestamp = sentTimestamp
sticker = DataMessage.Sticker.Builder().buildWith {
packId = byteString(length = 24)
packKey = byteString(length = 128)
stickerId = random.nextInt()
data_ = attachmentPointer()
emoji = emojis.random(random)
}
groupV2 = groupContextV2
}
).build()
}
/**
* Generate a random [String].
*/
fun string(length: Int = 10, allowNullString: Boolean = false): String {
var string = ""
if (allowNullString && random.nextBoolean()) {
return string
}
for (i in 0 until length) {
string += random.nextInt(65..90).toChar()
}
return string
}
/**
* Generate a random [ByteString].
*/
fun byteString(length: Int = 512): ByteString {
return random.nextBytes(length).toByteString()
}
/**
* Generate a random [AttachmentPointer].
*/
fun attachmentPointer(): AttachmentPointer {
return AttachmentPointer.Builder().run {
cdnKey = string()
contentType = mediaTypes.random(random)
key = byteString()
size = random.nextInt(1024 * 1024 * 50)
thumbnail = byteString()
digest = byteString()
fileName = string()
flags = 0
width = random.nextInt(until = 1024)
height = random.nextInt(until = 1024)
caption = string(allowNullString = true)
blurHash = string()
uploadTimestamp = random.nextLong()
cdnNumber = 1
build()
}
}
/**
* Creates a server delivered timestamp that is always later than the envelope and server "received" timestamp.
*/
fun fuzzServerDeliveredTimestamp(envelopeTimestamp: Long): Long {
return envelopeTimestamp + 10
}
}

View File

@@ -1,37 +1,22 @@
package org.thoughtcrime.securesms.testing
import io.reactivex.rxjava3.core.Single
import org.mockito.kotlin.any
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.stub
import org.signal.core.util.Hex
import org.signal.libsignal.protocol.IdentityKeyPair
import org.signal.libsignal.protocol.ecc.Curve
import org.signal.libsignal.protocol.state.PreKeyRecord
import org.signal.libsignal.protocol.util.KeyHelper
import org.signal.libsignal.protocol.util.Medium
import org.thoughtcrime.securesms.crypto.PreKeyUtil
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.pin.KbsRepository
import org.thoughtcrime.securesms.pin.TokenData
import org.thoughtcrime.securesms.test.BuildConfig
import org.whispersystems.signalservice.api.KbsPinData
import org.whispersystems.signalservice.api.KeyBackupService
import org.whispersystems.signalservice.api.kbs.HashedPin
import org.whispersystems.signalservice.api.kbs.MasterKey
import org.whispersystems.signalservice.api.messages.multidevice.DeviceInfo
import org.whispersystems.signalservice.api.push.ServiceId
import org.whispersystems.signalservice.api.push.SignedPreKeyEntity
import org.whispersystems.signalservice.internal.ServiceResponse
import org.whispersystems.signalservice.internal.contacts.entities.TokenResponse
import org.whispersystems.signalservice.internal.push.AuthCredentials
import org.whispersystems.signalservice.internal.push.DeviceInfoList
import org.whispersystems.signalservice.internal.push.PreKeyEntity
import org.whispersystems.signalservice.internal.push.PreKeyResponse
import org.whispersystems.signalservice.internal.push.PreKeyResponseItem
import org.whispersystems.signalservice.internal.push.PushServiceSocket
import org.whispersystems.signalservice.internal.push.RegistrationSessionMetadataJson
import org.whispersystems.signalservice.internal.push.SenderCertificate
import org.whispersystems.signalservice.internal.push.VerifyAccountResponse
import org.whispersystems.signalservice.internal.push.WhoAmIResponse
@@ -45,7 +30,8 @@ object MockProvider {
val senderCertificate = SenderCertificate().apply { certificate = ByteArray(0) }
val lockedFailure = PushServiceSocket.RegistrationLockFailure().apply {
backupCredentials = AuthCredentials.create("username", "password")
svr1Credentials = AuthCredentials.create("username", "password")
svr2Credentials = null
}
val primaryOnlyDeviceList = DeviceInfoList().apply {
@@ -56,6 +42,16 @@ object MockProvider {
)
}
val sessionMetadataJson = RegistrationSessionMetadataJson(
id = "asdfasdfasdfasdf",
nextCall = null,
nextSms = null,
nextVerificationAttempt = null,
allowedToRequestCode = true,
requestedInformation = emptyList(),
verified = true
)
fun createVerifyAccountResponse(aci: ServiceId, newPni: ServiceId): VerifyAccountResponse {
return VerifyAccountResponse().apply {
uuid = aci.toString()
@@ -72,29 +68,6 @@ object MockProvider {
}
}
fun mockGetRegistrationLockStringFlow(kbsRepository: KbsRepository) {
val tokenData: TokenData = mock {
on { enclave } doReturn BuildConfig.KBS_ENCLAVE
on { basicAuth } doReturn "basicAuth"
on { triesRemaining } doReturn 10
on { tokenResponse } doReturn TokenResponse()
}
kbsRepository.stub {
on { getToken(any() as? String) } doReturn Single.just(ServiceResponse.forResult(tokenData, 200, ""))
}
val session: KeyBackupService.RestoreSession = object : KeyBackupService.RestoreSession {
override fun hashSalt(): ByteArray = Hex.fromStringCondensed("cba811749042b303a6a7efa5ccd160aea5e3ea243c8d2692bd13d515732f51a8")
override fun restorePin(hashedPin: HashedPin?): KbsPinData = KbsPinData(MasterKey.createNew(SecureRandom()), null)
}
val kbsService = ApplicationDependencies.getKeyBackupService(BuildConfig.KBS_ENCLAVE)
kbsService.stub {
on { newRegistrationSession(any(), any()) } doReturn session
}
}
fun createPreKeyResponse(identity: IdentityKeyPair = SignalStore.account().aciIdentityKey, deviceId: Int): PreKeyResponse {
val signedPreKeyRecord = PreKeyUtil.generateSignedPreKey(SecureRandom().nextInt(Medium.MAX_VALUE), identity.privateKey)
val oneTimePreKey = PreKeyRecord(SecureRandom().nextInt(Medium.MAX_VALUE), Curve.generateKeyPair())

View File

@@ -7,15 +7,22 @@ import org.thoughtcrime.securesms.util.JsonUtils
import java.util.concurrent.TimeUnit
typealias ResponseFactory = (request: RecordedRequest) -> MockResponse
typealias RequestPredicate = (request: RecordedRequest) -> Boolean
/**
* Represent an HTTP verb for mocking web requests.
*/
sealed class Verb(val verb: String, val path: String, val responseFactory: ResponseFactory)
sealed class Verb(val requestPredicate: RequestPredicate, val responseFactory: ResponseFactory)
class Get(path: String, responseFactory: ResponseFactory) : Verb("GET", path, responseFactory)
class Get(path: String, predicate: RequestPredicate, responseFactory: ResponseFactory) : Verb(defaultRequestPredicate("GET", path, predicate), responseFactory) {
constructor(path: String, responseFactory: ResponseFactory) : this(path, { true }, responseFactory)
}
class Put(path: String, responseFactory: ResponseFactory) : Verb("PUT", path, responseFactory)
class Put(path: String, responseFactory: ResponseFactory) : Verb(defaultRequestPredicate("PUT", path), responseFactory)
class Post(path: String, responseFactory: ResponseFactory) : Verb(defaultRequestPredicate("POST", path), responseFactory)
class Delete(path: String, responseFactory: ResponseFactory) : Verb(defaultRequestPredicate("DELETE", path), responseFactory)
fun MockResponse.success(response: Any? = null): MockResponse {
return setResponseCode(200).apply {
@@ -46,3 +53,7 @@ inline fun <reified T> RecordedRequest.parsedRequestBody(): T {
val bodyString = String(body.readByteArray())
return JsonUtils.fromJson(bodyString, T::class.java)
}
private fun defaultRequestPredicate(verb: String, path: String, predicate: RequestPredicate = { true }): RequestPredicate = { request ->
request.method == verb && request.path.startsWith("/$path") && predicate(request)
}

View File

@@ -13,7 +13,7 @@ class RxTestSchedulerRule(
val ioTestScheduler: TestScheduler = defaultTestScheduler,
val computationTestScheduler: TestScheduler = defaultTestScheduler,
val singleTestScheduler: TestScheduler = defaultTestScheduler,
val newThreadTestScheduler: TestScheduler = defaultTestScheduler,
val newThreadTestScheduler: TestScheduler = defaultTestScheduler
) : ExternalResource() {
override fun before() {

View File

@@ -11,7 +11,9 @@ import androidx.test.platform.app.InstrumentationRegistry
import okhttp3.mockwebserver.MockResponse
import org.junit.rules.ExternalResource
import org.signal.libsignal.protocol.IdentityKey
import org.signal.libsignal.protocol.IdentityKeyPair
import org.signal.libsignal.protocol.SignalProtocolAddress
import org.thoughtcrime.securesms.SignalInstrumentationApplicationContext
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
import org.thoughtcrime.securesms.crypto.MasterSecretUtil
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil
@@ -20,21 +22,21 @@ import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.dependencies.InstrumentationApplicationDependencyProvider
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.net.DeviceTransferBlockingInterceptor
import org.thoughtcrime.securesms.profiles.ProfileName
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.registration.RegistrationData
import org.thoughtcrime.securesms.registration.RegistrationRepository
import org.thoughtcrime.securesms.registration.RegistrationUtil
import org.thoughtcrime.securesms.registration.VerifyResponse
import org.thoughtcrime.securesms.testing.GroupTestingUtils.asMember
import org.thoughtcrime.securesms.util.Util
import org.whispersystems.signalservice.api.profiles.SignalServiceProfile
import org.whispersystems.signalservice.api.push.ACI
import org.whispersystems.signalservice.api.push.ServiceId.ACI
import org.whispersystems.signalservice.api.push.SignalServiceAddress
import org.whispersystems.signalservice.internal.ServiceResponse
import org.whispersystems.signalservice.internal.ServiceResponseProcessor
import org.whispersystems.signalservice.internal.push.VerifyAccountResponse
import java.lang.IllegalArgumentException
import java.util.UUID
/**
@@ -43,7 +45,7 @@ import java.util.UUID
*
* To use: `@get:Rule val harness = SignalActivityRule()`
*/
class SignalActivityRule(private val othersCount: Int = 4) : ExternalResource() {
class SignalActivityRule(private val othersCount: Int = 4, private val createGroup: Boolean = false) : ExternalResource() {
val application: Application = ApplicationDependencies.getApplication()
@@ -53,28 +55,48 @@ class SignalActivityRule(private val othersCount: Int = 4) : ExternalResource()
private set
lateinit var others: List<RecipientId>
private set
lateinit var othersKeys: List<IdentityKeyPair>
var group: GroupTestingUtils.TestGroupInfo? = null
private set
val inMemoryLogger: InMemoryLogger
get() = (application as SignalInstrumentationApplicationContext).inMemoryLogger
override fun before() {
context = InstrumentationRegistry.getInstrumentation().targetContext
self = setupSelf()
others = setupOthers()
val setupOthers = setupOthers()
others = setupOthers.first
othersKeys = setupOthers.second
if (createGroup && others.size >= 2) {
group = GroupTestingUtils.insertGroup(
revision = 0,
self.asMember(),
others[0].asMember(),
others[1].asMember()
)
}
InstrumentationApplicationDependencyProvider.clearHandlers()
}
private fun setupSelf(): Recipient {
DeviceTransferBlockingInterceptor.getInstance().blockNetwork()
PreferenceManager.getDefaultSharedPreferences(application).edit().putBoolean("pref_prompted_push_registration", true).commit()
val masterSecret = MasterSecretUtil.generateMasterSecret(application, MasterSecretUtil.UNENCRYPTED_PASSPHRASE)
MasterSecretUtil.generateAsymmetricMasterSecret(application, masterSecret)
val preferences: SharedPreferences = application.getSharedPreferences(MasterSecretUtil.PREFERENCES_NAME, 0)
preferences.edit().putBoolean("passphrase_initialized", true).commit()
SignalStore.account().generateAciIdentityKeyIfNecessary()
SignalStore.account().generatePniIdentityKeyIfNecessary()
val registrationRepository = RegistrationRepository(application)
InstrumentationApplicationDependencyProvider.addMockWebRequestHandlers(Put("/v2/keys") { MockResponse().success() })
val response: ServiceResponse<VerifyAccountResponse> = registrationRepository.registerAccountWithoutRegistrationLock(
val response: ServiceResponse<VerifyResponse> = registrationRepository.registerAccount(
RegistrationData(
code = "123123",
e164 = "+15555550101",
@@ -82,22 +104,33 @@ class SignalActivityRule(private val othersCount: Int = 4) : ExternalResource()
registrationId = registrationRepository.registrationId,
profileKey = registrationRepository.getProfileKey("+15555550101"),
fcmToken = null,
pniRegistrationId = registrationRepository.pniRegistrationId
pniRegistrationId = registrationRepository.pniRegistrationId,
recoveryPassword = "asdfasdfasdfasdf"
),
VerifyAccountResponse(UUID.randomUUID().toString(), UUID.randomUUID().toString(), false)
VerifyResponse(
verifyAccountResponse = VerifyAccountResponse(UUID.randomUUID().toString(), UUID.randomUUID().toString(), false),
masterKey = null,
pin = null,
aciPreKeyCollection = RegistrationRepository.generateSignedAndLastResortPreKeys(SignalStore.account().aciIdentityKey, SignalStore.account().aciPreKeys),
pniPreKeyCollection = RegistrationRepository.generateSignedAndLastResortPreKeys(SignalStore.account().aciIdentityKey, SignalStore.account().pniPreKeys)
),
false
).blockingGet()
ServiceResponseProcessor.DefaultProcessor(response).resultOrThrow
SignalStore.kbsValues().optOut()
RegistrationUtil.maybeMarkRegistrationComplete(application)
SignalStore.svr().optOut()
RegistrationUtil.maybeMarkRegistrationComplete()
SignalDatabase.recipients.setProfileName(Recipient.self().id, ProfileName.fromParts("Tester", "McTesterson"))
SignalStore.settings().isMessageNotificationsEnabled = false
return Recipient.self()
}
private fun setupOthers(): List<RecipientId> {
private fun setupOthers(): Pair<List<RecipientId>, List<IdentityKeyPair>> {
val others = mutableListOf<RecipientId>()
val othersKeys = mutableListOf<IdentityKeyPair>()
if (othersCount !in 0 until 1000) {
throw IllegalArgumentException("$othersCount must be between 0 and 1000")
@@ -108,14 +141,16 @@ class SignalActivityRule(private val othersCount: Int = 4) : ExternalResource()
val recipientId = RecipientId.from(SignalServiceAddress(aci, "+15555551%03d".format(i)))
SignalDatabase.recipients.setProfileName(recipientId, ProfileName.fromParts("Buddy", "#$i"))
SignalDatabase.recipients.setProfileKeyIfAbsent(recipientId, ProfileKeyUtil.createNew())
SignalDatabase.recipients.setCapabilities(recipientId, SignalServiceProfile.Capabilities(true, true, true, true, true, true, true, true))
SignalDatabase.recipients.setCapabilities(recipientId, SignalServiceProfile.Capabilities(true, true, true))
SignalDatabase.recipients.setProfileSharing(recipientId, true)
SignalDatabase.recipients.markRegistered(recipientId, aci)
ApplicationDependencies.getProtocolStore().aci().saveIdentity(SignalProtocolAddress(aci.toString(), 0), IdentityKeyUtil.generateIdentityKeyPair().publicKey)
val otherIdentity = IdentityKeyUtil.generateIdentityKeyPair()
ApplicationDependencies.getProtocolStore().aci().saveIdentity(SignalProtocolAddress(aci.toString(), 0), otherIdentity.publicKey)
others += recipientId
othersKeys += otherIdentity
}
return others
return others to othersKeys
}
inline fun <reified T : Activity> launchActivity(initIntent: Intent.() -> Unit = {}): ActivityScenario<T> {

View File

@@ -2,10 +2,12 @@ package org.thoughtcrime.securesms.testing
import org.junit.rules.TestWatcher
import org.junit.runner.Description
import org.signal.core.util.deleteAll
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.ThreadTable
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.whispersystems.signalservice.api.push.ACI
import org.whispersystems.signalservice.api.push.PNI
import org.whispersystems.signalservice.api.push.ServiceId.ACI
import org.whispersystems.signalservice.api.push.ServiceId.PNI
import java.util.UUID
/**
@@ -34,7 +36,8 @@ class SignalDatabaseRule(
private fun deleteAllThreads() {
if (deleteAllThreadsOnEachRun) {
SignalDatabase.mms.deleteAllThreads()
SignalDatabase.threads.deleteAllConversations()
SignalDatabase.rawDatabase.deleteAll(ThreadTable.TABLE_NAME)
}
}
}

View File

@@ -1,70 +0,0 @@
package org.thoughtcrime.securesms.testing
import com.google.protobuf.ByteString
import org.signal.libsignal.zkgroup.groups.GroupMasterKey
import org.whispersystems.signalservice.api.push.ServiceId
import org.whispersystems.signalservice.internal.push.SignalServiceProtos
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContextV2
import org.whispersystems.signalservice.internal.serialize.protos.AddressProto
import org.whispersystems.signalservice.internal.serialize.protos.MetadataProto
import org.whispersystems.signalservice.internal.serialize.protos.SignalServiceContentProto
import java.util.UUID
import kotlin.random.Random
class TestProtos private constructor() {
fun address(
uuid: UUID = UUID.randomUUID()
): AddressProto.Builder {
return AddressProto.newBuilder()
.setUuid(ServiceId.from(uuid).toByteString())
}
fun metadata(
address: AddressProto = address().build(),
): MetadataProto.Builder {
return MetadataProto.newBuilder()
.setAddress(address)
}
fun groupContextV2(
revision: Int = 0,
masterKeyBytes: ByteArray = Random.Default.nextBytes(GroupMasterKey.SIZE)
): GroupContextV2.Builder {
return GroupContextV2.newBuilder()
.setRevision(revision)
.setMasterKey(ByteString.copyFrom(masterKeyBytes))
}
fun storyContext(
sentTimestamp: Long = Random.nextLong(),
authorUuid: String = UUID.randomUUID().toString()
): DataMessage.StoryContext.Builder {
return DataMessage.StoryContext.newBuilder()
.setAuthorUuid(authorUuid)
.setSentTimestamp(sentTimestamp)
}
fun dataMessage(): DataMessage.Builder {
return DataMessage.newBuilder()
}
fun content(): SignalServiceProtos.Content.Builder {
return SignalServiceProtos.Content.newBuilder()
}
fun serviceContent(
localAddress: AddressProto = address().build(),
metadata: MetadataProto = metadata().build()
): SignalServiceContentProto.Builder {
return SignalServiceContentProto.newBuilder()
.setLocalAddress(localAddress)
.setMetadata(metadata)
}
companion object {
fun <T> build(buildFn: TestProtos.() -> T): T {
return TestProtos().buildFn()
}
}
}

View File

@@ -1,12 +1,23 @@
package org.thoughtcrime.securesms.testing
import android.database.Cursor
import android.util.Base64
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.hasSize
import org.hamcrest.Matchers.`is`
import org.hamcrest.Matchers.not
import org.hamcrest.Matchers.notNullValue
import org.hamcrest.Matchers.nullValue
import org.signal.core.util.logging.Log
import org.signal.core.util.readToList
import org.signal.core.util.select
import org.thoughtcrime.securesms.database.MessageTable
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.util.MessageTableTestUtils
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException
import kotlin.time.Duration
/**
* Run the given [runnable] on a new thread and wait for it to finish.
@@ -33,7 +44,7 @@ fun <T : Any?> T.assertIsNotNull() {
assertThat(this, notNullValue())
}
infix fun <T : Any> T.assertIs(expected: T) {
infix fun <T : Any?> T.assertIs(expected: T) {
assertThat(this, `is`(expected))
}
@@ -44,3 +55,35 @@ infix fun <T : Any> T.assertIsNot(expected: T) {
infix fun <E, T : Collection<E>> T.assertIsSize(expected: Int) {
assertThat(this, hasSize(expected))
}
fun CountDownLatch.awaitFor(duration: Duration) {
if (!await(duration.inWholeMilliseconds, TimeUnit.MILLISECONDS)) {
throw TimeoutException("Latch await took longer than ${duration.inWholeMilliseconds}ms")
}
}
fun dumpTableToLogs(tag: String = "TestUtils", table: String) {
dumpTable(table).forEach { Log.d(tag, it.toString()) }
}
fun dumpTable(table: String): List<List<Pair<String, String?>>> {
return SignalDatabase.rawDatabase
.select()
.from(table)
.run()
.readToList { cursor ->
val map: List<Pair<String, String?>> = cursor.columnNames.map { column ->
val index = cursor.getColumnIndex(column)
var data: String? = when (cursor.getType(index)) {
Cursor.FIELD_TYPE_BLOB -> Base64.encodeToString(cursor.getBlob(index), 0)
else -> cursor.getString(index)
}
if (table == MessageTable.TABLE_NAME && column == MessageTable.TYPE) {
data = MessageTableTestUtils.typeColumnToString(cursor.getLong(index))
}
column to data
}
map
}
}

View File

@@ -0,0 +1,11 @@
package org.thoughtcrime.securesms.util;
/**
* A class that allows us to inject feature flags during tests.
*/
public final class FeatureFlagsAccessor {
public static void forceValue(String key, Object value) {
FeatureFlags.FORCED_VALUES.put(key, value);
}
}

View File

@@ -0,0 +1,76 @@
package org.thoughtcrime.securesms.util
import org.thoughtcrime.securesms.database.MessageTable
import org.thoughtcrime.securesms.database.MessageTypes
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.model.MessageRecord
/**
* Helper methods for interacting with [MessageTable] in tests.
*/
object MessageTableTestUtils {
fun getMessages(threadId: Long): List<MessageRecord> {
return MessageTable.mmsReaderFor(SignalDatabase.messages.getConversation(threadId)).use {
it.toList()
}
}
fun typeColumnToString(type: Long): String {
return """
isOutgoingMessageType:${MessageTypes.isOutgoingMessageType(type)}
isForcedSms:${type and MessageTypes.MESSAGE_FORCE_SMS_BIT != 0L}
isDraftMessageType:${type and MessageTypes.BASE_TYPE_MASK == MessageTypes.BASE_DRAFT_TYPE}
isFailedMessageType:${type and MessageTypes.BASE_TYPE_MASK == MessageTypes.BASE_SENT_FAILED_TYPE}
isPendingMessageType:${type and MessageTypes.BASE_TYPE_MASK == MessageTypes.BASE_OUTBOX_TYPE || type and MessageTypes.BASE_TYPE_MASK == MessageTypes.BASE_SENDING_TYPE}
isSentType:${type and MessageTypes.BASE_TYPE_MASK == MessageTypes.BASE_SENT_TYPE}
isPendingSmsFallbackType:${type and MessageTypes.BASE_TYPE_MASK == MessageTypes.BASE_PENDING_INSECURE_SMS_FALLBACK || type and MessageTypes.BASE_TYPE_MASK == MessageTypes.BASE_PENDING_SECURE_SMS_FALLBACK}
isPendingSecureSmsFallbackType:${type and MessageTypes.BASE_TYPE_MASK == MessageTypes.BASE_PENDING_SECURE_SMS_FALLBACK}
isPendingInsecureSmsFallbackType:${type and MessageTypes.BASE_TYPE_MASK == MessageTypes.BASE_PENDING_INSECURE_SMS_FALLBACK}
isInboxType:${type and MessageTypes.BASE_TYPE_MASK == MessageTypes.BASE_INBOX_TYPE}
isJoinedType:${type and MessageTypes.BASE_TYPE_MASK == MessageTypes.JOINED_TYPE}
isUnsupportedMessageType:${type and MessageTypes.BASE_TYPE_MASK == MessageTypes.UNSUPPORTED_MESSAGE_TYPE}
isInvalidMessageType:${type and MessageTypes.BASE_TYPE_MASK == MessageTypes.INVALID_MESSAGE_TYPE}
isBadDecryptType:${type and MessageTypes.BASE_TYPE_MASK == MessageTypes.BAD_DECRYPT_TYPE}
isSecureType:${type and MessageTypes.SECURE_MESSAGE_BIT != 0L}
isPushType:${type and MessageTypes.PUSH_MESSAGE_BIT != 0L}
isEndSessionType:${type and MessageTypes.END_SESSION_BIT != 0L}
isKeyExchangeType:${type and MessageTypes.KEY_EXCHANGE_BIT != 0L}
isIdentityVerified:${type and MessageTypes.KEY_EXCHANGE_IDENTITY_VERIFIED_BIT != 0L}
isIdentityDefault:${type and MessageTypes.KEY_EXCHANGE_IDENTITY_DEFAULT_BIT != 0L}
isInvalidVersionKeyExchange:${type and MessageTypes.KEY_EXCHANGE_INVALID_VERSION_BIT != 0L}
isBundleKeyExchange:${type and MessageTypes.KEY_EXCHANGE_BUNDLE_BIT != 0L}
isIdentityUpdate:${type and MessageTypes.KEY_EXCHANGE_IDENTITY_UPDATE_BIT != 0L}
isRateLimited:${type and MessageTypes.MESSAGE_RATE_LIMITED_BIT != 0L}
isExpirationTimerUpdate:${type and MessageTypes.EXPIRATION_TIMER_UPDATE_BIT != 0L}
isIncomingAudioCall:${type == MessageTypes.INCOMING_AUDIO_CALL_TYPE}
isIncomingVideoCall:${type == MessageTypes.INCOMING_VIDEO_CALL_TYPE}
isOutgoingAudioCall:${type == MessageTypes.OUTGOING_AUDIO_CALL_TYPE}
isOutgoingVideoCall:${type == MessageTypes.OUTGOING_VIDEO_CALL_TYPE}
isMissedAudioCall:${type == MessageTypes.MISSED_AUDIO_CALL_TYPE}
isMissedVideoCall:${type == MessageTypes.MISSED_VIDEO_CALL_TYPE}
isGroupCall:${type == MessageTypes.GROUP_CALL_TYPE}
isGroupUpdate:${type and MessageTypes.GROUP_UPDATE_BIT != 0L}
isGroupV2:${type and MessageTypes.GROUP_V2_BIT != 0L}
isGroupQuit:${type and MessageTypes.GROUP_LEAVE_BIT != 0L && type and MessageTypes.GROUP_V2_BIT == 0L}
isChatSessionRefresh:${type and MessageTypes.ENCRYPTION_REMOTE_FAILED_BIT != 0L}
isDuplicateMessageType:${type and MessageTypes.ENCRYPTION_REMOTE_DUPLICATE_BIT != 0L}
isDecryptInProgressType:${type and 0x40000000 != 0L}
isNoRemoteSessionType:${type and MessageTypes.ENCRYPTION_REMOTE_NO_SESSION_BIT != 0L}
isLegacyType:${type and MessageTypes.ENCRYPTION_REMOTE_LEGACY_BIT != 0L || type and MessageTypes.ENCRYPTION_REMOTE_BIT != 0L}
isProfileChange:${type == MessageTypes.PROFILE_CHANGE_TYPE}
isGroupV1MigrationEvent:${type == MessageTypes.GV1_MIGRATION_TYPE}
isChangeNumber:${type == MessageTypes.CHANGE_NUMBER_TYPE}
isBoostRequest:${type == MessageTypes.BOOST_REQUEST_TYPE}
isThreadMerge:${type == MessageTypes.THREAD_MERGE_TYPE}
isSmsExport:${type == MessageTypes.SMS_EXPORT_TYPE}
isGroupV2LeaveOnly:${type and MessageTypes.GROUP_V2_LEAVE_BITS == MessageTypes.GROUP_V2_LEAVE_BITS}
isSpecialType:${type and MessageTypes.SPECIAL_TYPES_MASK != 0L}
isStoryReaction:${type and MessageTypes.SPECIAL_TYPES_MASK == MessageTypes.SPECIAL_TYPE_STORY_REACTION}
isGiftBadge:${type and MessageTypes.SPECIAL_TYPES_MASK == MessageTypes.SPECIAL_TYPE_GIFT_BADGE}
isPaymentsNotificaiton:${type and MessageTypes.SPECIAL_TYPES_MASK == MessageTypes.SPECIAL_TYPE_PAYMENTS_NOTIFICATION}
isRequestToActivatePayments:${type and MessageTypes.SPECIAL_TYPES_MASK == MessageTypes.SPECIAL_TYPE_PAYMENTS_ACTIVATE_REQUEST}
isPaymentsActivated:${type and MessageTypes.SPECIAL_TYPES_MASK == MessageTypes.SPECIAL_TYPE_PAYMENTS_ACTIVATED}
""".trimIndent().replace(Regex("is[A-Z][A-Za-z0-9]*:false\n?"), "").replace("\n", "")
}
}

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<profileable android:shell="true" />
<activity android:name="org.signal.benchmark.BenchmarkSetupActivity"
android:launchMode="singleTask"
android:theme="@style/Theme.Signal.DayNight.NoActionBar"
android:windowSoftInputMode="stateHidden"
android:exported="true"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
</application>
</manifest>

View File

@@ -0,0 +1,57 @@
package org.signal.benchmark
import android.os.Bundle
import android.widget.TextView
import org.signal.benchmark.setup.TestMessages
import org.signal.benchmark.setup.TestUsers
import org.thoughtcrime.securesms.BaseActivity
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.recipients.Recipient
class BenchmarkSetupActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
when (intent.extras!!.getString("setup-type")) {
"cold-start" -> setupColdStart()
"conversation-open" -> setupConversationOpen()
}
val textView: TextView = TextView(this).apply {
text = "done"
}
setContentView(textView)
}
private fun setupColdStart() {
TestUsers.setupSelf()
TestUsers.setupTestRecipients(50).forEach {
val recipient: Recipient = Recipient.resolved(it)
TestMessages.insertIncomingTextMessage(other = recipient, body = "Cool text message?!?!")
TestMessages.insertIncomingImageMessage(other = recipient, attachmentCount = 1)
TestMessages.insertIncomingImageMessage(other = recipient, attachmentCount = 2, body = "Album")
TestMessages.insertIncomingImageMessage(other = recipient, body = "Test", attachmentCount = 1, failed = true)
SignalDatabase.messages.setAllMessagesRead()
SignalDatabase.threads.update(SignalDatabase.threads.getOrCreateThreadIdFor(recipient = recipient), true)
}
}
private fun setupConversationOpen() {
TestUsers.setupSelf()
TestUsers.setupTestRecipient().let {
val recipient: Recipient = Recipient.resolved(it)
val messagesToAdd = 1000
val generator: TestMessages.TimestampGenerator = TestMessages.TimestampGenerator(System.currentTimeMillis() - (messagesToAdd * 2000L) - 60_000L)
for (i in 0 until messagesToAdd) {
TestMessages.insertIncomingTextMessage(other = recipient, body = "Test message $i", timestamp = generator.nextTimestamp())
TestMessages.insertOutgoingTextMessage(other = recipient, body = "Test message $i", timestamp = generator.nextTimestamp())
}
SignalDatabase.threads.update(SignalDatabase.threads.getOrCreateThreadIdFor(recipient = recipient), true)
}
}
}

View File

@@ -0,0 +1,40 @@
package org.signal.benchmark
import android.content.Context
import org.thoughtcrime.securesms.BuildConfig
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.push.AccountManagerFactory
import org.thoughtcrime.securesms.util.FeatureFlags
import org.whispersystems.signalservice.api.SignalServiceAccountManager
import org.whispersystems.signalservice.api.account.PreKeyUpload
import org.whispersystems.signalservice.api.push.ServiceId.ACI
import org.whispersystems.signalservice.api.push.ServiceId.PNI
import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration
import java.io.IOException
import java.util.Optional
class DummyAccountManagerFactory : AccountManagerFactory() {
override fun createAuthenticated(context: Context, aci: ACI, pni: PNI, number: String, deviceId: Int, password: String): SignalServiceAccountManager {
return DummyAccountManager(
ApplicationDependencies.getSignalServiceNetworkAccess().getConfiguration(number),
aci,
pni,
number,
deviceId,
password,
BuildConfig.SIGNAL_AGENT,
FeatureFlags.okHttpAutomaticRetry(),
FeatureFlags.groupLimits().hardLimit
)
}
private class DummyAccountManager(configuration: SignalServiceConfiguration?, aci: ACI?, pni: PNI?, e164: String?, deviceId: Int, password: String?, signalAgent: String?, automaticNetworkRetry: Boolean, maxGroupSize: Int) : SignalServiceAccountManager(configuration, aci, pni, e164, deviceId, password, signalAgent, automaticNetworkRetry, maxGroupSize) {
@Throws(IOException::class)
override fun setGcmId(gcmRegistrationId: Optional<String>) {
}
@Throws(IOException::class)
override fun setPreKeys(preKeyUpload: PreKeyUpload) {
}
}
}

View File

@@ -0,0 +1,198 @@
package org.signal.benchmark.setup
import org.thoughtcrime.securesms.attachments.PointerAttachment
import org.thoughtcrime.securesms.database.AttachmentTable
import org.thoughtcrime.securesms.database.MessageType
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.TestDbUtils
import org.thoughtcrime.securesms.mms.IncomingMessage
import org.thoughtcrime.securesms.mms.OutgoingMessage
import org.thoughtcrime.securesms.mms.QuoteModel
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.releasechannel.ReleaseChannel
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentRemoteId
import java.util.Collections
import java.util.Optional
object TestMessages {
fun insertOutgoingTextMessage(other: Recipient, body: String, timestamp: Long = System.currentTimeMillis()) {
insertOutgoingMessage(
recipient = other,
message = OutgoingMessage(
recipient = other,
body = body,
timestamp = timestamp,
isSecure = true
),
timestamp = timestamp
)
}
fun insertOutgoingImageMessage(other: Recipient, body: String? = null, attachmentCount: Int, timestamp: Long = System.currentTimeMillis()): Long {
val attachments: List<SignalServiceAttachmentPointer> = (0 until attachmentCount).map {
imageAttachment()
}
val message = OutgoingMessage(
recipient = other,
body = body,
attachments = PointerAttachment.forPointers(Optional.of(attachments)),
timestamp = timestamp,
isSecure = true
)
return insertOutgoingMediaMessage(recipient = other, message = message, timestamp = timestamp)
}
private fun insertOutgoingMediaMessage(recipient: Recipient, message: OutgoingMessage, timestamp: Long): Long {
val insert = insertOutgoingMessage(recipient, message = message, timestamp = timestamp)
setMessageMediaTransfered(insert)
return insert
}
private fun insertOutgoingMessage(recipient: Recipient, message: OutgoingMessage, timestamp: Long? = null): Long {
val insert = SignalDatabase.messages.insertMessageOutbox(
message,
SignalDatabase.threads.getOrCreateThreadIdFor(recipient),
false,
null
)
if (timestamp != null) {
TestDbUtils.setMessageReceived(insert, timestamp)
}
SignalDatabase.messages.markAsSent(insert, true)
return insert
}
fun insertIncomingTextMessage(other: Recipient, body: String, timestamp: Long? = null) {
val message = IncomingMessage(
type = MessageType.NORMAL,
from = other.id,
body = body,
sentTimeMillis = timestamp ?: System.currentTimeMillis(),
serverTimeMillis = timestamp ?: System.currentTimeMillis(),
receivedTimeMillis = timestamp ?: System.currentTimeMillis()
)
SignalDatabase.messages.insertMessageInbox(message, SignalDatabase.threads.getOrCreateThreadIdFor(other)).get().messageId
}
fun insertIncomingQuoteTextMessage(other: Recipient, body: String, quote: QuoteModel, timestamp: Long?) {
val message = IncomingMessage(
type = MessageType.NORMAL,
from = other.id,
body = body,
sentTimeMillis = timestamp ?: System.currentTimeMillis(),
serverTimeMillis = timestamp ?: System.currentTimeMillis(),
receivedTimeMillis = timestamp ?: System.currentTimeMillis(),
quote = quote
)
insertIncomingMessage(other, message = message)
}
fun insertIncomingImageMessage(other: Recipient, body: String? = null, attachmentCount: Int, timestamp: Long? = null, failed: Boolean = false): Long {
val attachments: List<SignalServiceAttachmentPointer> = (0 until attachmentCount).map {
imageAttachment()
}
val message = IncomingMessage(
type = MessageType.NORMAL,
from = other.id,
sentTimeMillis = timestamp ?: System.currentTimeMillis(),
serverTimeMillis = timestamp ?: System.currentTimeMillis(),
receivedTimeMillis = timestamp ?: System.currentTimeMillis(),
attachments = PointerAttachment.forPointers(Optional.of(attachments))
)
return insertIncomingMessage(recipient = other, message = message, failed = failed)
}
fun insertIncomingVoiceMessage(other: Recipient, timestamp: Long? = null): Long {
val message = IncomingMessage(
type = MessageType.NORMAL,
from = other.id,
sentTimeMillis = timestamp ?: System.currentTimeMillis(),
serverTimeMillis = timestamp ?: System.currentTimeMillis(),
receivedTimeMillis = timestamp ?: System.currentTimeMillis(),
attachments = PointerAttachment.forPointers(Optional.of(Collections.singletonList(voiceAttachment()) as List<SignalServiceAttachment>))
)
return insertIncomingMessage(recipient = other, message = message, failed = false)
}
private fun insertIncomingMessage(recipient: Recipient, message: IncomingMessage, failed: Boolean = false): Long {
val id = insertIncomingMessage(recipient = recipient, message = message)
if (failed) {
setMessageMediaFailed(id)
} else {
setMessageMediaTransfered(id)
}
return id
}
private fun insertIncomingMessage(recipient: Recipient, message: IncomingMessage): Long {
return SignalDatabase.messages.insertMessageInbox(message, SignalDatabase.threads.getOrCreateThreadIdFor(recipient)).get().messageId
}
private fun setMessageMediaFailed(messageId: Long) {
SignalDatabase.attachments.getAttachmentsForMessage(messageId).forEachIndexed { index, attachment ->
SignalDatabase.attachments.setTransferProgressPermanentFailure(attachment.attachmentId, messageId)
}
}
private fun setMessageMediaTransfered(messageId: Long) {
SignalDatabase.attachments.getAttachmentsForMessage(messageId).forEachIndexed { _, attachment ->
SignalDatabase.attachments.setTransferState(messageId, attachment.attachmentId, AttachmentTable.TRANSFER_PROGRESS_DONE)
}
}
private fun imageAttachment(): SignalServiceAttachmentPointer {
return SignalServiceAttachmentPointer(
ReleaseChannel.CDN_NUMBER,
SignalServiceAttachmentRemoteId.from(""),
"image/webp",
null,
Optional.empty(),
Optional.empty(),
1024,
1024,
Optional.empty(),
Optional.empty(),
0,
Optional.of("/not-there.jpg"),
false,
false,
false,
Optional.empty(),
Optional.empty(),
System.currentTimeMillis()
)
}
private fun voiceAttachment(): SignalServiceAttachmentPointer {
return SignalServiceAttachmentPointer(
ReleaseChannel.CDN_NUMBER,
SignalServiceAttachmentRemoteId.from(""),
"audio/aac",
null,
Optional.empty(),
Optional.empty(),
1024,
1024,
Optional.empty(),
Optional.empty(),
0,
Optional.of("/not-there.aac"),
true,
false,
false,
Optional.empty(),
Optional.empty(),
System.currentTimeMillis()
)
}
class TimestampGenerator(private var start: Long = System.currentTimeMillis()) {
fun nextTimestamp(): Long {
start += 500L
return start
}
}
}

View File

@@ -0,0 +1,117 @@
package org.signal.benchmark.setup
import android.app.Application
import android.content.SharedPreferences
import android.preference.PreferenceManager
import org.signal.benchmark.DummyAccountManagerFactory
import org.signal.core.util.concurrent.safeBlockingGet
import org.signal.libsignal.protocol.SignalProtocolAddress
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
import org.thoughtcrime.securesms.crypto.MasterSecretUtil
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.net.DeviceTransferBlockingInterceptor
import org.thoughtcrime.securesms.profiles.ProfileName
import org.thoughtcrime.securesms.push.AccountManagerFactory
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.registration.RegistrationData
import org.thoughtcrime.securesms.registration.RegistrationRepository
import org.thoughtcrime.securesms.registration.RegistrationUtil
import org.thoughtcrime.securesms.registration.VerifyResponse
import org.thoughtcrime.securesms.util.Util
import org.whispersystems.signalservice.api.profiles.SignalServiceProfile
import org.whispersystems.signalservice.api.push.ServiceId.ACI
import org.whispersystems.signalservice.api.push.SignalServiceAddress
import org.whispersystems.signalservice.internal.ServiceResponse
import org.whispersystems.signalservice.internal.ServiceResponseProcessor
import org.whispersystems.signalservice.internal.push.VerifyAccountResponse
import java.util.UUID
object TestUsers {
private var generatedOthers: Int = 0
fun setupSelf(): Recipient {
val application: Application = ApplicationDependencies.getApplication()
DeviceTransferBlockingInterceptor.getInstance().blockNetwork()
PreferenceManager.getDefaultSharedPreferences(application).edit().putBoolean("pref_prompted_push_registration", true).commit()
val masterSecret = MasterSecretUtil.generateMasterSecret(application, MasterSecretUtil.UNENCRYPTED_PASSPHRASE)
MasterSecretUtil.generateAsymmetricMasterSecret(application, masterSecret)
val preferences: SharedPreferences = application.getSharedPreferences(MasterSecretUtil.PREFERENCES_NAME, 0)
preferences.edit().putBoolean("passphrase_initialized", true).commit()
SignalStore.account().generateAciIdentityKeyIfNecessary()
SignalStore.account().generatePniIdentityKeyIfNecessary()
val registrationRepository = RegistrationRepository(application)
val registrationData = RegistrationData(
code = "123123",
e164 = "+15555550101",
password = Util.getSecret(18),
registrationId = registrationRepository.registrationId,
profileKey = registrationRepository.getProfileKey("+15555550101"),
fcmToken = "fcm-token",
pniRegistrationId = registrationRepository.pniRegistrationId,
recoveryPassword = "asdfasdfasdfasdf"
)
val verifyResponse = VerifyResponse(
VerifyAccountResponse(UUID.randomUUID().toString(), UUID.randomUUID().toString(), false),
masterKey = null,
pin = null,
aciPreKeyCollection = RegistrationRepository.generateSignedAndLastResortPreKeys(SignalStore.account().aciIdentityKey, SignalStore.account().aciPreKeys),
pniPreKeyCollection = RegistrationRepository.generateSignedAndLastResortPreKeys(SignalStore.account().aciIdentityKey, SignalStore.account().pniPreKeys)
)
AccountManagerFactory.setInstance(DummyAccountManagerFactory())
val response: ServiceResponse<VerifyResponse> = registrationRepository.registerAccount(
registrationData,
verifyResponse,
false
).safeBlockingGet()
ServiceResponseProcessor.DefaultProcessor(response).resultOrThrow
SignalStore.svr().optOut()
RegistrationUtil.maybeMarkRegistrationComplete()
SignalDatabase.recipients.setProfileName(Recipient.self().id, ProfileName.fromParts("Tester", "McTesterson"))
return Recipient.self()
}
fun setupTestRecipient(): RecipientId {
return setupTestRecipients(1).first()
}
fun setupTestRecipients(othersCount: Int): List<RecipientId> {
val others = mutableListOf<RecipientId>()
synchronized(this) {
if (generatedOthers + othersCount !in 0 until 1000) {
throw IllegalArgumentException("$othersCount must be between 0 and 1000")
}
for (i in generatedOthers until generatedOthers + othersCount) {
val aci = ACI.from(UUID.randomUUID())
val recipientId = RecipientId.from(SignalServiceAddress(aci, "+15555551%03d".format(i)))
SignalDatabase.recipients.setProfileName(recipientId, ProfileName.fromParts("Buddy", "#$i"))
SignalDatabase.recipients.setProfileKeyIfAbsent(recipientId, ProfileKeyUtil.createNew())
SignalDatabase.recipients.setCapabilities(recipientId, SignalServiceProfile.Capabilities(true, true, true))
SignalDatabase.recipients.setProfileSharing(recipientId, true)
SignalDatabase.recipients.markRegistered(recipientId, aci)
val otherIdentity = IdentityKeyUtil.generateIdentityKeyPair()
ApplicationDependencies.getProtocolStore().aci().saveIdentity(SignalProtocolAddress(aci.toString(), 0), otherIdentity.publicKey)
others += recipientId
}
generatedOthers += othersCount
}
return others
}
}

View File

@@ -0,0 +1,14 @@
package org.thoughtcrime.securesms.database
import android.content.ContentValues
import org.signal.core.util.SqlUtil.buildArgs
object TestDbUtils {
fun setMessageReceived(messageId: Long, timestamp: Long) {
val database: SQLiteDatabase = SignalDatabase.messages.databaseHelper.signalWritableDatabase
val contentValues = ContentValues()
contentValues.put(MessageTable.DATE_RECEIVED, timestamp)
val rowsUpdated = database.update(MessageTable.TABLE_NAME, contentValues, DatabaseTable.ID_WHERE, buildArgs(messageId))
}
}

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:name=".CanaryApplicationContext"
tools:replace="android:name" />
</manifest>

View File

@@ -0,0 +1,65 @@
package org.thoughtcrime.securesms
import android.os.StrictMode
import android.os.StrictMode.ThreadPolicy
import leakcanary.LeakCanary
import shark.AndroidReferenceMatchers
class CanaryApplicationContext : ApplicationContext() {
override fun onCreate() {
super.onCreate()
StrictMode.setThreadPolicy(
ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork()
.penaltyLog()
.build()
)
try {
Class.forName("dalvik.system.CloseGuard")
.getMethod("setEnabled", Boolean::class.javaPrimitiveType)
.invoke(null, true)
} catch (e: ReflectiveOperationException) {
throw RuntimeException(e)
}
LeakCanary.config = LeakCanary.config.copy(
referenceMatchers = AndroidReferenceMatchers.appDefaults +
AndroidReferenceMatchers.ignoredInstanceField(
className = "android.service.media.MediaBrowserService\$ServiceBinder",
fieldName = "this\$0"
) +
AndroidReferenceMatchers.ignoredInstanceField(
className = "androidx.media.MediaBrowserServiceCompat\$MediaBrowserServiceImplApi26\$MediaBrowserServiceApi26",
fieldName = "mBase"
) +
AndroidReferenceMatchers.ignoredInstanceField(
className = "android.support.v4.media.MediaBrowserCompat",
fieldName = "mImpl"
) +
AndroidReferenceMatchers.ignoredInstanceField(
className = "android.support.v4.media.session.MediaControllerCompat",
fieldName = "mToken"
) +
AndroidReferenceMatchers.ignoredInstanceField(
className = "android.support.v4.media.session.MediaControllerCompat",
fieldName = "mImpl"
) +
AndroidReferenceMatchers.ignoredInstanceField(
className = "org.thoughtcrime.securesms.components.voice.VoiceNotePlaybackService",
fieldName = "mApplication"
) +
AndroidReferenceMatchers.ignoredInstanceField(
className = "org.thoughtcrime.securesms.service.GenericForegroundService\$LocalBinder",
fieldName = "this\$0"
) +
AndroidReferenceMatchers.ignoredInstanceField(
className = "org.thoughtcrime.securesms.contacts.ContactsSyncAdapter",
fieldName = "mContext"
)
)
}
}

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